File Coverage

blib/lib/CHI/Driver.pm
Criterion Covered Total %
statement 352 370 95.1
branch 138 170 81.1
condition 18 30 60.0
subroutine 78 84 92.8
pod 0 41 0.0
total 586 695 84.3


line stmt bran cond sub pod time code
1             package CHI::Driver;
2             $CHI::Driver::VERSION = '0.60';
3 20     20   11334 use Carp;
  20         31  
  20         1219  
4 20     20   6974 use CHI::CacheObject;
  20         41  
  20         675  
5 20     20   146 use CHI::Constants qw(CHI_Max_Time);
  20         27  
  20         1011  
6 20     20   8582 use CHI::Driver::Metacache;
  20         44  
  20         651  
7 20     20   8699 use CHI::Driver::Role::HasSubcaches;
  20         64  
  20         672  
8 20     20   8715 use CHI::Driver::Role::IsSizeAware;
  20         57  
  20         779  
9 20     20   9962 use CHI::Driver::Role::IsSubcache;
  20         53  
  20         652  
10 20     20   7940 use CHI::Driver::Role::Universal;
  20         49  
  20         576  
11 20     20   7295 use CHI::Serializer::Storable;
  20         48  
  20         705  
12 20     20   8915 use CHI::Serializer::JSON;
  20         55  
  20         638  
13 20     20   201 use CHI::Util qw(parse_duration);
  20         23  
  20         921  
14 20     20   182 use CHI::Types qw(:all);
  20         26  
  20         3640  
15 20     20   103 use Digest::MD5;
  20         26  
  20         660  
16 20     20   80 use Encode;
  20         27  
  20         1422  
17 20     20   93 use Hash::MoreUtils qw(slice_grep);
  20         37  
  20         1189  
18 20     20   98 use Log::Any qw($log);
  20         29  
  20         167  
19 20     20   1458 use Moo;
  20         35  
  20         71  
20 20     20   8595 use MooX::Types::MooseLike::Base qw(:all);
  20         34  
  20         7149  
21 20     20   113 use Scalar::Util qw(blessed);
  20         28  
  20         1174  
22 20     20   11504 use Time::Duration;
  20         33097  
  20         1788  
23 20     20   11639 use Time::HiRes qw(gettimeofday);
  20         28038  
  20         89  
24 20     20   3490 use strict;
  20         32  
  20         572  
25 20     20   88 use warnings;
  20         31  
  20         9120  
26              
27             my $default_serializer = CHI::Serializer::Storable->new();
28             my $default_key_serializer = CHI::Serializer::JSON->new();
29             my $default_key_digester = Digest::MD5->new();
30              
31             my @common_params;
32             {
33             my %attr = (
34             chi_root_class => {
35             is => 'ro',
36             },
37             compress_threshold => {
38             is => 'ro',
39             isa => Int,
40             },
41             constructor_params => {
42             is => 'ro',
43             init_arg => undef,
44             },
45             driver_class => {
46             is => 'ro',
47             },
48             expires_at => {
49             is => 'rw',
50             default => sub { CHI_Max_Time },
51             },
52             expires_in => {
53             is => 'rw',
54             isa => Duration,
55             coerce => \&to_Duration,
56             },
57             expires_on_backend => {
58             is => 'ro',
59             isa => Num,
60             default => sub { 0 },
61             },
62             expires_variance => {
63             is => 'rw',
64             isa => Num,
65             default => sub { 0 },
66             },
67             has_subcaches => {
68             is => 'lazy',
69             isa => Bool,
70             init_arg => undef,
71             },
72             is_size_aware => {
73             is => 'ro',
74             isa => Bool,
75             },
76             is_subcache => {
77             is => 'ro',
78             isa => Bool,
79             },
80             key_digester => {
81             is => 'ro',
82             isa => Digester,
83             coerce => \&to_Digester,
84             default => sub { $default_key_digester },
85             },
86             key_serializer => {
87             is => 'ro',
88             isa => Serializer,
89             coerce => \&to_Serializer,
90             default => sub { $default_key_serializer },
91             },
92             label => {
93             is => 'rw',
94             lazy => 1,
95             builder => 1,
96             clearer => 1,
97             predicate => 1,
98             },
99             max_build_depth => {
100             is => 'ro',
101             default => sub { 8 },
102             },
103             max_key_length => {
104             is => 'ro',
105             isa => Int,
106             default => sub { 1 << 31 },
107             },
108             metacache => {
109             is => 'lazy',
110             clearer => 1,
111             predicate => 1,
112             },
113             namespace => {
114             is => 'ro',
115             isa => Str,
116             default => sub { 'Default' },
117             },
118             on_get_error => {
119             is => 'rw',
120             isa => OnError,
121             default => sub { 'log' },
122             },
123             on_set_error => {
124             is => 'rw',
125             isa => OnError,
126             default => sub { 'log' },
127             },
128             serializer => {
129             is => 'ro',
130             isa => Serializer,
131             coerce => \&to_Serializer,
132             default => sub { $default_serializer },
133             },
134             short_driver_name => {
135             is => 'lazy',
136             clearer => 1,
137             predicate => 1,
138             },
139             storage => {
140             is => 'ro',
141             },
142             );
143             push @common_params, keys %attr;
144             for my $attr ( keys %attr ) {
145             has $attr => %{ $attr{$attr} };
146             }
147             }
148              
149 20     20   751 sub _build_has_subcaches { undef }
150              
151             # These methods must be implemented by subclass
152             foreach my $method (qw(fetch store remove get_keys get_namespaces)) {
153 20     20   114 no strict 'refs';
  20         34  
  20         3612  
154 0     0   0 *{$method} = sub { die "method '$method' must be implemented by subclass" };
155             }
156              
157             # Given a hash of params, return the subset that are not in CHI's common parameters.
158             #
159             push @common_params, qw(
160             discard_policy
161             discard_timeout
162             l1_cache
163             max_size
164             max_size_reduction_factor
165             mirror_cache
166             parent_cache
167             subcache_type
168             subcaches
169             );
170             my %common_params = map { ( $_, 1 ) } @common_params;
171              
172             sub non_common_constructor_params {
173 1     1 0 2 my ( $class, $params ) = @_;
174              
175             return {
176 3         15 map { ( $_, $params->{$_} ) }
  5         10  
177 1         4 grep { !$common_params{$_} } keys(%$params)
178             };
179             }
180              
181             sub declare_unsupported_methods {
182 1     1 0 2 my ( $class, @methods ) = @_;
183              
184 1         2 foreach my $method (@methods) {
185 20     20   104 no strict 'refs';
  20         29  
  20         83106  
186 1         7 *{"$class\::$method"} =
187 1     1   4 sub { croak "method '$method' not supported by '$class'" };
  1         92  
188             }
189             }
190              
191 13579     13579 0 81585 sub cache_object_class { 'CHI::CacheObject' }
192              
193             # To override time() for testing - must be writable in a dynamically scoped way from tests
194             our $Test_Time; ## no critic (ProhibitPackageVars)
195             our $Build_Depth = 0; ## no critic (ProhibitPackageVars)
196              
197 0     0 0 0 sub valid_get_options { qw(expire_if busy_lock) }
198 0     0 0 0 sub valid_set_options { qw(expires_at expires_in expires_variance) }
199              
200             sub BUILD {
201 882     882 0 30930 my ( $self, $params ) = @_;
202              
203             # Ward off infinite build recursion, e.g. from circular subcache configuration.
204             #
205 882         1990 local $Build_Depth = $Build_Depth + 1;
206 882 50       11141 die "$Build_Depth levels of CHI cache creation; infinite recursion?"
207             if ( $Build_Depth > $self->max_build_depth );
208              
209             # Save off constructor params. Used to create metacache, for
210             # example. Hopefully this will not cause circular references...
211             #
212 882         6541 $self->{constructor_params} = {%$params};
213 882         2710 foreach my $param (qw(l1_cache mirror_cache parent_cache)) {
214 2646         4730 delete( $self->{constructor_params}->{$param} );
215             }
216              
217             # If stats enabled, add ns_stats slot for keeping track of stats
218             #
219 882         8960 my $stats = $self->chi_root_class->stats;
220 882 100       3448 if ( $stats->enabled ) {
221 6         34 $self->{ns_stats} = $stats->stats_for_driver($self);
222             }
223              
224             # Call BUILD_roles on any of the roles that need initialization.
225             #
226 882         9806 $self->BUILD_roles($params);
227             }
228              
229 882     882 0 25982 sub BUILD_roles {
230              
231             # Will be modified by roles that need it
232             }
233              
234             sub _build_short_driver_name {
235 390     390   10189 my ($self) = @_;
236              
237 390         4193 ( my $name = $self->driver_class ) =~ s/^CHI::Driver:://;
238              
239 390         6112 return $name;
240             }
241              
242             sub _build_label {
243 378     378   9963 my ($self) = @_;
244              
245 378         9515 return $self->short_driver_name;
246             }
247              
248             sub _build_metacache {
249 74     74   3927 my $self = shift;
250              
251 74         1659 return CHI::Driver::Metacache->new( owner_cache => $self );
252             }
253              
254             sub get {
255 10038     10038 0 95885 my ( $self, $key, %params ) = @_;
256              
257 10038 100       20674 croak "must specify key" unless defined($key);
258 10031         13581 my $ns_stats = $self->{ns_stats};
259 10031         25564 my $log_is_debug = $log->is_debug;
260 10031   66     44214 my $measure_time = defined($ns_stats) || $log_is_debug;
261 10031         8613 my ( $start_time, $elapsed_time );
262              
263             # Fetch cache object
264             #
265 10031 50       29792 $start_time = gettimeofday() if $measure_time;
266 10031 100       11566 my $obj = eval { $params{obj} || $self->get_object($key) };
  10031         30454  
267 10031 50       37381 $elapsed_time = ( gettimeofday() - $start_time ) * 1000 if $measure_time;
268 10031 100       18789 if ( my $error = $@ ) {
269 4 50       8 $ns_stats->{'get_errors'}++ if defined($ns_stats);
270 4         28 $self->_handle_get_error( $error, $key );
271 3         21 return undef;
272             }
273 10027 100       17368 if ( !defined $obj ) {
274 1328 100       2714 $self->_record_get_stats( 'absent_misses', $elapsed_time )
275             if defined($ns_stats);
276 1328 50       5031 $self->_log_get_result( $log, "MISS (not in cache)",
277             $key, $elapsed_time )
278             if $log_is_debug;
279 1328         31459 return undef;
280             }
281 8699 100       17473 if ( defined( my $obj_ref = $params{obj_ref} ) ) {
282 593         885 $$obj_ref = $obj;
283             }
284              
285             # Check if expired
286             #
287 8699   66     20103 my $is_expired = $obj->is_expired()
288             || ( defined( $params{expire_if} )
289             && $params{expire_if}->( $obj, $self ) );
290 8699 100       15585 if ($is_expired) {
291 3537 50       5765 $self->_record_get_stats( 'expired_misses', $elapsed_time )
292             if defined($ns_stats);
293 3537 50       9466 $self->_log_get_result( $log, "MISS (expired)", $key, $elapsed_time )
294             if $log_is_debug;
295              
296             # If busy_lock value provided, set a new "temporary" expiration time that many
297             # seconds forward before returning undef
298             #
299 3537 100       70990 if ( defined( my $busy_lock = $params{busy_lock} ) ) {
300 8   33     25 my $time = $Test_Time || time();
301 8         29 my $busy_lock_time = $time + parse_duration($busy_lock);
302 8         232 $obj->set_early_expires_at($busy_lock_time);
303 8         25 $obj->set_expires_at($busy_lock_time);
304 8         22 $self->set_object( $key, $obj );
305             }
306              
307 3537         17618 return undef;
308             }
309              
310 5162 100       8265 $self->_record_get_stats( 'hits', $elapsed_time ) if defined($ns_stats);
311 5162 50       15102 $self->_log_get_result( $log, "HIT", $key, $elapsed_time ) if $log_is_debug;
312 5162         105308 return $obj->value;
313             }
314              
315             sub _record_get_stats {
316 3     3   7 my ( $self, $stat, $elapsed_time ) = @_;
317 3         11 $self->{ns_stats}->{$stat}++;
318 3         9 $self->{ns_stats}->{'get_time_ms'} += $elapsed_time;
319             }
320              
321             sub unpack_from_data {
322 10039     10039 0 13617 my ( $self, $key, $data ) = @_;
323              
324 10039         18991 return $self->cache_object_class->unpack_from_data( $key, $data,
325             $self->serializer );
326             }
327              
328             sub get_object {
329 12323     12323 0 16837 my ( $self, $key ) = @_;
330              
331 12323 100       22874 croak "must specify key" unless defined($key);
332 12316         23521 $key = $self->transform_key($key);
333              
334 12316 100       36662 my $data = $self->fetch($key) or return undef;
335 9812         24190 my $obj = $self->unpack_from_data( $key, $data );
336 9812         31116 return $obj;
337             }
338              
339             sub get_expires_at {
340 77     77 0 172 my ( $self, $key ) = @_;
341 77 100       311 croak "must specify key" unless defined($key);
342              
343 70 100       266 if ( my $obj = $self->get_object($key) ) {
344 63         170 return $obj->expires_at;
345             }
346             else {
347 7         42 return;
348             }
349             }
350              
351             sub exists_and_is_expired {
352 21     21 0 57 my ( $self, $key ) = @_;
353 21 100       171 croak "must specify key" unless defined($key);
354              
355 14 50       38 if ( my $obj = $self->get_object($key) ) {
356 14         60 return $obj->is_expired;
357             }
358             else {
359 0         0 return;
360             }
361             }
362              
363             sub is_valid {
364 70     70 0 149 my ( $self, $key ) = @_;
365 70 100       298 croak "must specify key" unless defined($key);
366              
367 63 100       268 if ( my $obj = $self->get_object($key) ) {
368 35         121 return !$obj->is_expired;
369             }
370             else {
371 28         129 return;
372             }
373             }
374              
375             sub _default_set_options {
376 3206     3206   3619 my $self = shift;
377              
378 3206         5247 return { map { $_ => $self->$_() }
  9618         166944  
379             qw( expires_at expires_in expires_variance ) };
380             }
381              
382             sub set {
383 3547     3547 0 53347 my $self = shift;
384 3547         6126 my ( $key, $value, $options ) = @_;
385              
386 3547 100       8591 croak "must specify key" unless defined($key);
387 3540         12717 $key = $self->transform_key($key);
388 3540 50       7258 return unless defined($value);
389              
390             # Fill in $options if not passed, copy if passed, and apply defaults.
391             #
392 3540 100       7391 if ( !defined($options) ) {
393 3168         9315 $options = $self->_default_set_options;
394             }
395             else {
396 372 100       964 if ( !ref($options) ) {
397 65 100       248 if ( $options eq 'never' ) {
    50          
398 8         29 $options = { expires_at => CHI_Max_Time };
399             }
400             elsif ( $options eq 'now' ) {
401 0         0 $options = { expires_in => 0 };
402             }
403             else {
404 57         165 $options = { expires_in => $options };
405             }
406             }
407              
408             # Disregard default expires_at and expires_in if either are provided
409             #
410 372 100 100     1588 if ( exists( $options->{expires_at} )
411             || exists( $options->{expires_in} ) )
412             {
413 334         9050 $options =
414             { expires_variance => $self->expires_variance, %$options };
415             }
416             else {
417 38         52 $options = { %{ $self->_default_set_options }, %$options };
  38         320  
418             }
419             }
420              
421 3540         41673 $self->set_with_options( $key, $value, $options );
422             }
423              
424             sub set_with_options {
425 3540     3540 0 5932 my ( $self, $key, $value, $options ) = @_;
426 3540         5663 my $ns_stats = $self->{ns_stats};
427 3540         11044 my $log_is_debug = $log->is_debug;
428 3540   66     18990 my $measure_time = defined($ns_stats) || $log_is_debug;
429 3540         4531 my ( $start_time, $elapsed_time );
430              
431             # Determine early and final expiration times
432             #
433 3540   66     11388 my $time = $Test_Time || time();
434 3540         3802 my $created_at = $time;
435 3540 100       15900 my $expires_at =
436             ( defined( $options->{expires_in} ) )
437             ? $time + parse_duration( $options->{expires_in} )
438             : $options->{expires_at};
439 3540 100       11592 my $early_expires_at =
    100          
440             defined( $options->{early_expires_at} ) ? $options->{early_expires_at}
441             : ( $expires_at == CHI_Max_Time ) ? CHI_Max_Time
442             : $expires_at -
443             ( ( $expires_at - $time ) * $options->{expires_variance} );
444              
445             # Pack into data, and store
446             #
447 3540         10217 my $obj =
448             $self->cache_object_class->new( $key, $value, $created_at,
449             $early_expires_at, $expires_at, $self->serializer,
450             $self->compress_threshold );
451 3540 100       9386 if ( defined( my $obj_ref = $options->{obj_ref} ) ) {
452 14         31 $$obj_ref = $obj;
453             }
454 3540 50       12423 $start_time = gettimeofday() if $measure_time;
455 3540 100       35501 if ( $self->set_object( $key, $obj ) ) {
456 3520 50       14399 $elapsed_time = ( gettimeofday() - $start_time ) * 1000
457             if $measure_time;
458              
459             # Log the set
460             #
461 3520 100       7174 if ( defined($ns_stats) ) {
462 2         5 $ns_stats->{'sets'}++;
463 2         10 $ns_stats->{'set_key_size'} += length( $obj->key );
464 2         10 $ns_stats->{'set_value_size'} += $obj->size;
465 2         5 $ns_stats->{'set_time_ms'} += $elapsed_time;
466             }
467 3520 50       6127 if ($log_is_debug) {
468 3520         11776 $self->_log_set_result( $log, $obj, $elapsed_time );
469             }
470             }
471              
472 3523         130155 return $value;
473             }
474              
475             sub set_object {
476 3716     3716 0 19873 my ( $self, $key, $obj ) = @_;
477              
478 3716         9776 my $data = $obj->pack_to_data();
479 3716         10312 my $expires_on_backend = $self->expires_on_backend;
480 3716 50 33     11497 my @expires_in = (
481             $expires_on_backend && $obj->expires_at < CHI_Max_Time
482             ? ( ( $obj->expires_at - time ) * $expires_on_backend )
483             : ()
484             );
485 3716         4582 eval { $self->store( $key, $data, @expires_in ) };
  3716         13821  
486 3716 100       12536 if ( my $error = $@ ) {
487 20 50       82 $self->{ns_stats}->{'set_errors'}++ if defined( $self->{ns_stats} );
488 20         403 $self->_handle_set_error( $error, $obj );
489 3         12 return 0;
490             }
491 3696         14743 return 1;
492             }
493              
494             sub get_keys_iterator {
495 50     50 0 90 my ($self) = @_;
496              
497 50         273 my @keys = $self->get_keys();
498 50     132   503 return sub { shift(@keys) };
  132         526  
499             }
500              
501             sub clear {
502 0     0 0 0 my $self = shift;
503 0 0       0 die "clear takes no arguments" if @_;
504              
505 0         0 $self->remove_multi( [ $self->get_keys() ] );
506             }
507              
508             sub expire {
509 175     175 0 3386 my ( $self, $key ) = @_;
510 175 100       573 croak "must specify key" unless defined($key);
511              
512 168   33     674 my $time = $Test_Time || time();
513 168 50       379 if ( defined( my $obj = $self->get_object($key) ) ) {
514 168         240 my $expires_at = $time - 1;
515 168         451 $obj->set_early_expires_at($expires_at);
516 168         383 $obj->set_expires_at($expires_at);
517 168         414 $self->set_object( $key, $obj );
518             }
519             }
520              
521             sub compute {
522 49     49 0 81 my $self = shift;
523 49         71 my $key = shift;
524 49         83 my $wantarray = wantarray();
525              
526             # Allow these in either order for backward compatibility
527 49 100       194 my ( $code, $options ) =
528             ( ref( $_[0] ) eq 'CODE' ) ? ( $_[0], $_[1] ) : ( $_[1], $_[0] );
529              
530 49 100 66     337 croak "must specify key and code" unless defined($key) && defined($code);
531              
532             my %get_options =
533             ( ref($options) eq 'HASH' )
534 42 50   28   302 ? slice_grep { /(?:expire_if|busy_lock)/ } $options
  28         552  
535             : ();
536             my $set_options =
537             ( ref($options) eq 'HASH' )
538 42 50   28   436 ? { slice_grep { !/(?:expire_if|busy_lock)/ } $options }
  28         328  
539             : $options;
540              
541 42         805 my $value = $self->get( $key, %get_options );
542 42 100       116 if ( !defined $value ) {
543 35         37 my ( $start_time, $elapsed_time );
544 35         48 my $ns_stats = $self->{ns_stats};
545 35 50       67 $start_time = gettimeofday if defined($ns_stats);
546 35 100       123 $value = $wantarray ? [ $code->() ] : $code->();
547 35 50       80 $elapsed_time = ( gettimeofday() - $start_time ) * 1000
548             if defined($ns_stats);
549 35         479 $self->set( $key, $value, $set_options );
550 35 50       112 if ( defined($ns_stats) ) {
551 0         0 $ns_stats->{'computes'}++;
552 0         0 $ns_stats->{'compute_time_ms'} += $elapsed_time;
553             }
554             }
555 42 100       248 return $wantarray ? @$value : $value;
556             }
557              
558             sub purge {
559 0     0 0 0 my ($self) = @_;
560              
561 0         0 foreach my $key ( $self->get_keys() ) {
562 0 0       0 if ( my $obj = $self->get_object($key) ) {
563 0 0       0 if ( $obj->is_expired() ) {
564 0         0 $self->remove($key);
565             }
566             }
567             }
568             }
569              
570             sub dump_as_hash {
571 20     20 0 38 my ($self) = @_;
572              
573 20         31 my %hash;
574 20         129 foreach my $key ( $self->get_keys() ) {
575 260 50       2437 if ( defined( my $value = $self->get($key) ) ) {
576 260         673 $hash{$key} = $value;
577             }
578             }
579 20         334 return \%hash;
580             }
581              
582             sub is_empty {
583 0     0 0 0 my ($self) = @_;
584              
585 0         0 return !$self->get_keys();
586             }
587              
588             #
589             # (SEMI-) ATOMIC OPERATIONS
590             #
591              
592             sub add {
593 21     21 0 38 my $self = shift;
594 21         32 my $key = shift;
595              
596 21 100       178 if ( !$self->is_valid($key) ) {
597 14         333 $self->set( $key, @_ );
598             }
599             }
600              
601             sub append {
602 15     15 0 276 my ( $self, $key, $new ) = @_;
603              
604 15 100       101 my $current = $self->fetch($key) or return undef;
605 10         45 $self->store( $key, $current . $new );
606 10         36 return 1;
607             }
608              
609             sub replace {
610 14     14 0 30 my $self = shift;
611 14         25 my $key = shift;
612              
613 14 100       95 if ( $self->is_valid($key) ) {
614 7         141 $self->set( $key, @_ );
615             }
616             }
617              
618             #
619             # MULTI KEY OPERATIONS
620             #
621              
622             sub fetch_multi_hashref {
623 85     85 0 115 my ( $self, $keys ) = @_;
624              
625 85         145 return { map { ( $_, $self->fetch($_) ) } @$keys };
  360         822  
626             }
627              
628             sub get_multi_hashref_objects {
629 85     85 0 118 my ( $self, $keys ) = @_;
630 85         733 my $key_data = $self->fetch_multi_hashref($keys);
631             return {
632 360         386 map {
633 85         340 my $data = $key_data->{$_};
634 360 100       868 defined($data)
635             ? ( $_, $self->unpack_from_data( $_, $data ) )
636             : ( $_, undef )
637             } keys(%$key_data)
638             };
639             }
640              
641             sub get_multi_arrayref {
642 92     92 0 561 my ( $self, $keys ) = @_;
643 92 100       324 croak "must specify keys" unless defined($keys);
644 85         162 my $transformed_keys = [ map { $self->transform_key($_) } @$keys ];
  360         567  
645              
646 85         171 my $key_count = scalar(@$keys);
647 85         532 my $keyobjs = $self->get_multi_hashref_objects($transformed_keys);
648             return [
649 360         447 map {
650 85         309 my $key = $transformed_keys->[$_];
651 360         481 my $obj = $keyobjs->{$key};
652 360 100       2767 $obj ? $self->get( $key, obj => $obj ) : undef
653             } ( 0 .. $key_count - 1 )
654             ];
655             }
656              
657             sub get_multi_hashref {
658 29     29 0 75 my ( $self, $keys ) = @_;
659 29 100       198 croak "must specify keys" unless defined($keys);
660              
661 22         75 my $key_count = scalar(@$keys);
662 22         339 my $values = $self->get_multi_arrayref($keys);
663 22         100 return { map { ( $keys->[$_], $values->[$_] ) } ( 0 .. $key_count - 1 ) };
  85         251  
664             }
665              
666             sub set_multi {
667 81     81 0 188 my $self = shift;
668 81         599 $self->store_multi(@_);
669             }
670              
671             sub store_multi {
672 81     81 0 182 my ( $self, $key_values, $set_options ) = @_;
673 81 100       382 croak "must specify key_values" unless defined($key_values);
674              
675 74         485 while ( my ( $key, $value ) = each(%$key_values) ) {
676 189         4048 $self->set( $key, $value, $set_options );
677             }
678             }
679              
680             sub remove_multi {
681 19     19 0 57 my ( $self, $keys ) = @_;
682 19 100       163 croak "must specify keys" unless defined($keys);
683              
684 12         51 foreach my $key (@$keys) {
685 65         1732 $self->remove($key);
686             }
687             }
688              
689             #
690             # KEY TRANSFORMATION
691             #
692              
693             my %escapes;
694             for ( 0 .. 255 ) {
695             $escapes{ chr($_) } = sprintf( "+%02x", $_ );
696             }
697             my $_fail_hi = sub {
698             croak( sprintf "Can't escape multibyte character \\x{%04X}", ord( $_[0] ) );
699             };
700              
701             sub transform_key {
702 18418     18418 0 21225 my ( $self, $key ) = @_;
703              
704 18418 100 66     81978 if ( ref($key) ) {
    100          
705 553         3433 $key = $self->key_serializer->serialize($key);
706             }
707             elsif ( Encode::is_utf8($key) && $key =~ /[^\x00-\xFF]/ ) {
708 188         1193 $key = $self->encode_key($key);
709             }
710 18418 100       57842 if ( length($key) > $self->max_key_length ) {
711 74         251 $key = $self->digest_key($key);
712             }
713              
714 18418         59901 return $key;
715             }
716              
717             sub digest_key {
718 127     127 0 188 my ( $self, $key ) = @_;
719              
720 127         1594 return $self->key_digester->add($key)->hexdigest;
721             }
722              
723             sub encode_key {
724 188     188 0 308 my ( $self, $key ) = @_;
725              
726 188         794 return Encode::encode( utf8 => $key );
727             }
728              
729             # These will be called by drivers if necessary, and in testing. By default
730             # no escaping/unescaping is necessary.
731             #
732 303     303 0 649 sub escape_key { $_[1] }
733 303     303 0 799 sub unescape_key { $_[1] }
734              
735             # May be used by drivers to implement escape_key/unescape_key.
736             #
737             sub escape_for_filename {
738 9463     9463 0 9514 my ( $self, $key ) = @_;
739              
740 9463 50       22094 $key =~ s/([^A-Za-z0-9_\=\-\~])/$escapes{$1} || $_fail_hi->($1)/ge;
  12403         39351  
741 9463         21313 return $key;
742             }
743              
744             sub unescape_for_filename {
745 2487     2487 0 2314 my ( $self, $key ) = @_;
746              
747 2487 50       5730 $key =~ s/\+([0-9A-Fa-f]{2})/chr(hex($1))/eg if defined $key;
  2847         5215  
748 2487         5768 return $key;
749             }
750              
751             sub is_escaped_for_filename {
752 98     98 0 126 my ( $self, $key ) = @_;
753              
754 98         146 return $self->escape_for_filename( $self->unescape_for_filename($key) ) eq
755             $key;
756             }
757              
758             #
759             # LOGGING AND ERROR HANDLING
760             #
761              
762             sub _log_get_result {
763 10027     10027   10901 my $self = shift;
764 10027         9200 my $log = shift;
765 10027         9878 my $msg = shift;
766 10027         20260 $log->debug( sprintf( "%s: %s", $self->_describe_cache_get(@_), $msg ) );
767             }
768              
769             sub _log_set_result {
770 3520     3520   4292 my $self = shift;
771 3520         3595 my $log = shift;
772 3520         9971 $log->debug( $self->_describe_cache_set(@_) );
773             }
774              
775             sub _handle_get_error {
776 4     4   4 my $self = shift;
777 4         5 my $error = shift;
778 4         4 my $key = $_[0];
779              
780 4         20 my $msg =
781             sprintf( "error during %s: %s", $self->_describe_cache_get(@_), $error );
782 4         98 $self->_dispatch_error_msg( $msg, $error, $self->on_get_error(), $key );
783             }
784              
785             sub _handle_set_error {
786 20     20   54 my ( $self, $error, $obj ) = @_;
787              
788 20         99 my $msg =
789             sprintf( "error during %s: %s", $self->_describe_cache_set($obj),
790             $error );
791 20         943 $self->_dispatch_error_msg( $msg, $error, $self->on_set_error(),
792             $obj->key );
793             }
794              
795             sub _dispatch_error_msg {
796 24     24   513 my ( $self, $msg, $error, $on_error, $key ) = @_;
797              
798 24         57 for ($on_error) {
799 24 100       103 ( ref($_) eq 'CODE' ) && do { $_->( $msg, $key, $error ) };
  2         8  
800             /^log$/
801 24 100       89 && do { $log->error($msg) };
  2         12  
802 24 100       83 /^ignore$/ && do { };
803 24 50       68 /^warn$/ && do { carp $msg };
  0         0  
804 24 100       115 /^die$/ && do { croak $msg };
  18         483  
805             }
806             }
807              
808             sub _describe_cache_get {
809 10031     10031   11723 my ( $self, $key, $elapsed_time ) = @_;
810              
811             return
812 10031 100       269730 sprintf( "cache get for namespace='%s', key='%s', cache='%s'"
    100          
813             . ( defined($elapsed_time) ? ", time='%dms'" : "" ),
814             $self->namespace, $key, $self->label,
815             defined($elapsed_time) ? int($elapsed_time) : () );
816             }
817              
818             sub _describe_cache_set {
819 3540     3540   4490 my ( $self, $obj, $elapsed_time ) = @_;
820              
821 3540 100       9277 my $expires_str = (
822             ( $obj->expires_at == CHI_Max_Time )
823             ? 'never'
824             : Time::Duration::concise(
825             Time::Duration::duration_exact(
826             $obj->expires_at - $obj->created_at
827             )
828             )
829             );
830              
831             return
832 3540 100       39814 sprintf(
    100          
833             "cache set for namespace='%s', key='%s', size=%d, expires='%s', cache='%s'"
834             . ( defined($elapsed_time) ? ", time='%dms'" : "" ),
835             $self->namespace, $obj->key, $obj->size, $expires_str, $self->label,
836             defined($elapsed_time) ? int($elapsed_time) : () );
837              
838             }
839              
840             1;
841              
842             __END__