root/lang/perl/Cache-Memcached-libmemcached/trunk/lib/Cache/Memcached/libmemcached.pm @ 8525

Revision 8525, 15.0 kB (checked in by daisuke, 5 years ago)

lang/perl/Cache-Memcached-libmemcached; Add hashing_algorithm/distribution_method docs and tests

  • Property svn:keywords set to Id
Line 
1# $Id$
2#
3# Copyright (c) 2008 Daisuke Maki <daisuke@endeworks.jp>
4# All rights reserved.
5
6package Cache::Memcached::libmemcached;
7use strict;
8use warnings;
9use base qw(Memcached::libmemcached);
10use Carp qw(croak);
11use Storable ();
12
13our $VERSION = '0.02001';
14
15use constant HAVE_ZLIB    => eval { require Compress::Zlib } && !$@;
16use constant F_STORABLE   => 1;
17use constant F_COMPRESS   => 2;
18
19BEGIN
20{
21    # Make sure to load bytes.pm if HAVE_ZLIB is enabled
22    if (HAVE_ZLIB) {
23        require bytes;
24    }
25   
26
27    # accessors
28    foreach my $field qw(compress_enable compress_threshold compress_savings) {
29        eval sprintf(<<"        EOSUB", $field, $field, $field, $field);
30            sub set_%s { \$_[0]->{%s} = \$_[1] }
31            sub get_%s { \$_[0]->{%s} }
32        EOSUB
33        die if $@;
34    }
35
36    # proxy these methods
37    foreach my $method qw(delete set add replace prepend append cas) {
38        eval <<"        EOSUB";
39            sub $method { shift->memcached_$method(\@_) }
40        EOSUB
41        die if $@;
42    }
43}
44
45sub import
46{
47    my $class = shift;
48    Memcached::libmemcached->export_to_level(1, undef, @_) ;
49}
50
51sub new
52{
53    my $class = shift;
54    my $args  = shift || {};
55
56    $args->{servers} || die "No servers specified";
57
58    my $self = $class->SUPER::new();
59
60    $self->{compress_threshold} = $args->{compress_threshold};
61    $self->{compress_savingsS}   = $args->{compress_savings} || 0.20;
62    $self->{compress_enable}    =
63        exists $args->{compress_enable} ? $args->{compress_enable} : 1;
64
65    # servers
66    $self->set_servers($args->{servers});
67
68    # Set compression/serialization callbacks
69    $self->set_callback_coderefs(
70        # Closures so we have reference to $self
71        $self->_mk_callbacks()
72    );
73
74    # behavior options
75    $self->set_no_block( $args->{no_block} ) if exists $args->{no_block};
76    $self->set_hashing_algorithm( $args->{hashing_algorithm} ) if exists $args->{hashing_algorithm};
77    $self->set_distribution_method( $args->{distribution_method} ) if exists $args->{distribution_method};
78
79    return $self;
80}
81
82sub set_servers
83{
84    my $self = shift;
85    my $servers = shift || [];
86    foreach my $server (@$servers) {
87        $self->server_add($server);
88    }
89}
90
91sub server_add
92{
93    my $self = shift;
94    my $server = shift;
95
96    # check for existance of :
97    if ($server =~ /^([^:]+):([^:]+)$/) {
98        my ($hostname, $port) = ($1, $2);
99        $self->memcached_server_add($hostname, $port );
100    } else {
101        $self->memcached_server_add_unix_socket( $server );
102    }
103}
104
105sub _mk_callbacks
106{
107    my $self = shift;
108
109    my $inflate = sub {
110        my ($key, $flags) = @_;
111        if ($flags & F_COMPRESS) {
112            if (! HAVE_ZLIB) {
113                croak("Data for $key is compressed, but we have no Compress::Zlib");
114            }
115            $_ = Compress::Zlib::memGunzip($_);
116        }
117
118        if ($flags & F_STORABLE) {
119            $_ = Storable::thaw($_);
120        }
121        return ();
122    };
123
124    my $deflate = sub {
125        # Check if we have a complex structure
126        if (ref $_) {
127            $_ = Storable::nfreeze($_);
128            $_[1] |= F_STORABLE;
129        }
130
131        # Check if we need compression
132        if (HAVE_ZLIB && $self->{compress_enable} && $self->{compress_threshold}) {
133            # Find the byte length
134            my $length = bytes::length($_);
135            if ($length > $self->{compress_threshold}) {
136                my $tmp = Compress::Zlib::memGzip($_);
137                if (1 - bytes::length($tmp) / $length < $self->{compress_savingsS}) {
138                    $_ = $tmp;
139                    $_[1] |= F_COMPRESS;
140                }
141            }
142        }
143        return ();
144    };
145    return ($deflate, $inflate);
146}
147
148sub incr
149{
150    my $self = shift;
151    $_[0] or croak("No key specified in incr");
152    $_[1] ||= 1 if @_ < 2;
153    my $val = 0;
154    $self->memcached_increment(@_[0,1], $val);
155    return $val;
156}
157
158sub decr
159{
160    my $self = shift;
161    $_[0] or croak("No key specified in decr");
162    $_[1] ||= 1 if @_ < 2;
163    my $val = 0;
164    $self->memcached_decrement(@_[0,1], $val);
165    return $val;
166}
167
168sub flush_all
169{
170    $_[0]->memcached_flush(0);
171}
172
173*remove = \&delete;
174
175sub disconnect_all
176{
177    $_[0]->memcached_quit();
178}
179
180sub stats { die "stats() not implemented" }
181
182BEGIN
183{
184    my @boolean_behavior = qw( no_block );
185    my %behavior = (
186        distribution_method => 'distribution',
187        hashing_algorithm   => 'hash'
188    );
189
190    foreach my $name (@boolean_behavior) {
191        my $code = sprintf(<<'        EOSUB', $name, uc $name, $name, uc $name);
192            sub is_%s {
193                $_[0]->memcached_behavior_get( Memcached::libmemcached::MEMCACHED_BEHAVIOR_%s() );
194            }
195
196            sub set_%s {
197                $_[0]->memcached_behavior_set( Memcached::libmemcached::MEMCACHED_BEHAVIOR_%s(), $_[1] );
198            }
199        EOSUB
200        eval $code;
201        die if $@;
202    }
203
204    while (my($method, $field) = each %behavior) {
205        my $code = sprintf(<<'        EOSUB', $method, uc $field, $method, uc $field);
206            sub get_%s {
207                $_[0]->memcached_behavior_get( Memcached::libmemcached::MEMCACHED_BEHAVIOR_%s() );
208            }
209
210            sub set_%s {
211                $_[0]->memcached_behavior_set( Memcached::libmemcached::MEMCACHED_BEHAVIOR_%s(), $_[1]);
212            }
213        EOSUB
214        eval $code;
215        die if $@;
216    }
217
218}
219
2201;
221
222__END__
223
224=head1 NAME
225
226Cache::Memcached::libmemcached - Perl Interface to libmemcached
227
228=head1 SYNOPSIS
229
230  use Cache::Memcached::libmemcached;
231  my $memd = Cache::Memcached::libmemcached->new({
232    servers => [ "10.0.0.15:11211", "10.0.0.15:11212", "/var/sock/memcached" ],
233    compress_threshold => 10_000
234  });
235
236  $memd->set("my_key", "Some value");
237  $memd->set("object_key", { 'complex' => [ "object", 2, 4 ]});
238
239  $val = $memd->get("my_key");
240  $val = $memd->get("object_key");
241  if ($val) { print $val->{complex}->[2] }
242
243  $memd->incr("key");
244  $memd->decr("key");
245  $memd->incr("key", 2);
246
247  $memd->delete("key");
248  $memd->remove("key"); # Alias to delete
249
250  my $hashref = $memd->get_multi(@keys);
251
252  # Constants
253  use Cache::Memcached::libmemcached qw(MEMCACHED_DISTRIBUTION_CONSISTENT);
254  $memd->set_distribution_method(MEMCACHED_DISTRIBUTION_CONSISTENT());
255
256  # Extra constructor options that are not in Cache::Memcached
257  # See Memcached::libmemcached::constants for a list of available options
258  my $memd = Cache::Memcached::libmemcached->new({
259    ...,
260    no_block            => $boolean,
261    distribution_method => $distribution_method,
262    hashing_algorithm   => $hashing_algorithm,
263  });
264
265=head1 DESCRIPTION
266
267This is the Cache::Memcached compatible interface to libmemcached,
268a C library to interface with memcached.
269
270Cache::Memcached::libmemcached is built on top of Memcached::libmemcached.
271While Memcached::libmemcached aims to port libmemcached API to perl,
272Cache::Memcached::libmemcached attempts to be API compatible with
273Cache::Memcached, so it can be used as a drop-in replacement.
274
275Note that as of version 0.02000, Cache::Memcached::libmemcached I<inherits>
276from Memcached::libmemcached. While you are free to use the
277Memcached::libmemcached specific methods directly on the object, you should
278use them with care, as it will mean that your code is no longer compatible
279with the Cache::Memcached API therefore losing some of th portability in
280case you want to replace it with some other package.
281
282=head1 FOR Cache::Memcached::LibMemcached USERS
283
284Cache::Memcached::libmemcached is a rewrite of Cache::Memcached::LibMemcached,
285using Memcached::libmemcached instead of straight XS as its backend.
286
287Therefore you might notice some differences. Here are the ones we are
288aware of:
289
290=over 4
291
292=item cas() and stats() are not implemented
293
294They were sort of implemented in a previous life, but since
295Memcached::libmemcached is still undecided how to handle these, we don't
296support it either.
297
298=item performance is probably a bit different
299
300To be honest, we haven't ran benchmarks comparing the two (yet). In general,
301you might see a decrease in performance here and there because we've
302essentially added another call stack (instead of going straight from perl to
303XS, we are now going from perl to perl to XS). But on the other hand,
304Memcached::libmemcached is in the hands of XS gurus like Time Bunce, so
305you are probably sparing yourself some accidental hooplas that occasional
306C programmers like me might introduce.
307
308=back
309
310=head1 Cache::Memcached COMPATIBLE METHODS
311
312Except for the minor incompatiblities, below methods are generally compatible
313with Cache::Memcached.
314
315=head2 new
316
317Takes on parameter, a hashref of options.
318
319=head2 set_servers
320
321  $memd->set_servers( [ qw(serv1:port1 serv2:port2 ...) ]);
322
323Sets the server list.
324
325=head2 get
326
327  my $val = $memd->get($key);
328
329Retrieves a key from the memcached. Returns the value (automatically thawed
330with Storable, if necessary) or undef.
331
332Currently the arrayref form of $key is NOT supported. Perhaps in the future.
333
334=head2 get_multi
335
336  my $hashref = $memd->get_multi(@keys);
337
338Retrieves multiple keys from the memcache doing just one query.
339Returns a hashref of key/value pairs that were available.
340
341=head2 set
342
343  $memd->set($key, $value[, $expires]);
344
345Unconditionally sets a key to a given value in the memcache. Returns true if
346it was stored successfully.
347
348Currently the arrayref form of $key is NOT supported. Perhaps in the future.
349
350=head2 add
351
352  $memd->add($key, $value[, $expires]);
353
354Like set(), but only stores in memcache if they key doesn't already exist.
355
356=head2 replace
357
358  $memd->replace($key, $value[, $expires]);
359
360Like set(), but only stores in memcache if they key already exist.
361
362=head2 append
363
364  $memd->append($key, $value);
365
366Appends $value to whatever value associated with $key. Only available for
367memcached > 1.2.4
368
369=head2 prepend
370
371  $memd->prepend($key, $value);
372
373Prepends $value to whatever value associated with $key. Only available for
374memcached > 1.2.4
375
376=head2 incr
377
378=head2 decr
379
380  my $newval = $memd->incr($key);
381  my $newval = $memd->decr($key);
382  my $newval = $memd->incr($key, $offset);
383  my $newval = $memd->decr($key, $offset);
384
385Atomically increments or decrements the specified the integer value specified
386by $key. Returns undef if the key doesn't exist on the server.
387
388=head2 delete
389
390=head2 remove
391
392  $memd->delete($key);
393
394Deletes a key.
395
396XXX - The behavior when second argument is specified may differ from
397Cache::Memcached -- this hasn't been very well tested. Patches welcome!
398
399=head2 flush_all
400
401  $memd->fush_all;
402
403Runs the memcached "flush_all" command on all configured hosts, emptying all
404their caches.
405
406=head2 set_compress_threshold
407
408  $memd->set_compress_threshold($threshold);
409
410Set the compress threshold.
411
412=head2 enable_compress
413
414  $memd->enable_compress($bool);
415
416This is actually an alias to set_compress_enable(). The original version
417from Cache::Memcached is, despite its naming, a setter as well.
418
419=head2 stats
420
421  my $h = $memd->stats();
422
423This method is still half-baked. Patches welcome.
424
425=head2 disconnect_all
426
427Disconnects from servers
428
429=head2 cas
430
431  $memd->cas($key, $cas, $value[, $exptime]);
432
433XXX - This method is still broken.
434
435Sets if $cas matches the value on the server.
436
437=head2 gets
438
439=head2 get_cas
440
441  my $cas = $memd->gets($key);
442  my $cas = $memd->get_cas($key);
443
444Get the CAS value for $key
445
446=head2 get_cas_multi
447
448  my $h = $memd->get_cas_multi(@keys)
449
450Gets CAS values for multiple keys
451
452=head1 Cache::Memcached::libmemcached SPECIFIC METHODS
453
454These methods are libmemcached-specific.
455
456=head2 server_add
457
458Adds a memcached server.
459
460=head2 server_add_unix_socket
461
462Adds a memcached server, connecting via unix socket.
463
464=head2 server_list_free
465
466Frees the memcached server list.
467
468=head1 UTILITY METHODS
469
470WARNING: Please do not consider the existance for these methods to be final.
471They may be renamed or may entirely disappear from future releases.
472
473=head2 get_compress_threshold
474
475Return the current value of compress_threshold
476
477=head2 set_compress_enable
478
479Set the value of compress_enable
480
481=head2 get_compress_enable
482
483Return the current value of compress_enable
484
485=head2 set_compress_savings
486
487Set the value of compress_savings
488
489=head2 get_compress_savings
490
491Return the current value of compress_savings
492
493=head1 BEHAVIOR CUSTOMIZATION
494
495Certain libmemcached behaviors can be configured with the following
496methods.
497
498(NOTE: This API is not fixed yet)
499
500=head2 behavior_set
501
502=head2 behavior_get
503
504If you want to customize something that we don't have a wrapper for,
505you can directly use these method.
506
507=head2 set_no_block
508
509  Cache::Memcached::libmemcached->new({
510    ...
511    no_block => 1
512  });
513  # or
514  $memd->set_no_block( 1 );
515
516Set to use blocking/non-blocking I/O. When this is in effect, get() becomes
517flaky, so don't attempt to call it. This has the most effect for set()
518operations, because libmemcached stops waiting for server response after
519writing to the socket (set() will also always return success)
520
521Please consult the man page for C<memcached_behavior_set()> for details
522before setting.
523
524=head2 is_no_block
525
526Get the current value of no_block behavior.
527
528=head2 set_distribution_method
529
530  $memd->set_distribution_method( MEMCACHED_DISTRIBUTION_CONSISTENT );
531
532Set the distribution behavior.
533
534=head2 get_distribution_method
535
536Get the distribution behavior.
537
538=head2 set_hashing_algorithm
539
540  $memd->set_hashing_algorithm( MEMCACHED_HASH_KETAMA );
541
542Set the hashing algorithm used.
543
544=head2 get_hashing_algorithm
545
546Get the hashing algorithm used.
547
548=head2 set_support_cas
549
550  $memd->set_support_cas($boolean);
551  # or
552  $memd = Cache::Memcached::libmemcached->new( {
553    ...
554    support_cas => 1
555  } );
556
557Enable/disable CAS support.
558
559=head1 VARIOUS MEMCACHED MODULES
560
561Below are the various memcached modules available on CPAN.
562
563Please check tool/benchmark.pl for a live comparison of these modules.
564(except for Cache::Memcached::XS, which I wasn't able to compile under my
565main dev environment)
566
567=head2 Cache::Memcached
568
569This is the "main" module. It's mostly written in Perl.
570
571=head2 Cache::Memcached::libmemcached
572
573Cache::Memcached::libmemcached, which is the module for which your reading
574the document of, is a perl binding for libmemcached (http://tangent.org/552/libmemcached.html). Not to be confused with libmemcache (see below).
575
576=head2 Cache::Memcached::XS
577
578Cache::Memcached::XS is a binding for libmemcache (http://people.freebsd.org/~seanc/libmemcache/).
579The main memcached site at http://danga.com/memcached/apis.bml seems to
580indicate that the underlying libmemcache is no longer in active development.
581
582=head2 Cache::Memcached::Fast
583
584Cache::Memcached::Fast is a memcached client written in XS from scratch.
585
586=head2 Memcached::libmemcached
587
588Memcached::libmemcached is a straight binding to libmemcached. It has all
589of the libmemcached API. If you don't care about a drop-in replacement for
590Cache::Memcached, and want to benefit from *all* of libmemcached offers,
591this is the way to go.
592
593=head1 CAVEATS
594
595Unless you know what you're getting yourself into, don't try to subclass this
596module just yet. Internal structures may change without notice.
597
598=head1 AUTHOR
599
600Copyright (c) 2008 Daisuke Maki E<lt>daisuke@endeworks.jpE<gt>
601
602=head1 LICENSE
603
604This program is free software; you can redistribute it and/or modify it
605under the same terms as Perl itself.
606
607See http://www.perl.com/perl/misc/Artistic.html
608
609=cut
Note: See TracBrowser for help on using the browser.