File Coverage

blib/lib/Cache/Isolator.pm
Criterion Covered Total %
statement 21 63 33.3
branch 0 18 0.0
condition 0 18 0.0
subroutine 7 15 46.6
pod 5 5 100.0
total 33 119 27.7


line stmt bran cond sub pod time code
1             package Cache::Isolator;
2              
3 3     3   326706 use strict;
  3         8  
  3         105  
4 3     3   17 use warnings;
  3         7  
  3         91  
5 3     3   16 use Carp;
  3         10  
  3         217  
6 3     3   10982 use Try::Tiny;
  3         5964  
  3         194  
7 3     3   11686 use Time::HiRes;
  3         2283  
  3         28  
8 3     3   366 use List::Util qw/shuffle/;
  3         7  
  3         379  
9             use Class::Accessor::Lite (
10 3         27 ro => [ qw(cache interval timeout concurrency trial early_expires_ratio expires_before) ],
11 3     3   957 );
  3         1220  
12              
13             our $VERSION = '0.02';
14              
15             sub new {
16 0     0 1   my $class = shift;
17 0           my %args = (
18             interval => 0.01,
19             timeout => 10,
20             trial => 0,
21             concurrency => 1,
22             early_expires_ratio => 0,
23             expires_before => 10,
24             @_
25             );
26              
27 0 0 0       croak('cache value should be object and appeared add, set and delete methods.')
      0        
      0        
      0        
28             unless ( $args{cache}
29             && UNIVERSAL::can( $args{cache}, 'get' )
30             && UNIVERSAL::can( $args{cache}, 'set' )
31             && UNIVERSAL::can( $args{cache}, 'add' )
32             && UNIVERSAL::can( $args{cache}, 'delete' ) );
33              
34 0           bless \%args, $class;
35             }
36              
37             sub get_or_set {
38 0     0 1   my ($self, $key, $cb, $expires ) = @_;
39              
40 0           my $value;
41 0           my $try = 0;
42              
43 0           TRYLOOP: while ( 1 ) {
44 0           $value = $self->get($key);
45 0 0         last TRYLOOP if $value;
46              
47 0           $try++;
48 0           my @lockkeys = map { $key .":lock:". $_ } shuffle 1..$self->concurrency;
  0            
49 0           foreach my $lockkey ( @lockkeys ) {
50 0           my $locked = $self->cache->add($lockkey, 1, $self->timeout ); #lock
51 0 0         if ( $locked ) {
52             try {
53 0     0     $value = $self->get($key);
54 0 0         return 1 if $value;
55 0           $value = $cb->();
56 0           $self->set( $key, $value, $expires );
57             }
58             catch {
59 0     0     die $_;
60             }
61             finally {
62 0     0     $self->cache->delete( $lockkey ); #lock
63 0           };
64 0           last TRYLOOP;
65             }
66             }
67 0 0 0       die "reached max trial count" if $self->trial > 0 && $try >= $self->trial;
68 0           Time::HiRes::sleep( $self->interval );
69             }
70 0           return $value;
71             }
72              
73             sub set {
74 0     0 1   my ($self, $key, $value, $expires) = @_;
75 0           $self->cache->set($key, $value, $expires);
76 0 0         if ( $self->early_expires_ratio > 0 ) {
77 0           $expires = $expires - $self->expires_before;
78 0 0         $expires = 1 if $expires <= 0;
79 0           $self->cache->set($key . ":earlyexp", $value, $expires);
80             }
81             }
82              
83             sub get {
84 0     0 1   my ($self, $key) = @_;
85 0 0 0       if ( $self->early_expires_ratio > 0 &&
86             int(rand($self->early_expires_ratio)) == 0 ) {
87 0           return $self->cache->get($key.":earlyexp");
88             }
89 0           my $result = $self->cache->get($key);
90 0 0         $result = $self->cache->get($key.":earlyexp") if ! defined $result;
91 0           $result;
92             }
93              
94             sub delete {
95 0     0 1   my ($self, $key) = @_;
96 0           $self->cache->delete($key.":earlyexp");
97 0           $self->cache->delete($key);
98             }
99              
100             1;
101             __END__