1 package Memoize::Memcached;
6 use UNIVERSAL qw( isa );
7 use Carp qw( carp croak );
8 use Memoize qw( unmemoize );
11 our $VERSION = '0.03';
14 $Data::Dumper::Sortkeys = 1;
16 warn "## FIXME ",__PACKAGE__, " patched with in-memory cache for fetch!";
20 our @EXPORT = qw( memoize_memcached );
21 our @EXPORT_OK = qw( unmemoize flush_cache );
23 all => [ @EXPORT, @EXPORT_OK ],
41 sub memoize_memcached {
42 # Be sure to leave @_ intact in case we need to redirect to
44 my ($function, %args) = @_;
46 if (exists $args{LIST_CACHE} or exists $args{ARRAY_CACHE}) {
47 carp "Call to 'memoize_memcached' with a cache option passed to 'memoize'";
48 goto &Memoize::memoize;
51 my $memcached_args = delete $args{memcached} || {};
52 croak "Invalid memcached argument (expected a hash)"
53 unless isa($memcached_args, 'HASH');
57 memoized_function => $function,
59 $args{LIST_CACHE} = [ HASH => $memo_data{$function}{list_cache} ];
60 $args{SCALAR_CACHE} = [ HASH => $memo_data{$function}{scalar_cache} ];
62 # If we are passed a normalizer, we need to keep a version of it
63 # around for flush_cache to use. This breaks encapsulation. And it
65 $memo_data{$function}{normalizer} = Memoize::_make_cref($args{NORMALIZER}, scalar caller)
66 if defined $args{NORMALIZER};
68 # Rebuild @_ since there is a good probability we have removed some
69 # arguments meant for us and added the cache arguments.
70 @_ = ($function, %args);
71 goto &Memoize::memoize;
75 # Unfortunately, we need to do some magic to make flush_cache sorta
76 # work. I don't think this is enough magic yet.
79 # If we have exactly 1 argument then we are probably expected to
80 # clear the cache for a single function. Pass this along to
81 # Memoize, even though it cannot be handled correctly at this time
82 # (whatever we do will be wrong, anyway).
84 warn "## flush_cache";
86 goto &Memoize::flush_cache if @_ == 1;
89 # If we have more than 1 argument, we are probably expected to clear
90 # a single call signature for a function. This we can almost do
93 # Even though we can do this "properly", it is still very bad. This
94 # breaks encapsulation pretty disgustingly. With any luck Memoize
95 # will eventually be patched to do this for us...
98 my ($function, @args) = @_;
99 my $cur_memo = $memo_data{$function};
100 my $normalizer = $memo_data{$function}{normalizer};
103 if (defined $normalizer) {
104 ($array_argstr) = $normalizer->(@_);
105 $scalar_argstr = $normalizer->(@_);
107 else { # Default normalizer
109 $array_argstr = $scalar_argstr = join chr(28), @args;
111 for my $cache (qw( list_cache scalar_cache )) {
112 for my $argstr ($scalar_argstr, $array_argstr) {
113 delete $cur_memo->{$cache}{$argstr};
120 # Currently all memoized functions share memcached config, so just
121 # find the first valid object and flush cache.
123 for my $function (keys %memo_data) {
124 next unless $memo_data{$function}{list_obj};
125 $memo_data{$function}{list_obj}{memcached_obj}->flush_all;
136 # Search through the arg list for the 'memcached' arg, process it,
137 # and remove it (and its associated value) from the arg list in
138 # anticipation of passing off to Exporter.
139 for my $idx ($[ + 1 .. $#_) {
140 my $arg = $_[$idx] || q();
141 next unless $arg eq 'memcached';
142 (undef, my $memcached_config) = splice @_, $idx, 2;
143 croak "Invalid memcached config (expected a hash ref)"
144 unless isa($memcached_config, 'HASH');
145 %memcached_config = %{$memcached_config};
148 return $class->export_to_level(1, @_);
152 sub _memcached_setup {
153 my %args = %memcached_config;
157 $args{$key} = $value;
160 my $function = delete $args{memoized_function};
161 my $list_key_prefix = delete $args{list_key_prefix};
162 my $scalar_key_prefix = delete $args{scalar_key_prefix};
164 $args{key_prefix} = 'memoize-' unless defined $args{key_prefix};
166 croak "Missing function name for memcached setup"
167 unless defined $function;
168 my $tie_data = $memo_data{$function} = {
175 my %cur_args = %args;
176 $cur_args{key_prefix}
177 .= (defined $function ? "$function-" : '-')
178 . (defined $list_key_prefix ? $list_key_prefix : 'list-')
180 $tie_data->{list_obj} = tie %{$tie_data->{list_cache}}, __PACKAGE__, %cur_args
181 or die "Error creating list cache";
184 $cur_args{key_prefix}
185 .= (defined $function ? "$function-" : '-')
186 . (defined $scalar_key_prefix ? $scalar_key_prefix : 'scalar-')
188 $tie_data->{scalar_obj} = tie %{$tie_data->{scalar_cache}}, __PACKAGE__, %cur_args
189 or die "Error creating scalar cache";
197 croak "Called new in object context" if ref $class;
198 my $self = fields::new($class);
209 $self->{key_prefix} = delete $args{key_prefix};
210 $self->{key_prefix} = q() unless defined $self->{key_prefix};
211 $self->{expire_time} = exists $args{expire_time} ? delete $args{expire_time} : undef;
213 # Default these to false so that we can use Data::Dumper on tied
214 # hashes by default. Yes, it will show them as empty, but I doubt
215 # someone running Dumper on this tied hash would really want to dump
216 # the contents of the memcached cache (and they can't anyway).
218 $self->{$_} = exists $args{$_} ? delete $args{$_} : !1
219 for qw( key_error scalar_error );
221 $self->{memcached_obj} = Cache::Memcached->new(\%args);
230 return $self->{key_prefix} . $key;
234 sub _key_lookup_error {
235 croak "Key lookup functionality is not implemented by memcached";
241 return $class->_new(@_);
247 my $key = $self->_get_key(shift);
249 my @args = ($key, $value);
250 push @args, $self->{expire_time} if defined $self->{expire_time};
251 $self->{memcached_obj}->set(@args);
252 warn "## STORE $key $value";
260 my $key = $self->_get_key(shift);
261 if ( exists $cache->{$key} ) {
262 $Koha::Persistant::stats->{memcache_FETCH}->[0]++;
263 return $cache->{$key};
265 $Koha::Persistant::stats->{memcache_FETCH}->[1]++;
266 warn "## FETCH $key";
267 my $v = $self->{memcached_obj}->get($key);
275 return defined $self->FETCH(@_);
281 my $key = $self->_get_key(shift);
282 $self->{memcached_obj}->delete($key);
283 warn "## DELETE $key";
290 # This is not safe because all object share memcached setup.
291 $self->{memcached_obj}->flush_all;
299 return unless $self->{key_error};
300 $self->_key_lookup_error;
306 return unless $self->{key_error};
307 $self->_key_lookup_error;
313 return unless $self->{scalar_error};
314 # I think this error still makes sense, since to determine if the
315 # cache has content one would need to first determine if the cache
317 $self->_key_lookup_error;
323 $self->{memcached_obj}->disconnect_all;
335 Memoize::Memcached - use a memcached cache to memoize functions
340 use Memoize::Memcached
342 servers => [ '127.0.0.1:11211' ],
345 memoize_memcached('foo');
347 # Function 'foo' is now memoized using the memcached server
348 # running on 127.0.0.1:11211 as the cache.
353 The way C<flush_cache> works with memcached can be dangerous. Please
354 read the documentation below on C<flush_cache>.
359 This module exports C<memoize_memcached>, C<flush_cache>, and
360 C<unmemoize>. The C<unmemoize> function is just the one from Memoize,
361 and is made available for convenience.
366 =head2 memoize_memcached
368 This is the memcached equivalent of C<memoize>. It works very
369 similarly, except for some difference in options.
371 If the C<LIST_CACHE> or C<SCALAR_CACHE> options are passed in,
372 C<memoize_memcached> will complain and then pass the request along to
373 C<memoize>. The result will be a memoized function, but using
374 whatever cache you specified and NOT using memcached at all.
376 This function also accepts a C<memcached> option, which expects a
377 hashref. This is de-referenced and passed directly into an internal
378 function which sets up the memcached configuration for that function.
379 This contents of this hashref are mostly options passed to
380 C<Cache::Memcached>, with a few exceptions.
382 The actual key used to look up memoize data in memcached is formed
383 from the function name, the normalized arguments, and some additional
384 prefixes which can be set via the C<memcached> option. These prefixes
385 are C<key_prefix>, C<list_key_prefix>, and C<scalar_key_prefix>.
387 The C<key_prefix> defaults to "memoize-" if it's not passed in, or an
388 undefined value is passed in.
390 The C<list_key_prefix> and C<scalar_key_prefix> options default to
391 "list-" and "scalar-" respectively, by the same criteria.
393 So, the default way the key is generated is:
395 "memoize-<function>-list-<normalized args>"
399 "memoize-<function>-scalar-<normalized args>"
401 The function and normalized args portion of this key are set
402 internally, but the "memoize-" prefix and the context portion can be
403 configured with memcached options as follows:
405 "<key_prefix>-function-<list_key_prefix|scalar_key_prefix>-args"
409 memoize_memcached('foo');
411 # keys generated will look like this:
412 # list context: memoize-foo-list-<argument signature>
413 # scalar context: memoize-foo-scalar-<argument signature>
415 memoize_memcached('foo',
419 list_key_prefix => 'L-',
420 scalar_key_prefix => 'S-',
424 # keys generated will look like this:
425 # list context: _M-foo-L-<argument signature>
426 # scalar context: _M-foo-S-<argument signature>
430 The behavior documented in C<Memoize> is sort of implemented. A call
431 to C<flush_cache('memoized_function')> will indeed clear the cache of
432 all cached return values for that function, BUT it will also clear the
433 entire memcached cache, including all other memoized functions using
434 the same memcached cache, and even data unrelated to
435 C<Memoize::Memcached> in the same cache. It will flush the entire
438 There are 2 new ways to call this function:
444 flush_cache(memoized_function => qw( an argument signature ));
446 The call without arguments will flush the entire memcached cache, just
447 like the 1 argument version. This includes unrelated data. Be
450 The call with 2 or more arguments will flush only the cached return
451 values (array and scalar contexts) for a call to the function named
452 by the first argument with an argument signature matching the second
453 argument to the end. Unlike the other 2 ways to call this function,
454 when called this way only the specified part of the cache is flushed.
456 I would recommended that only the 2 or more argument version of
457 C<flush_cache> be called unless you are very sure of what you are
463 The biggest gotcha is that you probably never want to call
464 C<flush_cache('memoized_function')>. Because of the way C<CLEAR> is
465 implemented against memcached, this call will flush the entire
466 memcached cache. Everything. Even stuff having nothing to do with
467 C<Memoize::Memcached>. You are warned.
472 A more intuitive interface for handling different memcached server
473 configurations would probably be useful.
478 David Trischuk, C<< <trischuk at gmail.com> >>
483 Please report any bugs or feature requests to C<bug-memoize-memcached at rt.cpan.org>, or through
484 the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Memoize-Memcached>. I will be notified, and then you'll
485 automatically be notified of progress on your bug as I make changes.
490 You can find documentation for this module with the perldoc command.
492 perldoc Memoize::Memcached
494 You can also look for information at:
498 =item * RT: CPAN's request tracker
500 L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Memoize-Memcached>
502 =item * AnnoCPAN: Annotated CPAN documentation
504 L<http://annocpan.org/dist/Memoize-Memcached>
508 L<http://cpanratings.perl.org/d/Memoize-Memcached>
512 L<http://search.cpan.org/dist/Memoize-Memcached>
517 =head1 ACKNOWLEDGMENTS
519 The tied hash portion of this module is heavily based on
520 C<Cache::Memcached::Tie> by Andrew Kostenko.
523 =head1 COPYRIGHT & LICENSE
525 Copyright 2008 David Trischuk, all rights reserved.
527 This program is free software; you can redistribute it and/or modify it
528 under the same terms as Perl itself.