File Coverage

blib/lib/CHI/Driver.pm
Criterion Covered Total %
statement 351 369 95.1
branch 139 170 81.7
condition 20 30 66.6
subroutine 78 84 92.8
pod 0 41 0.0
total 588 694 84.7


line stmt bran cond sub pod time code
1             package CHI::Driver;
2             $CHI::Driver::VERSION = '0.61';
3 20     20   13157 use Carp;
  20         53  
  20         1303  
4 20     20   9096 use CHI::CacheObject;
  20         55  
  20         775  
5 20     20   152 use CHI::Constants qw(CHI_Max_Time);
  20         36  
  20         1092  
6 20     20   9811 use CHI::Driver::Metacache;
  20         70  
  20         706  
7 20     20   10630 use CHI::Driver::Role::HasSubcaches;
  20         66  
  20         838  
8 20     20   10574 use CHI::Driver::Role::IsSizeAware;
  20         64  
  20         766  
9 20     20   10598 use CHI::Driver::Role::IsSubcache;
  20         56  
  20         600  
10 20     20   8837 use CHI::Driver::Role::Universal;
  20         62  
  20         672  
11 20     20   8762 use CHI::Serializer::Storable;
  20         60  
  20         700  
12 20     20   9881 use CHI::Serializer::JSON;
  20         52  
  20         681  
13 20     20   132 use CHI::Util qw(parse_duration);
  20         36  
  20         952  
14 20     20   110 use CHI::Types qw(:all);
  20         40  
  20         3455  
15 20     20   144 use Digest::MD5;
  20         41  
  20         804  
16 20     20   119 use Encode;
  20         42  
  20         1744  
17 20     20   150 use Hash::MoreUtils qw(slice_grep);
  20         49  
  20         1362  
18 20     20   163 use Log::Any qw($log);
  20         51  
  20         161  
19 20     20   4840 use Moo;
  20         51  
  20         160  
20 20     20   8459 use MooX::Types::MooseLike::Base qw(:all);
  20         43  
  20         6546  
21 20     20   161 use Scalar::Util qw(blessed);
  20         58  
  20         1157  
22 20     20   11429 use Time::Duration;
  20         40717  
  20         1795  
23 20     20   11893 use Time::HiRes qw(gettimeofday);
  20         30336  
  20         96  
24 20     20   3875 use strict;
  20         54  
  20         517  
25 20     20   108 use warnings;
  20         50  
  20         10251  
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 24     24   670 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   166 no strict 'refs';
  20         62  
  20         5157  
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 4 my ( $class, $params ) = @_;
174              
175             return {
176 3         9 map { ( $_, $params->{$_} ) }
177 1         4 grep { !$common_params{$_} } keys(%$params)
  5         12  
178             };
179             }
180              
181             sub declare_unsupported_methods {
182 1     1 0 4 my ( $class, @methods ) = @_;
183              
184 1         4 foreach my $method (@methods) {
185 20     20   161 no strict 'refs';
  20         44  
  20         92855  
186 1         9 *{"$class\::$method"} =
187 1     1   6 sub { croak "method '$method' not supported by '$class'" };
  1         76  
188             }
189             }
190              
191 16084     16084 0 80306 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 998     998 0 28648 my ( $self, $params ) = @_;
202              
203             # Ward off infinite build recursion, e.g. from circular subcache configuration.
204             #
205 998         2084 local $Build_Depth = $Build_Depth + 1;
206 998 50       5668 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 998         7445 $self->{constructor_params} = {%$params};
213 998         3070 foreach my $param (qw(l1_cache mirror_cache parent_cache)) {
214 2994         5385 delete( $self->{constructor_params}->{$param} );
215             }
216              
217             # If stats enabled, add ns_stats slot for keeping track of stats
218             #
219 998         5051 my $stats = $self->chi_root_class->stats;
220 998 100       3859 if ( $stats->enabled ) {
221 6         26 $self->{ns_stats} = $stats->stats_for_driver($self);
222             }
223              
224             # Call BUILD_roles on any of the roles that need initialization.
225             #
226 998         6399 $self->BUILD_roles($params);
227             }
228              
229       998 0   sub BUILD_roles {
230              
231             # Will be modified by roles that need it
232             }
233              
234             sub _build_short_driver_name {
235 450     450   4576 my ($self) = @_;
236              
237 450         2902 ( my $name = $self->driver_class ) =~ s/^CHI::Driver:://;
238              
239 450         5922 return $name;
240             }
241              
242             sub _build_label {
243 438     438   4457 my ($self) = @_;
244              
245 438         7875 return $self->short_driver_name;
246             }
247              
248             sub _build_metacache {
249 85     85   1125 my $self = shift;
250              
251 85         1798 return CHI::Driver::Metacache->new( owner_cache => $self );
252             }
253              
254             sub get {
255 11946     11946 0 123039 my ( $self, $key, %params ) = @_;
256              
257 11946 100       25384 croak "must specify key" unless defined($key);
258 11938         19401 my $ns_stats = $self->{ns_stats};
259 11938         29367 my $log_is_debug = $log->is_debug;
260 11938   66     101794 my $measure_time = defined($ns_stats) || $log_is_debug;
261 11938         17885 my ( $start_time, $elapsed_time );
262              
263             # Fetch cache object
264             #
265 11938 50       35930 $start_time = gettimeofday() if $measure_time;
266 11938 100       18912 my $obj = eval { $params{obj} || $self->get_object($key) };
  11938         34825  
267 11938 50       64956 $elapsed_time = ( gettimeofday() - $start_time ) * 1000 if $measure_time;
268 11938 100       25946 if ( my $error = $@ ) {
269 4 50       8 $ns_stats->{'get_errors'}++ if defined($ns_stats);
270 4         16 $self->_handle_get_error( $error, $key );
271 3         19 return undef;
272             }
273 11934 100       22217 if ( !defined $obj ) {
274 1689 100       3833 $self->_record_get_stats( 'absent_misses', $elapsed_time )
275             if defined($ns_stats);
276 1689 50       6582 $self->_log_get_result( $log, "MISS (not in cache)",
277             $key, $elapsed_time )
278             if $log_is_debug;
279 1689         110244 return undef;
280             }
281 10245 100       21752 if ( defined( my $obj_ref = $params{obj_ref} ) ) {
282 643         1134 $$obj_ref = $obj;
283             }
284              
285             # Check if expired
286             #
287             my $is_expired = $obj->is_expired()
288             || ( defined( $params{expire_if} )
289 10245   100     23026 && $params{expire_if}->( $obj, $self ) );
290 10245 100       21763 if ($is_expired) {
291 4025 50       7337 $self->_record_get_stats( 'expired_misses', $elapsed_time )
292             if defined($ns_stats);
293 4025 50       12321 $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 4025 100       234785 if ( defined( my $busy_lock = $params{busy_lock} ) ) {
300 9   33     34 my $time = $Test_Time || time();
301 9         43 my $busy_lock_time = $time + parse_duration($busy_lock);
302 9         389 $obj->set_early_expires_at($busy_lock_time);
303 9         33 $obj->set_expires_at($busy_lock_time);
304 9         34 $self->set_object( $key, $obj );
305             }
306              
307 4025         19660 return undef;
308             }
309              
310 6220 100       10583 $self->_record_get_stats( 'hits', $elapsed_time ) if defined($ns_stats);
311 6220 50       18911 $self->_log_get_result( $log, "HIT", $key, $elapsed_time ) if $log_is_debug;
312 6220         372596 return $obj->value;
313             }
314              
315             sub _record_get_stats {
316 3     3   9 my ( $self, $stat, $elapsed_time ) = @_;
317 3         11 $self->{ns_stats}->{$stat}++;
318 3         7 $self->{ns_stats}->{'get_time_ms'} += $elapsed_time;
319             }
320              
321             sub unpack_from_data {
322 11831     11831 0 22897 my ( $self, $key, $data ) = @_;
323              
324 11831         24038 return $self->cache_object_class->unpack_from_data( $key, $data,
325             $self->serializer );
326             }
327              
328             sub get_object {
329 14546     14546 0 26613 my ( $self, $key ) = @_;
330              
331 14546 100       29243 croak "must specify key" unless defined($key);
332 14538         31259 $key = $self->transform_key($key);
333              
334 14538 100       41077 my $data = $self->fetch($key) or return undef;
335 11485         779501 my $obj = $self->unpack_from_data( $key, $data );
336 11485         33769 return $obj;
337             }
338              
339             sub get_expires_at {
340 88     88 0 282 my ( $self, $key ) = @_;
341 88 100       381 croak "must specify key" unless defined($key);
342              
343 80 100       252 if ( my $obj = $self->get_object($key) ) {
344 72         202 return $obj->expires_at;
345             }
346             else {
347 8         192 return;
348             }
349             }
350              
351             sub exists_and_is_expired {
352 24     24 0 81 my ( $self, $key ) = @_;
353 24 100       173 croak "must specify key" unless defined($key);
354              
355 16 50       51 if ( my $obj = $self->get_object($key) ) {
356 16         57 return $obj->is_expired;
357             }
358             else {
359 0         0 return;
360             }
361             }
362              
363             sub is_valid {
364 80     80 0 229 my ( $self, $key ) = @_;
365 80 100       342 croak "must specify key" unless defined($key);
366              
367 72 100       239 if ( my $obj = $self->get_object($key) ) {
368 40         129 return !$obj->is_expired;
369             }
370             else {
371 32         853 return;
372             }
373             }
374              
375             sub _default_set_options {
376 3889     3889   6209 my $self = shift;
377              
378 3889         7554 return { map { $_ => $self->$_() }
  11667         175292  
379             qw( expires_at expires_in expires_variance ) };
380             }
381              
382             sub set {
383 4261     4261 0 63273 my $self = shift;
384 4261         9824 my ( $key, $value, $options ) = @_;
385              
386 4261 100       10069 croak "must specify key" unless defined($key);
387 4253         11062 $key = $self->transform_key($key);
388 4253 50       9314 return unless defined($value);
389              
390             # Fill in $options if not passed, copy if passed, and apply defaults.
391             #
392 4253 100       9274 if ( !defined($options) ) {
393 3846         9225 $options = $self->_default_set_options;
394             }
395             else {
396 407 100       1080 if ( !ref($options) ) {
397 78 100       353 if ( $options eq 'never' ) {
    50          
398 9         37 $options = { expires_at => CHI_Max_Time };
399             }
400             elsif ( $options eq 'now' ) {
401 0         0 $options = { expires_in => 0 };
402             }
403             else {
404 69         220 $options = { expires_in => $options };
405             }
406             }
407              
408             # Disregard default expires_at and expires_in if either are provided
409             #
410 407 100 100     1535 if ( exists( $options->{expires_at} )
411             || exists( $options->{expires_in} ) )
412             {
413 364         8239 $options =
414             { expires_variance => $self->expires_variance, %$options };
415             }
416             else {
417 43         80 $options = { %{ $self->_default_set_options }, %$options };
  43         126  
418             }
419             }
420              
421 4253         43294 $self->set_with_options( $key, $value, $options );
422             }
423              
424             sub set_with_options {
425 4253     4253 0 9559 my ( $self, $key, $value, $options ) = @_;
426 4253         7554 my $ns_stats = $self->{ns_stats};
427 4253         13904 my $log_is_debug = $log->is_debug;
428 4253   66     43811 my $measure_time = defined($ns_stats) || $log_is_debug;
429 4253         6665 my ( $start_time, $elapsed_time );
430              
431             # Determine early and final expiration times
432             #
433 4253   66     12554 my $time = $Test_Time || time();
434 4253         6097 my $created_at = $time;
435             my $expires_at =
436             ( defined( $options->{expires_in} ) )
437             ? $time + parse_duration( $options->{expires_in} )
438 4253 100       9899 : $options->{expires_at};
439             my $early_expires_at =
440             defined( $options->{early_expires_at} ) ? $options->{early_expires_at}
441             : ( $expires_at == CHI_Max_Time ) ? CHI_Max_Time
442             : $expires_at -
443 4253 100       14833 ( ( $expires_at - $time ) * $options->{expires_variance} );
    100          
444              
445             # Pack into data, and store
446             #
447 4253         10018 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 4253 100       10988 if ( defined( my $obj_ref = $options->{obj_ref} ) ) {
452 16         51 $$obj_ref = $obj;
453             }
454 4253 50       14152 $start_time = gettimeofday() if $measure_time;
455 4253 100       37757 if ( $self->set_object( $key, $obj ) ) {
456 4247 50       16405 $elapsed_time = ( gettimeofday() - $start_time ) * 1000
457             if $measure_time;
458              
459             # Log the set
460             #
461 4247 100       10097 if ( defined($ns_stats) ) {
462 2         7 $ns_stats->{'sets'}++;
463 2         10 $ns_stats->{'set_key_size'} += length( $obj->key );
464 2         9 $ns_stats->{'set_value_size'} += $obj->size;
465 2         6 $ns_stats->{'set_time_ms'} += $elapsed_time;
466             }
467 4247 50       8329 if ($log_is_debug) {
468 4247         12175 $self->_log_set_result( $log, $obj, $elapsed_time );
469             }
470             }
471              
472 4250         330865 return $value;
473             }
474              
475             sub set_object {
476 4463     4463 0 27889 my ( $self, $key, $obj ) = @_;
477              
478 4463         11629 my $data = $obj->pack_to_data();
479 4463         11378 my $expires_on_backend = $self->expires_on_backend;
480 4463 100 66     14126 my @expires_in = (
481             $expires_on_backend && $obj->expires_at < CHI_Max_Time
482             ? ( ( $obj->expires_at - time ) * $expires_on_backend )
483             : ()
484             );
485 4463         7016 eval { $self->store( $key, $data, @expires_in ) };
  4463         14570  
486 4463 100       513553 if ( my $error = $@ ) {
487 6 50       25 $self->{ns_stats}->{'set_errors'}++ if defined( $self->{ns_stats} );
488 6         53 $self->_handle_set_error( $error, $obj );
489 3         10 return 0;
490             }
491 4457         17899 return 1;
492             }
493              
494             sub get_keys_iterator {
495 60     60 0 142 my ($self) = @_;
496              
497 60         426 my @keys = $self->get_keys();
498 60     160   94294 return sub { shift(@keys) };
  160         560  
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 209     209 0 4341 my ( $self, $key ) = @_;
510 209 100       693 croak "must specify key" unless defined($key);
511              
512 201   33     970 my $time = $Test_Time || time();
513 201 50       528 if ( defined( my $obj = $self->get_object($key) ) ) {
514 201         351 my $expires_at = $time - 1;
515 201         606 $obj->set_early_expires_at($expires_at);
516 201         523 $obj->set_expires_at($expires_at);
517 201         535 $self->set_object( $key, $obj );
518             }
519             }
520              
521             sub compute {
522 56     56 0 134 my $self = shift;
523 56         104 my $key = shift;
524 56         103 my $wantarray = wantarray();
525              
526             # Allow these in either order for backward compatibility
527 56 100       269 my ( $code, $options ) =
528             ( ref( $_[0] ) eq 'CODE' ) ? ( $_[0], $_[1] ) : ( $_[1], $_[0] );
529              
530 56 100 66     392 croak "must specify key and code" unless defined($key) && defined($code);
531              
532             my %get_options =
533             ( ref($options) eq 'HASH' )
534 48 50   32   352 ? slice_grep { /(?:expire_if|busy_lock)/ } $options
  32         758  
535             : ();
536             my $set_options =
537             ( ref($options) eq 'HASH' )
538 48 50   32   611 ? { slice_grep { !/(?:expire_if|busy_lock)/ } $options }
  32         489  
539             : $options;
540              
541 48         799 my $value = $self->get( $key, %get_options );
542 48 100       176 if ( !defined $value ) {
543 40         70 my ( $start_time, $elapsed_time );
544 40         76 my $ns_stats = $self->{ns_stats};
545 40 50       89 $start_time = gettimeofday if defined($ns_stats);
546 40 100       185 $value = $wantarray ? [ $code->() ] : $code->();
547 40 50       102 $elapsed_time = ( gettimeofday() - $start_time ) * 1000
548             if defined($ns_stats);
549 40         446 $self->set( $key, $value, $set_options );
550 40 50       137 if ( defined($ns_stats) ) {
551 0         0 $ns_stats->{'computes'}++;
552 0         0 $ns_stats->{'compute_time_ms'} += $elapsed_time;
553             }
554             }
555 48 100       317 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 32     32 0 92 my ($self) = @_;
572              
573 32         57 my %hash;
574 32         146 foreach my $key ( $self->get_keys() ) {
575 416 50       41164 if ( defined( my $value = $self->get($key) ) ) {
576 416         1242 $hash{$key} = $value;
577             }
578             }
579 32         333 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 24     24 0 83 my $self = shift;
594 24         43 my $key = shift;
595              
596 24 100       110 if ( !$self->is_valid($key) ) {
597 16         274 $self->set( $key, @_ );
598             }
599             }
600              
601             sub append {
602 18     18 0 396 my ( $self, $key, $new ) = @_;
603              
604 18 100       71 my $current = $self->fetch($key) or return undef;
605 12         1792 $self->store( $key, $current . $new );
606 12         2622 return 1;
607             }
608              
609             sub replace {
610 16     16 0 42 my $self = shift;
611 16         30 my $key = shift;
612              
613 16 100       72 if ( $self->is_valid($key) ) {
614 8         111 $self->set( $key, @_ );
615             }
616             }
617              
618             #
619             # MULTI KEY OPERATIONS
620             #
621              
622             sub fetch_multi_hashref {
623 112     112 0 391 my ( $self, $keys ) = @_;
624              
625 112         254 return { map { ( $_, $self->fetch($_) ) } @$keys };
  544         24950  
626             }
627              
628             sub get_multi_hashref_objects {
629 112     112 0 252 my ( $self, $keys ) = @_;
630 112         438 my $key_data = $self->fetch_multi_hashref($keys);
631             return {
632             map {
633 112         3020 my $data = $key_data->{$_};
  544         812  
634 544 100       1452 defined($data)
635             ? ( $_, $self->unpack_from_data( $_, $data ) )
636             : ( $_, undef )
637             } keys(%$key_data)
638             };
639             }
640              
641             sub get_multi_arrayref {
642 120     120 0 927 my ( $self, $keys ) = @_;
643 120 100       680 croak "must specify keys" unless defined($keys);
644 112         267 my $transformed_keys = [ map { $self->transform_key($_) } @$keys ];
  544         956  
645              
646 112         252 my $key_count = scalar(@$keys);
647 112         501 my $keyobjs = $self->get_multi_hashref_objects($transformed_keys);
648             return [
649             map {
650 112         420 my $key = $transformed_keys->[$_];
  544         954  
651 544         836 my $obj = $keyobjs->{$key};
652 544 100       3858 $obj ? $self->get( $key, obj => $obj ) : undef
653             } ( 0 .. $key_count - 1 )
654             ];
655             }
656              
657             sub get_multi_hashref {
658 36     36 0 125 my ( $self, $keys ) = @_;
659 36 100       247 croak "must specify keys" unless defined($keys);
660              
661 28         70 my $key_count = scalar(@$keys);
662 28         755 my $values = $self->get_multi_arrayref($keys);
663 28         129 return { map { ( $keys->[$_], $values->[$_] ) } ( 0 .. $key_count - 1 ) };
  124         424  
664             }
665              
666             sub set_multi {
667 96     96 0 301 my $self = shift;
668 96         577 $self->store_multi(@_);
669             }
670              
671             sub store_multi {
672 96     96 0 298 my ( $self, $key_values, $set_options ) = @_;
673 96 100       455 croak "must specify key_values" unless defined($key_values);
674              
675 88         550 while ( my ( $key, $value ) = each(%$key_values) ) {
676 232         5188 $self->set( $key, $value, $set_options );
677             }
678             }
679              
680             sub remove_multi {
681 24     24 0 90 my ( $self, $keys ) = @_;
682 24 100       193 croak "must specify keys" unless defined($keys);
683              
684 16         71 foreach my $key (@$keys) {
685 104         5011 $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 22138     22138 0 36481 my ( $self, $key ) = @_;
703              
704 22138 100 66     87265 if ( ref($key) ) {
    100          
705 769         3244 $key = $self->key_serializer->serialize($key);
706             }
707             elsif ( Encode::is_utf8($key) && $key =~ /[^\x00-\xFF]/ ) {
708 267         918 $key = $self->encode_key($key);
709             }
710 22138 100       72846 if ( length($key) > $self->max_key_length ) {
711 79         366 $key = $self->digest_key($key);
712             }
713              
714 22138         69090 return $key;
715             }
716              
717             sub digest_key {
718 175     175 0 371 my ( $self, $key ) = @_;
719              
720 175         2138 return $self->key_digester->add($key)->hexdigest;
721             }
722              
723             sub encode_key {
724 267     267 0 613 my ( $self, $key ) = @_;
725              
726 267         1098 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 404     404 0 737 sub escape_key { $_[1] }
733 404     404 0 1238 sub unescape_key { $_[1] }
734              
735             # May be used by drivers to implement escape_key/unescape_key.
736             #
737             sub escape_for_filename {
738 10240     10240 0 17913 my ( $self, $key ) = @_;
739              
740 10240 50       26786 $key =~ s/([^A-Za-z0-9_\=\-\~])/$escapes{$1} || $_fail_hi->($1)/ge;
  20072         62396  
741 10240         24001 return $key;
742             }
743              
744             sub unescape_for_filename {
745 3216     3216 0 4841 my ( $self, $key ) = @_;
746              
747 3216 50       8204 $key =~ s/\+([0-9A-Fa-f]{2})/chr(hex($1))/eg if defined $key;
  5044         12907  
748 3216         7794 return $key;
749             }
750              
751             sub is_escaped_for_filename {
752 186     186 0 338 my ( $self, $key ) = @_;
753              
754 186         316 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 11934     11934   17473 my $self = shift;
764 11934         15533 my $log = shift;
765 11934         15964 my $msg = shift;
766 11934         26816 $log->debug( sprintf( "%s: %s", $self->_describe_cache_get(@_), $msg ) );
767             }
768              
769             sub _log_set_result {
770 4247     4247   7355 my $self = shift;
771 4247         5928 my $log = shift;
772 4247         10998 $log->debug( $self->_describe_cache_set(@_) );
773             }
774              
775             sub _handle_get_error {
776 4     4   8 my $self = shift;
777 4         5 my $error = shift;
778 4         7 my $key = $_[0];
779              
780 4         14 my $msg =
781             sprintf( "error during %s: %s", $self->_describe_cache_get(@_), $error );
782 4         115 $self->_dispatch_error_msg( $msg, $error, $self->on_get_error(), $key );
783             }
784              
785             sub _handle_set_error {
786 6     6   16 my ( $self, $error, $obj ) = @_;
787              
788 6         26 my $msg =
789             sprintf( "error during %s: %s", $self->_describe_cache_set($obj),
790             $error );
791 6         149 $self->_dispatch_error_msg( $msg, $error, $self->on_set_error(),
792             $obj->key );
793             }
794              
795             sub _dispatch_error_msg {
796 10     10   52 my ( $self, $msg, $error, $on_error, $key ) = @_;
797              
798 10         26 for ($on_error) {
799 10 100       33 ( ref($_) eq 'CODE' ) && do { $_->( $msg, $key, $error ) };
  2         10  
800             /^log$/
801 10 100       33 && do { $log->error($msg) };
  2         20  
802 10 100       125 /^ignore$/ && do { };
803 10 50       24 /^warn$/ && do { carp $msg };
  0         0  
804 10 100       34 /^die$/ && do { croak $msg };
  4         42  
805             }
806             }
807              
808             sub _describe_cache_get {
809 11938     11938   22186 my ( $self, $key, $elapsed_time ) = @_;
810              
811             return
812 11938 100       294542 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 4253     4253   9253 my ( $self, $obj, $elapsed_time ) = @_;
820              
821 4253 100       11069 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 4253 100       46464 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__