File Coverage

blib/lib/Apache/Cache.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Apache::Cache;
2             #$Id: Cache.pm,v 1.24 2001/09/27 12:56:27 rs Exp $
3              
4             =pod
5              
6             =head1 NAME
7              
8             Apache::Cache - Cache data accessible between Apache childrens
9              
10             =head1 SYNOPSIS
11              
12             use Apache::Cache qw(:status);
13              
14             my $cache = new Apache::Cache(default_expires_in=>"5 minutes");
15              
16             # if the if the next line is called within 10 minutes, then this
17             # will return the cache value overwise, this will return undef and the
18             # status method will be equal to the constant EXPIRED (exported by Apache::Cache
19             # on demande via the :status tag)
20              
21             # the next line try to get the data from the cache, if the data is stored in
22             # in the cache and if it not expired, then this return the data. Otherwise
23             # if data have never been store in the cache, or if it's expired, this will
24             # return undef and the status() method will be equal to constant EXPIRED (exported
25             # by Apache::Cache on demand, via the :status tag)
26              
27             my $value = $cache->get('Key');
28              
29             if($cache->status eq EXPIRED)
30             {
31             # can't get the data from the cache, we will need to get it by the normal way
32             # (via database, from file...)
33             $value = get_my_data('Key'); # here, the get_my_data() function is a function of your
34             # programe that generate a fresh value
35              
36             # this data have to expires in 30 secondes
37             my $expires_in = '30 secondes';
38             $cache->set(Key => $value, $expires_in);
39             }
40             elsif($cache->status eq FAILURE)
41             {
42             # don't use cache, cache maybe busy by another child or something goes wrong
43             $value = get_my_data('Key');
44             }
45              
46             =head1 DESCRIPTION
47              
48             This module allows you to cache data easily through shared memory. Whithin the framework
49             of an apache/mod_perl use, this cache is accessible from any child process. The data
50             validity is managed in the Cache::Cache model, but as well based on time than on size
51             or number of keys.
52              
53             Additionnally, you can implement a cache with Apache::Cache in your module without the risk
54             of namespace clash because Apache::Cache is enclosed in the constructor's package's caller
55             (see L for more details).
56              
57             =head1 USAGE
58              
59             For mod_perl users:
60              
61             in your httpd.conf, put this directive:
62              
63             PerlAddVar PROJECT_DOCUMENT_ROOT /path/to/your/project/root/
64              
65             and in your startup.pl:
66              
67             use Apache::Cache ();
68              
69             See L for more details.
70              
71             =cut
72              
73             BEGIN
74             {
75 3     3   20393 use strict;
  3         7  
  3         107  
76 3     3   57 use 5.005;
  3         9  
  3         104  
77 3     3   17 use Carp;
  3         9  
  3         210  
78 3     3   5018 use Apache::SharedMem qw(:all);
  0            
  0            
79             use Time::ParseDate;
80              
81             use base qw(Apache::SharedMem Exporter);
82              
83             %Apache::Cache::EXPORT_TAGS =
84             (
85             all => [qw(EXPIRED SUCCESS FAILURE EXPIRES_NOW EXPIRES_NEVER LOCK_EX LOCK_SH LOCK_UN LOCK_NB)],
86             expires => [qw(EXPIRES_NOW EXPIRES_NEVER)],
87             status => [qw(SUCCESS FAILURE EXPIRED)],
88             lock => [qw(LOCK_EX LOCK_SH LOCK_UN LOCK_NB)],
89             );
90             @Apache::Cache::EXPORT_OK = @{$Apache::Cache::EXPORT_TAGS{'all'}};
91              
92             # SUCCESS => 1
93             # FAILURE => 2
94             use constant EXPIRED => 4;
95              
96             use constant EXPIRES_NOW => 1;
97             use constant EXPIRES_NEVER => 0;
98              
99             $Apache::Cache::VERSION = '0.05';
100             }
101              
102             =pod
103              
104             =head1 METHODS
105              
106             =head2 new (cachename=> 'cachename', default_expires_in=> '1 second', max_keys=> 50, max_size=> 1_000)
107              
108             Constuct a new Apache::Cache's instance.
109              
110             =over 4
111              
112             =item *
113              
114             C optional, date
115              
116             The default data expiration time for objects place in the cache. Integers is interpreted in seconds, constant
117             EXPIRES_NOW make data expire imédiately and constant EXPIRES_NEVER make the data never expire. The
118             timeout can also be in a human readable format, see L for this format specification.
119              
120             Defaults to constant EXPIRES_NEVER if not explicitly set.
121              
122             =item *
123              
124             C optional, integer
125              
126             If you set more than C keys, olders are automatically removed. Usefull to control the cache's grow.
127             NOTE: if you know the exact length of your keys, use this option to control the cache size instead of the
128             C option.
129              
130             Defaults to no max_keys
131              
132             =item *
133              
134             C optional, integer
135              
136             no yet implemented
137              
138             =item *
139              
140             C optional, string
141              
142             The namespace associated with this cache.
143              
144             Defaults to "Default" if not explicitly set.
145              
146             =item *
147              
148             C optional, integer
149              
150             Number of second(s) to wait for locks used each time manipulating data in the shared memory.
151              
152             Defaults to not waiting. This means a get() - for expample - on a temporary locked
153             key - certainely by another process - will return a FAILED status.
154              
155             =back
156              
157             Additionnaly, all Apache::SharedMem parameters are also customizable. See L.
158              
159             =cut
160              
161             sub new
162             {
163             my $pkg = shift;
164             my $class = ref($pkg) || $pkg;
165              
166             my $options =
167             {
168             namespace => (caller())[0],
169             cachename => 'Default',
170             default_expires_in => EXPIRES_NEVER,
171             max_keys => undef(),
172             max_size => undef(),
173             default_lock_timeout=> undef(),
174             };
175              
176             croak("odd number of arguments for object construction")
177             if(@_ % 2);
178             my @del;
179             for(my $x = 0; $x < $#_; $x += 2)
180             {
181             if(exists($options->{lc($_[$x])}))
182             {
183             $options->{lc($_[$x])} = $_[($x + 1)];
184             # We split off this parameter from the main argument list.
185             # Remaining arguments will be send to Apache::SharedMem
186             splice(@_, $x, 2);
187             $x -= 2;
188             }
189             }
190              
191             foreach my $name (qw(cachename namespace))
192             {
193             croak("$pkg object creation missing $name parameter.")
194             unless(defined($options->{$name}) && $options->{$name} ne '');
195             }
196              
197             my $self = $class->SUPER::new(@_, namespace=>$options->{namespace});
198             return(undef()) unless(defined($self));
199             $self->{cache_options} = $options;
200              
201             unless($self->SUPER::exists($options->{cachename}, $self->_lock_timeout))
202             {
203             return(undef()) if($self->SUPER::status eq FAILURE);
204             $self->_init_cache || return undef;
205             }
206              
207             bless($self, $class);
208             return($self);
209             }
210              
211             =pod
212              
213             =head2 set (identifier => data, [timeout])
214              
215             $cache->set(mykey=>'the data to cache', '15 minutes');
216             if($cache->status & FAILURE)
217             {
218             warn("can't save data to cache: $cache->error");
219             }
220              
221             Store an item in the cache.
222              
223             =over 4
224              
225             =item *
226              
227             C required, string
228              
229             A string uniquely identifying the data.
230              
231             =item *
232              
233             C required, scalar or reference to any perl data type, except CODE and GLOB
234              
235             The data to store in the cache.
236              
237             =item *
238              
239             C optional, date
240              
241             The data expiration time for objects place in the cache. Integers is interpreted in seconds, constant
242             EXPIRES_NOW make data expire imédiately and constant EXPIRES_NEVER make the data never expire. The
243             timeout can also be in a human readable format, see L for this format specification.
244              
245             =back
246              
247             On failure this method return C and set status to FAILURE, see status() method below
248              
249             status : FAILURE SUCCESS
250              
251             =cut
252              
253             sub set
254             {
255             my $self = shift;
256             my $key = defined($_[0]) && $_[0] ne '' ? shift : croak(defined($_[0]) ? 'Not enough arguments for set method' : 'Invalid argument "" for set method');
257             my $value = defined($_[0]) ? shift : croak('Not enough arguments for set method');
258             my $time = defined($_[0]) ? shift : $self->{cache_options}->{default_expires_in};
259             my $lock_timeout = $self->{cache_options}->{default_lock_timeout};
260             croak('Too many arguments for set method') if(@_);
261             $self->_unset_error;
262             $self->_debug;
263              
264             if($key eq '_cache_metadata')
265             {
266             $self->_set_status(FAILURE);
267             $self->_set_error("$key: reserved key");
268             return(undef());
269             }
270              
271             my $timeout;
272             if($time)
273             {
274             if($time =~ m/\D/)
275             {
276             $timeout = parsedate($time, TIMEFIRST=>1, PREFER_FUTURE=>1);
277             unless(defined $timeout)
278             {
279             $self->_set_error("error on timeout string decoding. time string requested: $time");
280             $self->_set_status(FAILURE);
281             return(undef());
282             }
283             }
284             elsif($time eq EXPIRES_NOW)
285             {
286             $timeout = EXPIRES_NOW;
287             }
288             else
289             {
290             $timeout = time() + $time;
291             }
292             }
293             else
294             {
295             $timeout = EXPIRES_NEVER;
296             }
297              
298             $self->_debug('timeout is set for expires in ', ($timeout - time()), ' seconds');
299              
300             if(defined $lock_timeout ? $self->lock(LOCK_EX, $lock_timeout) : $self->lock(LOCK_EX|LOCK_NB))
301             {
302             my $data = $self->_get_datas || return(undef());
303             $data->{$key} = $value;
304             $data->{'_cache_metadata'}->{'timestamps'}->{$key} = $timeout;
305             push(@{$data->{'_cache_metadata'}->{'queue'}}, $key);
306              
307             $self->_check_keys($data);
308             $self->_check_size($data);
309              
310             $self->SUPER::set($self->{cache_options}->{cachename}=>$data, NOWAIT);
311             my $rv = $self->status; # saving returned status
312             $self->unlock; # don't wait for Apache::SharedMem to auto unlock on destroy
313             return(undef()) if($rv eq FAILURE);
314              
315             return($value);
316             }
317             else
318             {
319             $self->_set_error('can\'t get exclusive lock for "set" method');
320             $self->_set_status(FAILURE);
321             return(undef());
322             }
323             }
324              
325             =pod
326              
327             =head2 get (identifier)
328              
329             my $value = $cache->get('Key');
330              
331             if($cache->status & (EXPIRED | FAILURE)) # if status is EXPIRED or FAILURE
332             {
333             $value = 'fresh value';
334             }
335              
336             Fetch the data specified. If data where never set, or if data have expired, this method return
337             C and status is set to EXPIRED.
338              
339             =over 4
340              
341             =item *
342              
343             C required, string
344              
345             A string uniquely identifying the data.
346              
347             =back
348              
349             status : FAILURE SUCCESS EXPIRED
350              
351             =cut
352              
353             sub get
354             {
355             if(@_ != 2)
356             {
357             confess('Apache::Cache: Too many arguments for "get" method') if(@_ > 2);
358             confess('Apache::Cache: Not enough arguments for "get" method') if(@_ < 2);
359             }
360             my($self, $key) = @_;
361            
362             my $data = $self->_get_datas || return(undef());
363             unless(exists $data->{$key})
364             {
365             $self->_set_status(EXPIRED);
366             return(undef());
367             }
368             my $value = $data->{$key};
369             my $timeout = $data->{_cache_metadata}->{timestamps}->{$key};
370              
371             if(!defined $timeout || $timeout == EXPIRES_NOW || ($timeout != EXPIRES_NEVER && $timeout <= time()))
372             {
373             $self->_set_error("data was expired");
374             $self->delete($key); # if delete failed, error string will be its own but not status
375             $self->_set_status(EXPIRED);
376             return(undef());
377             }
378             else
379             {
380             $self->_set_status(SUCCESS);
381             return($value);
382             }
383             }
384              
385             =pod
386              
387             =head2 delete (identifier)
388              
389             Delete the data associated with the identifier from the cache.
390              
391             =over 4
392              
393             =item *
394              
395             C required, string
396              
397             A string uniquely identifying the data.
398              
399             =back
400              
401             status: SUCCESS FAILURE
402              
403             =cut
404              
405             sub delete
406             {
407             if(@_ != 2)
408             {
409             confess('Apache::Cache: Too many arguments for "delete" method') if(@_ > 2);
410             confess('Apache::Cache: Not enough arguments for "delete" method') if(@_ < 2);
411             }
412             my($self, $key) = @_;
413             my $lock_timeout = $self->{cache_options}->{default_lock_timeout};
414              
415             my $rv = undef;
416             if(defined $lock_timeout ? $self->lock(LOCK_EX, $lock_timeout) : $self->lock(LOCK_EX|LOCK_NB))
417             {
418             my $data = $self->_get_datas || return(undef());
419             if(exists $data->{$key})
420             {
421             $rv = delete($data->{$key});
422             delete($data->{_cache_metadata}->{timestamps}->{$key});
423             $data->{_cache_metadata}->{queue} = \@{grep($_ ne $key, @{$data->{_cache_metadata}->{queue}})};
424             $self->SUPER::set($self->{cache_options}->{cachename}=>$data);
425             return(undef()) if($self->status & FAILURE);
426             }
427             $self->unlock;
428             }
429             return($rv);
430             }
431              
432             =head2 clear
433              
434             Remove all objects from the namespace associated with this cache instance.
435              
436             status: SUCCESS FAILURE
437              
438             =cut
439              
440             sub clear
441             {
442             my $self = shift;
443             $self->_init_cache;
444             }
445              
446             # inherited from Apache::SharedMem
447              
448             =head2 status
449              
450             Return the last called method status. This status should be used with bitmask operators
451             &, ^, ~ and | like this :
452              
453             # is last method failed ?
454             if($object->status & FAILURE) {something to do on failure}
455              
456             # is last method don't succed ?
457             if($object->status ^ SUCCESS) {something to do on failure}
458              
459             # is last method failed or expired ?
460             if($object->status & (FAILURE | EXPIRED)) {something to do on expired or failure}
461              
462             It's not recommended to use equality operator (== and !=) or (eq and ne), they may don't
463             work in future versions.
464              
465             To import status' constants, you have to use the :status import tag, like below :
466              
467             use Apache::Cache qw(:status);
468              
469             =cut
470            
471              
472             sub _check_keys
473             {
474             my($self, $data) = @_;
475              
476             my $max_keys = $self->{cache_options}->{max_keys};
477             return() unless(defined $max_keys && $max_keys);
478             my $metadata = $data->{_cache_metadata};
479             my $nkeys = @{$metadata->{queue}};
480             $self->_debug("cache have now $nkeys keys");
481             if($nkeys > $max_keys)
482             {
483             my $time = time();
484             my $nkeys_target = int($max_keys - ($max_keys/10));
485             $self->_debug("cache is full, max_key: $max_keys, current key counts: $nkeys, cleaning ", $nkeys - $nkeys_target, " keys");
486             # cheching for expired datas
487             for(my $i = $nkeys - 1; $i >= 0; $i--)
488             {
489             if($metadata->{timestamps}->{$metadata->{queue}->[$i]} > $time)
490             {
491             my $key = $metadata->{queue}->[$i];
492             $self->_debug("$key is out of date, discarding");
493             delete($data->{$key});
494             delete($metadata->{timestamps}->{$key});
495             @{$metadata->{queue}} = grep($_ ne $key, @{$metadata->{queue}});
496             last if(--$nkeys <= $nkeys_target);
497             }
498             }
499             if($nkeys > $nkeys_target)
500             {
501             # splice of delete candidates
502             my @key2del = splice(@{$metadata->{queue}}, 0, ($nkeys - $nkeys_target - 1));
503             $self->_debug('cleaning not timed out keys: ', join(', ', @key2del));
504             delete(@$data{@key2del});
505             delete(@{$metadata->{timestamps}}{@key2del});
506             }
507             }
508             }
509              
510             sub _check_size
511             {
512             my($self, $data) = @_;
513              
514             my $max_size = $self->{cache_options}->{max_keys};
515             return() unless(defined $max_size && $max_size);
516             }
517              
518             sub _init_cache
519             {
520             my $self = shift;
521             my $cache_registry =
522             {
523             _cache_metadata =>
524             {
525             timestamps => {},
526             queue => [],
527             }
528             };
529             $self->SUPER::set($self->{cache_options}->{cachename}=>$cache_registry, $self->_lock_timeout);
530              
531             return($self->SUPER::status eq FAILURE ? undef : 1);
532             }
533              
534             sub _lock_timeout
535             {
536             my $self = shift;
537             my $lock_timeout = $self->{cache_options}->{default_lock_timeout};
538             return(defined $lock_timeout ? $lock_timeout : NOWAIT);
539             }
540              
541             sub _get_datas
542             {
543             my $self = shift;
544            
545             my $data = $self->SUPER::get($self->{cache_options}->{cachename}, $self->_lock_timeout);
546             if($self->status eq FAILURE)
547             {
548             $self->_set_error("can't get the cacheroot: ", $self->error);
549             return(undef());
550             }
551              
552             croak("Apache::Cache: wrong data format.")
553             if(ref($data) ne 'HASH' || ! exists $data->{_cache_metadata});
554            
555             return($data);
556             }
557              
558             1;
559              
560             =pod
561              
562             =head1 EXPORTS
563              
564             =head2 Default exports
565              
566             None.
567              
568             =head2 Available exports
569              
570             Following constant is available for exports : EXPIRED SUCCESS FAILURE
571             EXPIRES_NOW EXPIRES_NEVER LOCK_EX LOCK_SH LOCK_UN.
572              
573             =head2 Export tags defined
574              
575             The tag ":all" will get all of the above exports.
576             Following tags are also available :
577              
578             =over 4
579              
580             =item
581              
582             :status
583              
584             Contents: SUCCESS FAILURE EXPIRED
585              
586             This tag is really recommended to the importation all the time.
587              
588             =item
589              
590             :expires
591              
592             Contents: EXPIRES_NOW EXPIRES_NEVER
593              
594             =item
595              
596             :lock
597              
598             Contents: LOCK_EX LOCK_SH LOCK_UN LOCK_NB
599              
600             =back
601              
602             =head1 KNOW BUGS
603              
604             Under mod_perl, with eavy load, this error may occured some time:
605              
606             Apache::SharedMem object initialization: Unable to initialize root ipc shared memory
607             segment: File exists at /usr/local/lib/perl5/site_perl/5.005/Apache/SharedMem.pm line 929
608              
609             We not really understand the probleme source, so any help will be appreciated. For fixing
610             this problem when it occured, you should stop apache, clean the ipc segment and restart
611             apache.
612              
613             =head1 AUTHOR
614              
615             Olivier Poitrey Ers@rhapsodyk.netE
616              
617             =head1 LICENCE
618              
619             This program is free software; you can redistribute it and/or modify
620             it under the terms of the GNU General Public License as published by
621             the Free Software Foundation; either version 2 of the License, or (at
622             your option) any later version.
623              
624             This program is distributed in the hope that it will be useful, but
625             WITHOUT ANY WARRANTY; without even the implied warranty of
626             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
627             General Public License for more details.
628              
629             You should have received a copy of the GNU General Public License
630             along with the program; if not, write to the Free Software
631             Foundation, Inc. :
632              
633             59 Temple Place, Suite 330, Boston, MA 02111-1307
634              
635             =head1 COPYRIGHT
636              
637             Copyright (C) 2001 - Olivier Poitrey
638              
639             =head1 PREREQUISITES
640              
641             Apache::Cache needs Apache::SharedMem available from the CPAN.
642              
643             =head1 SEE ALSO
644              
645             L
646              
647             =head1 HISTORY
648              
649             $Log: Cache.pm,v $
650             Revision 1.24 2001/09/27 12:56:27 rs
651             documentation upgrade
652              
653             Revision 1.23 2001/09/24 08:18:20 rs
654             status now return bitmask values
655              
656             Revision 1.22 2001/09/21 16:24:13 rs
657             new method clear
658             new private methods _init_cache and _lock_timeout
659             new constructor parameter 'default_lock_timeout'
660              
661             Revision 1.21 2001/09/21 12:42:53 rs
662             adding pod section KNOW BUGS
663              
664             Revision 1.20 2001/09/20 12:40:18 rs
665             Documentation update: add an EXPORTS section
666              
667             Revision 1.19 2001/09/19 16:22:38 rs
668             fixe a pod bug
669              
670             Revision 1.18 2001/09/19 15:34:17 rs
671             major doc update (METHOD section)
672              
673             Revision 1.17 2001/09/19 13:37:43 rs
674             0.04 => 0.05
675              
676             Revision 1.16 2001/09/19 13:37:09 rs
677             - constructor have now a default value for "cachename", and the 'cachename'
678             parameter is now optional
679              
680             - Documentation upgrade (SINOPSYS simplified)
681              
682             Revision 1.15 2001/08/29 07:45:32 rs
683             add mod_perl specifique documentation
684              
685             Revision 1.14 2001/08/28 13:22:46 rs
686             major bugfix: _check_keys method wasn't clean keys correctly
687              
688             Revision 1.13 2001/08/28 08:42:38 rs
689             set method wasn't unlock on exit !
690              
691             Revision 1.12 2001/08/17 13:26:36 rs
692             some minor pod modifications
693              
694             Revision 1.11 2001/08/17 13:20:45 rs
695             - fix major bug in "get" method: on first timeout, status was set to
696             "delete" method's status (often SUCCESS) instead of EXPIRED
697             - add some sections to pod documentation
698