############################################################################### # Purpose : Generic Cache Factory with various policy factories. # Author : Sam Graham # Created : 23 Jun 2008 # CVS : $Id: CacheFactory.pm,v 1.23 2009-10-07 09:41:06 illusori Exp $ ############################################################################### package Cache::CacheFactory; use warnings; use strict; use Carp; use Cache::Cache; use Cache::CacheFactory::Storage; use Cache::CacheFactory::Expiry; use Cache::CacheFactory::Object; use base qw/Cache::Cache/; $Cache::CacheFactory::VERSION = '1.09'; $Cache::CacheFactory::NO_MAX_SIZE = -1; @Cache::CacheFactory::EXPORT = qw(); @Cache::CacheFactory::EXPORT_OK = qw( best_available_storage_policy best_available_pruning_policy best_available_validity_policy $NO_MAX_SIZE ); %Cache::CacheFactory::EXPORT_TAGS = ( best_available => [ qw( best_available_storage_policy best_available_pruning_policy best_available_validity_policy ) ], ); sub new { my $class = shift; my ( $self, %options ); %options = ref( $_[ 0 ] ) ? %{$_[ 0 ]} : @_; $self = { policies => {}, compat => {}, }; bless $self, ( ref( $class ) || $class ); # # Compat options with Cache::Cache subclasses. $self->{ namespace } = $options{ namespace } || 'Default'; # # Compat with Cache::Cache. $self->{ compat }->{ default_expires_in } = $options{ default_expires_in } if exists $options{ default_expires_in }; # # Cache-wide settings. $self->set_positional_set( $options{ positional_set } ) if exists $options{ positional_set }; # Control first-run eligibility for auto-purging. $self->set_last_auto_purge( $options{ last_auto_purge } ) if defined( $options{ last_auto_purge } ); # Auto-purge intervals. $self->set_auto_purge_interval( $options{ auto_purge_interval } ) if exists $options{ auto_purge_interval }; $self->set_auto_purge_on_set_interval( $options{ auto_purge_on_set_interval } ) if exists $options{ auto_purge_on_set_interval }; $self->set_auto_purge_on_get_interval( $options{ auto_purge_on_get_interval } ) if exists $options{ auto_purge_on_get_interval }; # Auto-purge toggles. $self->set_auto_purge_on_set( $options{ auto_purge_on_set } ) if exists $options{ auto_purge_on_set }; $self->set_auto_purge_on_get( $options{ auto_purge_on_get } ) if exists $options{ auto_purge_on_get }; # Do we quietly (or silently) fail on missing policies? $self->{ nonfatal_missing_policies } = 1 if $options{ nonfatal_missing_policies }; $self->{ nonwarning_missing_policies } = 1 if $options{ nonwarning_missing_policies }; # Do we deeply clone our data when setting it? $self->{ no_deep_clone } = 1 if $options{ no_deep_clone }; # # Grab our policies from the options. $self->set_storage_policies( $options{ storage } ); $self->set_pruning_policies( $options{ pruning } ) if $options{ pruning }; $self->set_validity_policies( $options{ validity } ) if $options{ validity }; if( $#{$self->{ policies }->{ storage }->{ order }} == -1 ) { # OK, we've got no storage policies, we only get this # far if nonfatal_missing_policies has been set. # Either way it's a fatal error for a cache, so we # return an undef. $self->warning( "No valid storage policies supplied" ) unless $self->{ nonwarning_missing_policies }; return( undef ); } return( $self ); } sub new_cache_entry_object { my ( $self ) = @_; return( Cache::CacheFactory::Object->new() ); } sub set { my $self = shift; my ( $param, $object, $key, $data, $mode ); # Aiii, backwards-compat with Cache::Cache->set(). if( $self->{ compat }->{ positional_set } and ( ( $self->{ compat }->{ positional_set } ne 'auto' ) or ( $_[ 0 ] ne 'key' ) ) ) { my ( $next_arg, $expires_in ); $key = shift; $data = shift; $expires_in = shift; $param = {}; if( defined( $next_arg = shift ) ) { # Hackery to support mode from add()/replace(). if( $next_arg eq 'mode' ) { $mode = shift; } else { $param->{ expires_in } = $expires_in; # TODO: warn if expires set and not time policy? } } $mode = shift if defined( $next_arg = shift ) and $next_arg eq 'mode'; } else { $param = ref( $_[ 0 ] ) ? { %{$_[ 0 ]} } : { @_ }; if( exists( $param->{ key } ) ) { $key = $param->{ key }; delete $param->{ key }; } else { warn "No key supplied to ${self}->set(), are you calling it " . "with compat-style positional parameters but haven't set " . "the positional_set option?"; return; } if( exists( $param->{ data } ) ) { $data = $param->{ data }; delete $param->{ data }; } else { warn "No data supplied to ${self}->set(), are you calling it " . "with compat-style positional parameters but haven't set " . "the positional_set option?"; return; } if( exists( $param->{ mode } ) ) { $mode = $param->{ mode }; delete $param->{ mode }; } } if( $mode ) { if( $self->exists( $key ) ) { return if $mode eq 'add'; } else { return if $mode eq 'replace'; } } $param->{ created_at } = time() unless $param->{ created_at }; $param->{ no_deep_clone } = 1 if $self->{ no_deep_clone }; # Create Cache::CacheFactory::Object instance. $object = $self->new_cache_entry_object(); # Initialize it from the param. $object->initialize( $key, $data, $param ); $self->foreach_driver( 'validity', 'set_object_validity', $key, $object, $param ); $self->foreach_driver( 'pruning', 'set_object_pruning', $key, $object, $param ); if( $param->{ no_deep_clone } ) { # Since most Cache::Cache's do their own deep cloning # we try a bit of a hack to try to bypass that. $self->foreach_policy( 'storage', sub { my ( $self, $policy, $storage ) = @_; # Only try this hack on things that subclass behaviour # we understand. if( $storage->isa( 'Cache::BaseCache' ) ) { my ( $backend ); if( $backend = $storage->_get_backend() ) { $object->set_size( undef ); $object->set_key( undef ); $backend->store( $storage->get_namespace(), $key, $object ); return; } } # Ok, we couldn't figure out how to do our dirty hack... $storage->set_object( $key, $object, $param ); } ); } else { $self->foreach_driver( 'storage', 'set_object', $key, $object, $param ); } $self->auto_purge( 'set' ); } sub get { my ( $self, $key ) = @_; my ( $object ); $self->foreach_policy( 'storage', sub { my ( $self, $policy, $storage ) = @_; $object = $storage->get_object( $key ); return unless defined $object; $self->foreach_policy( 'validity', sub { my ( $self, $policy, $validity ) = @_; return if $validity->is_valid( $self, $storage, $object ); undef $object; # TODO: should remove from this storage. optionally? $self->last(); } ); $self->last() if defined $object; } ); $self->auto_purge( 'get' ); return( $object->get_data() ) if defined $object; return( undef ); } sub get_object { my ( $self, $key ) = @_; my ( $object ); $self->foreach_policy( 'storage', sub { my ( $self, $policy, $storage ) = @_; $object = $storage->get_object( $key ); $self->last() if defined $object; } ); return( $object ); } sub set_object { my ( $self, $key, $object ) = @_; # Backwards compat with Cache::Object objects. unless( $object->isa( 'Cache::CacheFactory::Object' ) ) { my ( $param ); $param = {}; $param->{ no_deep_clone } = 1 if $self->{ no_deep_clone }; $object = Cache::CacheFactory::Object->new_from_old( $object, $param ); # TODO: compat with expires_at } $self->foreach_driver( 'storage', 'set_object', $key, $object ); } sub remove { my ( $self, $key ) = @_; $self->foreach_driver( 'storage', 'remove', $key ); } # # CacheFactory extensions. sub exists { my ( $self, $key ) = @_; my ( $exists ); $self->foreach_policy( 'storage', sub { my ( $self, $policy, $storage ) = @_; # If they've implemented an exists method, use it, # otherwise just do it the slow way. if( $storage->can( 'exists' ) ) { $exists = $storage->exists( $key ); } else { $exists = defined( $storage->get_object( $key ) ); } return $self->last() if $exists; } ); return( $exists ? 1 : 0 ); } # # These following provide Cache::Memcached style interface. # get_multi(), incr() and decr() cannot be "properly" implemented # to use underlying functions because our object wrapper prevents # the operations being single calls to the storage policy's # implementation (if they have one), this then directly negates # the purpose of these methods existing in the first place. sub delete { my ( $self, $key ) = @_; $self->remove( $key ); } sub add { my $self = shift; $self->set( @_, mode => 'add' ); } sub replace { my $self = shift; $self->set( @_, mode => 'replace' ); } # #sub get_multi #{ # my ( $self, @keys ) = @_; #} #sub incr #{ # my ( $self, $key, $value ) = @_; #} #sub decr #{ # my ( $self, $key, $value ) = @_; #} sub Clear { my ( $self, @args ) = @_; $self->foreach_driver( 'storage', 'Clear', @args ); } sub clear { my ( $self, @args ) = @_; $self->foreach_driver( 'storage', 'clear', @args ); } sub Purge { my ( $self, @args ) = @_; $self->purge( @args ); } sub purge { my ( $self, @args ) = @_; $self->foreach_driver( 'pruning', 'purge', $self, @args ); } sub auto_purge { my ( $self, $set_or_get ) = @_; return unless $self->{ "auto_purge_on_${set_or_get}" }; return if $self->{ last_auto_purge } >= time() - $self->{ "auto_purge_${set_or_get}_interval" }; # Set timestamp before purge in case we bomb out. # Ideally we should do some manner of locking to prevent # concurrent purges. # Maybe that's the application's business instead. $self->{ last_auto_purge } = time(); $self->purge(); # Update timestamp after purge so we don't spinlock if the purge # takes longer than the interval. $self->{ last_auto_purge } = time(); } sub Size { my ( $self, @args ) = @_; my ( $size ); $size = 0; $self->foreach_policy( 'storage', sub { my ( $self, $policy, $driver ) = @_; # Cache::FastMemoryCache 0.01 dies on Size(), workaround. return if $driver->isa( 'Cache::FastMemoryCache' ); $size += $driver->Size( @args ); } ); return( $size ); } sub size { my ( $self ) = @_; my ( $size ); $size = 0; $self->foreach_policy( 'storage', sub { my ( $self, $policy, $driver ) = @_; $size += $driver->size(); } ); return( $size ); } sub get_namespaces { my ( $self ) = @_; my ( %namespaces ); %namespaces = (); $self->foreach_policy( 'storage', sub { my ( $self, $policy, $driver ) = @_; # Cache::NullCache->get_namespaces() dies, workaround it. return $self->last() if $driver->isa( 'Cache::NullCache' ); foreach my $namespace ( $driver->get_namespaces() ) { $namespaces{ $namespace }++; } } ); return( keys( %namespaces ) ); } sub get_keys { my ( $self ) = @_; my ( %keys ); %keys = (); $self->foreach_policy( 'storage', sub { my ( $self, $policy, $driver ) = @_; foreach my $key ( $driver->get_keys() ) { $keys{ $key }++; } } ); return( keys( %keys ) ); } sub get_identifiers { my ( $self ) = @_; return( $self->get_keys() ); } sub set_positional_set { my ( $self, $positional_set ) = @_; $self->{ compat }->{ positional_set } = $positional_set; } sub get_positional_set { my ( $self ) = @_; return( $self->{ compat }->{ positional_set } ); } sub set_default_expires_in { my ( $self, $default_expires_in ) = @_; my ( $time_pruning, $time_validity ); $time_pruning = $self->get_policy_driver( 'pruning', 'time' ); $time_validity = $self->get_policy_driver( 'validity', 'time' ); unless( $time_pruning or $time_validity ) { carp "Cannot set_default_expires_in() when neither a pruning nor " . "a validity policy of 'time' is set."; return; } $time_pruning->set_default_expires_in( $default_expires_in ) if $time_pruning; $time_validity->set_default_expires_in( $default_expires_in ) if $time_validity; } sub get_default_expires_in { my ( $self ) = @_; my ( $time_pruning, $time_validity ); $time_pruning = $self->get_policy_driver( 'pruning', 'time' ); $time_validity = $self->get_policy_driver( 'validity', 'time' ); unless( $time_pruning or $time_validity ) { carp "Cannot get_default_expires_in() when neither a pruning nor " . "a validity policy of 'time' is set."; return( undef ); } # If they have both set, we go with the validity one since that's # generally the one that has more immediate effect. # If they're setting it via default_expires_in then both should # be the same anyway... return( $time_validity->get_default_expires_in() ) if $time_validity; return( $time_pruning->get_default_expires_in() ) if $time_pruning; } sub limit_size { my ( $self, $size ) = @_; my ( $size_policy ); $size_policy = $self->get_policy_driver( 'pruning', 'size' ); unless( $size_policy ) { carp "Cannot limit_size() when no 'size' pruning policy is set."; return; } $size_policy->limit_size( $self, $size ); } sub set_last_auto_purge { my ( $self, $last_auto_purge ) = @_; $self->{ last_auto_purge } = ( $last_auto_purge eq 'now' ) ? time() : $last_auto_purge; } sub get_last_auto_purge { my ( $self ) = @_; return( $self->{ last_auto_purge } ); } sub set_auto_purge_on_set { my ( $self, $auto_purge_on_set ) = @_; $self->{ auto_purge_on_set } = $auto_purge_on_set; } sub get_auto_purge_on_set { my ( $self ) = @_; return( $self->{ auto_purge_on_set } ); } sub set_auto_purge_on_get { my ( $self, $auto_purge_on_get ) = @_; $self->{ auto_purge_on_get } = $auto_purge_on_get; } sub get_auto_purge_on_get { my ( $self ) = @_; return( $self->{ auto_purge_on_get } ); } sub set_auto_purge_interval { my ( $self, $auto_purge_interval ) = @_; $self->set_auto_purge_on_set_interval( $auto_purge_interval ); $self->set_auto_purge_on_get_interval( $auto_purge_interval ); } sub get_auto_purge_interval { my ( $self ) = @_; return( $self->get_auto_purge_on_get_interval() || $self->get_auto_purge_on_set_interval() ); } sub set_auto_purge_on_set_interval { my ( $self, $auto_purge_interval ) = @_; $self->{ auto_purge_on_set_interval } = $auto_purge_interval; } sub get_auto_purge_on_set_interval { my ( $self ) = @_; return( $self->{ auto_purge_on_set_interval } ); } sub set_auto_purge_on_get_interval { my ( $self, $auto_purge_interval ) = @_; $self->{ auto_purge_on_get_interval } = $auto_purge_interval; } sub get_auto_purge_on_get_interval { my ( $self ) = @_; return( $self->{ auto_purge_on_get_interval } ); } sub set_namespace { my ( $self, $namespace ) = @_; $self->{ namespace } = $namespace; $self->foreach_driver( 'storage', 'set_namespace', $namespace ); } sub get_namespace { my ( $self ) = @_; return( $self->{ namespace } ); } # Coerce the policy arg into a hashref and ordered param list. sub _normalize_policies { my ( $self, $policies ) = @_; return( { order => [ $policies ], param => { $policies => {} }, } ) unless ref( $policies ); return( { order => [ keys( %{$policies} ) ], param => $policies, } ) if ref( $policies ) eq 'HASH'; if( ref( $policies ) eq 'ARRAY' ) { my ( $ret ); $self->error( "Policy arg wasn't even-sized arrayref" ) unless $#{$policies} % 2; $ret = { order => [], param => {} }; for( my $i = 0; $i <= $#{$policies}; $i += 2 ) { push @{$ret->{ order }}, $policies->[ $i ]; $ret->{ param }->{ $policies->[ $i ] } = $policies->[ $i + 1 ]; } return( $ret ); } $self->error( "Unknown policy format: " . ref( $policies ) ); } sub set_policy { my ( $self, $policytype, $policies ) = @_; my ( $factoryclass ); $self->error( "No $policytype policy set" ) unless $policies; $policies = $self->_normalize_policies( $policies ); $self->{ policies }->{ $policytype } = $policies; $factoryclass = 'Cache::CacheFactory::' . ( $policytype eq 'storage' ? 'Storage' : 'Expiry' ); # Handle compat param. $policies->{ param }->{ time }->{ default_expires_in } = $self->{ compat }->{ default_expires_in } if exists $self->{ compat }->{ default_expires_in } and $policies->{ param }->{ time } and not exists $policies->{ param }->{ time }->{ default_expires_in }; $policies->{ drivers } = {}; foreach my $policy ( @{$policies->{ order }} ) { my ( $driver, $param ); $param = $policies->{ param }->{ $policy }; delete $policies->{ param }->{ $policy }; # Ensure we set the namespace if one isn't set explicitly. $param->{ namespace } = $self->{ namespace } if $policytype eq 'storage' and not exists $param->{ namespace }; $driver = $factoryclass->new( $policy, $param ); if( $driver ) { $policies->{ drivers }->{ $policy } = $driver; } else { my ( $driver_module, $error ); $driver_module = $factoryclass->get_registered_class( $policy ); $error = "Unable to load driver for $policytype policy: $policy"; if( $driver_module ) { $error .= "; is $driver_module installed?"; } else { $error .= "; is '$policy' a typo, or a custom policy that " . "hasn't been registered with $factoryclass?"; } if( $self->{ nonfatal_missing_policies } ) { $self->warning( $error ) unless $self->{ nonwarning_missing_policies }; } else { $self->error( $error ); } # Prune it from the policy run order. $policies->{ order } = [ grep { $_ ne $policy } @{$policies->{ order }} ]; } } } sub get_policy_driver { my ( $self, $policytype, $policy ) = @_; return( $self->{ policies }->{ $policytype }->{ drivers }->{ $policy } ); } sub get_policy_drivers { my ( $self, $policytype ) = @_; return( $self->{ policies }->{ $policytype }->{ drivers } ); } # # # Next few methods run a closure against each policy or invoke a # method against each policy's driver. It's a bit inefficient but # saves on duplicating the same ordering and looping code everywhere # and keeps me sane(ish). Oh for a native ordered-hashref. sub last { my ( $self ) = @_; $self->{ _last } = 1; } sub foreach_policy { my ( $self, $policytype, $closure ) = @_; my ( $policies ); $policies = $self->{ policies }->{ $policytype }; foreach my $policy ( @{$policies->{ order }} ) { $closure->( $self, $policy, $policies->{ drivers }->{ $policy } ); next unless $self->{ _last }; delete $self->{ _last }; last; } } sub foreach_driver { my ( $self, $policytype, $method, @args ) = @_; my ( $policies ); $policies = $self->{ policies }->{ $policytype }; foreach my $policy ( @{$policies->{ order }} ) { $policies->{ drivers }->{ $policy }->$method( @args ); next unless $self->{ _last }; delete $self->{ _last }; last; } } sub set_storage_policies { my ( $self, $policies ) = @_; $self->set_policy( 'storage', $policies ); } sub set_pruning_policies { my ( $self, $policies ) = @_; $self->set_policy( 'pruning', $policies ); } sub set_validity_policies { my ( $self, $policies ) = @_; $self->set_policy( 'validity', $policies ); } sub _error_message { my $self = shift; my ( $error ); $error = join( '', @_ ); return( "Cache error: $error" ); } sub error { my $self = shift; die( $self->_error_message( @_ ) ); } sub warning { my $self = shift; warn( $self->_error_message( @_ ) ); } # # # Non-OO functions. # sub _best_available_policy { my ( $policytype, @policies ) = @_; my ( $factoryclass ); $factoryclass = 'Cache::CacheFactory::' . ( $policytype eq 'storage' ? 'Storage' : 'Expiry' ); while( my $policy = shift( @policies ) ) { return( $policy ) if $factoryclass->get_registered_class( $policy ); } return( undef ); } sub best_available_storage_policy { return( _best_available_policy( 'storage', @_ ) ); } sub best_available_pruning_policy { return( _best_available_policy( 'pruning', @_ ) ); } sub best_available_validity_policy { return( _best_available_policy( 'validity', @_ ) ); } 1; __END__ =pod =head1 NAME Cache::CacheFactory - Factory class for Cache::Cache and other modules. =head1 SYNOPSIS use Cache::CacheFactory; my $cache = Cache::CacheFactory->new( storage => 'file' ); $cache->set( 'customer', 'Fred' ); ... Later ... print $cache->get( 'customer' ); ... prints "Fred" =head1 DESCRIPTION Cache::CacheFactory is a drop-in replacement for the L subclasses allowing you to access a variety of caching policies from a single place, mixing and matching as you choose rather than having to search for the cache module that provides the exact combination you want. In a nutshell you specify a policy for storage, for pruning and for validity checks and CacheFactory hooks you up with the right modules to provide that behaviour while providing you with the same API you're used to from Cache::Cache - the only thing you need to change is your call to the constructor. More advanced use allows you to set multiple policies for pruning and validity checks, and even for storage although that's currently of limited use. =head1 METHODS =over =item $cache = Cache::CacheFactory->new( %options ) =item $cache = Cache::CacheFactory->new( $options ) Construct a new cache object with the specified options supplied as either a hash or a hashref. Errors during construction are usually fatal and reported via C, some have C options to override this behaviour in which case an C value will be returned from C. See L below for more details on possible options. =item $cache->set( key => $key, data => $data, [ expires_in => $expires_in, %additional_args ] ) =item $cache->add( key => $key, data => $data, [ expires_in => $expires_in, %additional_args ] ) =item $cache->replace( key => $key, data => $data, [ expires_in => $expires_in, %additional_args ] ) =item $cache->set( $key, $data, [ $expires_in ] ) (only in compat-mode) =item $cache->add( $key, $data, [ $expires_in ] ) (only in compat-mode) =item $cache->replace( $key, $data, [ $expires_in ] ) (only in compat-mode) Associates C<$data> with C<$key> in the cache. C<< $cache->add() >> is a special form of C<< $cache->set() >> that will set the key if-and-only-if it doesn't already exist in the cache. C<< $cache->replace() >> is a special form of C<< $cache->set() >> that will set the key if-and-only-if it does already exist in the cache. B: the existence test and set in C<< $cache->add() >> and C<< $cache->replace() >> is B an atomic operation, if you have a shared cache you will need to implement your own locking mechanism if you need to rely on this behaviour. A deep copy of C<$data> will automatically be taken if it is a reference, you can turn this behaviour off with the cache option C detailed in L below. C<$expires_in> indicates the time in seconds until this data should be erased, or the constant C<$EXPIRES_NOW>, or the constant C<$EXPIRES_NEVER>. Defaults to C<$EXPIRES_NEVER>. This variable can also be in the extended format of "[number] [unit]", e.g., "10 minutes". The valid units are s, second, seconds, sec, m, minute, minutes, min, h, hour, hours, d, day, days, w, week, weeks, M, month, months, y, year, and years. Additionally, C<$EXPIRES_NOW> can be represented as "now" and C<$EXPIRES_NEVER> can be represented as "never". C<$expires_in> is silently ignored (future versions may warn) if the cache didn't choose a 'time' pruning or validity policy at setup. Any additional args will be passed on to the policies chosen at setup time (and documented by those policy modules.) B The positional args version of this method is only available if the compat flag C was supplied as an option when the cache was created. If C is a true value but not set to C<'auto'> then the hash format is disabled and C acts as if it is always given positional args - this will do unwanted things if you pass it hash args. If C was given C<'auto'> as a value then C will attempt to auto-detect when you're supplying positional args and when you're supplying hash args, it does this by the rather-breakable means of asking if the first arg is called 'key', if so then it assumes you're passing a hash, otherwise it'll fall back to using positional args. Examples: $cache->set( key => 'customer', data => 'Fred', expires_in => '10 minutes', ); $created_at = time(); $template = build_my_template( '/path/to/webpages/index.html' ); $cache->set( key => 'index', data => $template, created_at => $time, dependencies => [ '/path/to/webpages/index.html', ], ); =item $data = $cache->get( $key ); Gets the data associated with C<$key> from the first storage policy that contains a fresh cached copy. =item $cache->remove( $key ); Removes the data associated with C<$key> from each of the storage policies in this cache. =item $cache->delete( $key ); This is a convenience alias for C<< $cache->remove( $key ) >>. =item $boolean = $cache->exists( $key ); Returns true if data associated with C<$key> exists in the cache and false if there is no data associated with that key. This method makes no assumption about the form of the data stored: if you store a value of C you will still get a true return from C<< $cache->exists() >>. =item $object = $cache->get_object( $key ); Returns the L used to store the underlying data associated with C<$key>. This behaves much the same as the L returned by C<< Cache::Cache->get_object() >>. =item $cache->set_object( $key, $object ); Associates C<$key> with L C<$object>. If you supply a L in C<$object> instead, L will create a new L instance as a copy before storing the copy. =item @keys = $cache->get_keys(); Returns a list of all keys in this instance's namespace across all storage policies. =item @keys = $cache->get_identifiers(); B. Behaves identically to C<< $cache->get_keys() >>, use that instead. Provided only for backwards compatibility. =item $cache->set_namespace( $namespace ); Sets the cache's namespace as per the C option. This does B move any existing cache contents over to the new namespace, it simply points the cache object at the new namespace. =item $namespace = $cache->get_namespace(); Returns the current namespace as set either by C<< $cache->set_namespace() >> or the C option. =item $cache->Clear(); Clears all caches using each of the storage policies. This does not just clear caches with the exact same policies: it calls C on each policy in turn. =item $cache->clear(); Removes all cached data for this instance's namespace from each of the storage policies. =item $cache->Purge(); B C now does the same thing as C since it isn't clear quite what it should do with multiple caches with different pruning and storage policies. Its use is strongly deprecated. =item $cache->purge(); Applies the pruning policy to all data in this namespace. =item $size = $cache->Size(); Returns the total size of all objects in all caches with any of the storage policies of this cache. =item $size = $cache->size(); Returns the total size of all objects in this namespace in any of the storage policies of this cache. =item @namespaces = $cache->get_namespaces(); Returns a list of all namespaces in any of the storage policies of this cache. =item $cache->set_positional_set( 0 | 1 | 'auto' ); =item $positional_set = $cache->get_positional_set(); These two methods allow you to alter the behaviour of the C compatibility option. See the documentation on C<< $cache->set() >> or L for more information on this setting. =item $cache->set_default_expires_in( $expires_in ); =item $expires_in = $cache->get_default_expires_in(); These two methods allow you to alter the C compatibility option. See the documentation on C<< $cache->set() >> or L for more information on this setting. Note that when you have both a pruning and validity policy of 'time' the C of the validity policy is returned in preference to the pruning policy. Both will most likely be identical unless you're intentionally setting them differently via the new API, in which case: use the new API to get the value you want. =item $cache->set_last_auto_purge( 0 | 'now' | $seconds_since_epoch ); =item $seconds_since_epoch = $cache->get_last_auto_purge(); Sets or gets the timestamp of the last auto-purge. See the documention for C in L for further details. =item $cache->set_auto_purge_on_set( 0 | 1 ); =item $cache->set_auto_purge_on_get( 0 | 1 ); =item $boolean = $cache->get_auto_purge_on_set(); =item $boolean = $cache->get_auto_purge_on_get(); Turns auto-purging on/off for C<< $cache->set() >> or C<< $cache->get() >>, or returns the current state of auto-purging for each. See the documention for C and C in L for further details. =item $cache->set_auto_purge_interval( $seconds ); =item $cache->set_auto_purge_on_set_interval( $seconds ); =item $cache->set_auto_purge_on_get_interval( $seconds ); =item $seconds = $cache->get_auto_purge_interval(); =item $seconds = $cache->get_auto_purge_on_set_interval(); =item $seconds = $cache->get_auto_purge_on_get_interval(); Set or get the appropriate auto-purge interval as per the C, C or C options. Look at L for further details. =item $cache->limit_size( $size ); Only available if a pruning policy of 'size' has been set, this method will allow you to perform a one-off prune of the storage policies to C<$size> size or below. This behaves like the C method of L. =back =head1 NON-OBJECT-ORIENTATED FUNCTIONS =over =item $policy = best_available_storage_policy( @policies ); =item $policy = best_available_pruning_policy( @policies ); =item $policy = best_available_validity_policy( @policies ); These helper functions take a list of policies in the order you prefer them and returns the first one that is installed on the running system. This is useful if you don't know which packages are installed on the target system and have a list of alternatives you want to check against. For example: use Cache::CacheFactory qw/:best_available/; my $cache = Cache::CacheFactory->new( storage => best_available_storage_policy( qw/sharedmemory memory file/ ), ); This would produce either: a shared-memory cache if L was available, failing that it would try a memory cache if L was available, and finally it would try L if the other two failed. By default these functions are not exported, you will need to supply C<:best_available> on the use line to import them. =back =head1 CONSTANTS You can export the following constants: =over =item $NO_MAX_SIZE You can export this with C<< use Cache::CacheFactory qw/$NO_MAX_SIZE/; >> and supply it to the C option of a 'size' pruning policy. This value of C<$NO_MAX_SIZE> is compatible with that defined by L, so you can use either source. See L for further details. =back =head1 OPTIONS The following options may be passed to the C constructor: =over =item storage => $storage_policy =item storage => { $storage_policy1 => $policy1_options, $storage_policy2 => $policy2_options, ... } =item storage => [ $storage_policy1 => $policy1_options, $storage_policy2 => $policy2_options, ... ] =item pruning => $pruning_policy =item pruning => { $pruning_policy1 => $policy1_options, $pruning_policy2 => $policy2_options, ... } =item pruning => [ $pruning_policy1 => $policy1_options, $pruning_policy2 => $policy2_options, ... ] =item validity => $validity_policy =item validity => { $validity_policy1 => $policy1_options, $validity_policy2 => $policy2_options, ... } =item validity => [ $validity_policy1 => $policy1_options, $validity_policy2 => $policy2_options, ... ] Chooses a storage, pruning, or validity policy (or policies) possibly passing in a hashref of options to each policy. Passing a hashref of policies is probably a bad idea since you have no control over the order in which policies are processed, if you supply them as an arrayref then they will be run in order. See L below for more information on policies. =item namespace => $namespace The namespace associated with this cache. Defaults to "Default" if not explicitly set. All keys are unique within a given namespace, you B risk key-clashes with other applications if you use a persistent or shared storage policy and do not set a namespace to something unique to do with your application. =item auto_purge_on_set => 0 | 1 =item auto_purge_on_get => 0 | 1 If set to a true value turns auto-purging on, if set to a false value turns auto-purging off. C determines if calling C<< $cache->set() >> can trigger an auto-purge, and C does the same for C<< $cache->get() >>. Since a purge can be an expensive operation you will usually want to enable only C if you're in the usual I environment, although see the example below in C for an alternative strategy. =item auto_purge_interval => $interval =item auto_purge_on_set_interval => $interval =item auto_purge_on_get_interval => $interval Sets the interval between auto-purges to C<$interval> seconds. When checking whether an auto-purge should occur, the last purge time is compared to the current time, if it is more than C<$interval> seconds in the past, a new C will be triggered. By use of C and C you can tune the interval independently for each. This may be useful in some situations: my $cache = Cache::CacheFactory->new( storage => 'memory', pruning => { 'time' => { default_prune_after => '1 m' } }, auto_purge_on_set => 1, auto_purge_on_get => 1, auto_purge_on_set_interval => 5, auto_purge_on_get_interval => 30, ); This will set a cache that prunes items older than 1 minute and will auto-purge after a C<< $cache->set() >> if there hasn't been an auto-purge in the past 5 seconds. It will also auto-purge after a C<< $cache->get() >> if there hasn't been an auto-purge in the past 30 seconds. This means that the expense of the auto-purge will usually be added to the (relatively) expensive C most of the time, and only delay the usually cheap C if there hasn't been a recent C to trigger the auto-purge. C sets both C and C to the same value. Note that for the auto-purge intervals to be used you will need to turn on either C or C. =item default_expires_in => $expiry_time This option is for backwards compatibility with L. If set it is passed on to the C<'time'> pruning and/or validity policy if you have chosen either of them. B if you do NOT have an pruning or validity policy of 'time', this option is silently ignored. This may raise a warning in future versions. You can also manipulate this option via C<< $cache->set_default_expires_in() >> and C<< $cache->get_default_expires_in() >> after cache creation. =item positional_set => 0 | 1 | 'auto' This option is for backwards compatibility with L. If set to a true value that isn't 'auto' it indicates that C<< $cache->set() >> should behave exactly as that in L, accepting only positional parameters. If you set this option you will be unable to supply parameters to policies other than C to the C<'time'> pruning or validity policy. If set to a value of 'auto' L will attempt to auto-detect whether you're supplying positional or named parameters to C<< $cache->set() >>. This mechanism is not very robust: it simply looks to see if the first parameter is the value 'key', if so it assumes you're supplying named parameters. The default behaviour, or if you set C to a false value, is to assume that named parameters are being supplied. Generally speaking, if you know for sure that all your code is using positional parameters you should set it to true, if you know all your code is using the new named parameters syntax you should set it false (or leave it undefined), and if you're uncertain or migrating from one to the other, you should set it to 'auto' and be careful that you always supply the C param first. You can also manipulate this option via C<< $cache->set_positional_set() >> and C<< $cache->get_positional_set() >> after cache creation. =item last_auto_purge => 0 | 'now' | $seconds_since_epoch This option grants you initial control of when the cache should consider the most recent auto-purge to have occurred, by default this is set to 0 meaning no auto-purge has occurred and one should run as soon as it is triggered. If you set it to 'now' then the cache will "pretend" that an auto-purge occurred at the same time as the cache creation and won't run another until the auto-purge interval has expired (C, C, or C as appropriate). You can also supply a number of seconds since the epoch, as returned by C, if you want more precise control - such as if your application stores the last auto-purge time in some external manner. =item nonfatal_missing_policies => 0 | 1 =item nonwarning_missing_policies => 0 | 1 Setting C to a true value will suppress the default C behaviour when a requested policy is missing and will instead generate a C. If you also set C to a true value, this C will also be surpressed. =item no_deep_clone => 0 | 1 Setting C to a true value will prevent the default behaviour of taking a deep clone of the data provided to C<< $cache->set() >>. This can be a performance gain if you don't need to be paranoid about the cache sharing references with whatever handed them in, or if you want to handle the cloning yourself within your application. Regretfully C on the cache can only act in an advisory capacity to storage policies, they may choose to disregard the flag and many of the L modules will do just this. (Not unreasonably: they predate L considerably.) L tries hard to convince them to avoid taking clones but may or may not succeed depending on precisely what you're attempting, you'll have to suck it and see I'm afraid. Using this option with a storage policy of 'memory' will provide you with similar behaviour to L, with the exception that, unavoidably, a deep clone is always created on C<< $cache->get() >>. If this is undesirable, install L and use a storage policy of 'fastmemory' in conjunction with setting C. =back =head1 POLICIES There are three types of policy you can set: storage, pruning and validity. L determines what mechanism is used to store the data. L determines what mechanism is used to reap or prune the cache. L determines what mechanism is used to determine if a cache entry is still up-to-date. =head2 Storage Policies Some common storage policies: =over =item file Implemented using L, this provides on-disk caching. =item memory Implemented using L, this provides per-process in-memory caching. =item sharedmemory Implemented using L, this provides in-memory caching with the cache shared between processes. =item fastmemory Implemented using L, this provides in-memory caching like the 'memory' policy but with all the deep-copies of data stripped out, best used in conjunction with the C option set on the cache. =item null Implemented using L, this cache is used to provide a fake cache that never stores anything. =back =head2 Pruning and Validity Policies All I and I policies are interchangable, the difference between the two is when the policy is applied: A pruning policy is applied when you C or periodically if C or C is set, it removes all entries that fail the policy from the cache. Note that an item can be I to be pruned but still be in the cache and fetched successfully from the cache - it won't be removed until C is called either manually or automatically. A validity policy is applied when an entry is retreived to ensure that it's still valid (or fresh or up-to-date if you prefer). If the entry isn't still valid then it's ignored as if it was never in the cache. Unlike pruning, validity always applies - you will never be able to fetch an item from the cache if it is invalid according to the policies you have chosen. A handy shorthand is that pruning determines how long we keep the data lying around in case we need it again, validity determines whether we trust that it's still accurate. =over =item time This provides pruning and validity policies similar to those built into L using the C param. It allows you to check for entries that are over a certain age. =item size This policy prunes the cache to attempt to keep it under a supplied size, much like L and the other C modules. This policy probably doesn't make much sense as a validity policy, although you can use it. =item lastmodified This policy compares the created date of the cache entry to the last-modified date of a list of file dependencies. If the create date is older than any of the file last-modified dates the entry is pruned or regarded as invalid. This is useful if you have data compiled or parsed from source data-files that may change, such as HTML templates or XML files. =item forever This debugging policy never regards items as invalid or prunable, it's implemented as the default behaviour in L. =back =head1 WRITING NEW POLICIES It's possible to write custom policy modules of your own, all policies are constructed using the L or L class factories. C provides the storage policies and C provides both the pruning and validity policies. New storage policies should conform to the L API, in particular they need to implement C and C. New expiry policies (both pruning and validity) should follow the API defined by L, ideally by subclassing it. Once you've written your new policy module you'll need to register it with L as documented in L, probably by placing one of the the following lines (depending on type) somewhere in your module: Cache::CacheFactory::Storage->register_factory_type( mypolicyname => 'MyModules::MyPolicyName' ); Cache::CacheFactory::Expiry->register_factory_type( mypolicyname => 'MyModules::MyPolicyName' ); Then you just need to make sure that your application has a use MyModules::MyPolicyName; before you ask L to create a cache with 'mypolicyname' as a policy. =head1 INTERNAL METHODS The following methods are mostly for internal use, but may be useful to redefine if you're subclassing L for some reason. =over =item $object = $cache->new_cache_entry_object(); Returns a new and uninitialized object to use for a cache entry, by default this object will be a L, if for some reason you want to overrule that decision you can return your own object. =item $cache->set_policy( $policytype, $policies ); Used as part of the C constructor, this sets the policy type C<$policytype> to use the policies defined in C<$policies>, this may do strange things if you do it to an already used cache instance. =item $cache->set_storage_policies( $policies ); =item $cache->set_pruning_policies( $policies ); =item $cache->set_validity_policies( $policies ); Convenience wrappers around C. =item $cache->get_policy_driver( $policytype, $policy ); Gets the driver object instance for the matching C<$policytype> and C<$policy>, useful if it has non-standard extensions to the API that you can't access through L. =item $cache->get_policy_drivers( $policytype ); Returns a hashref of policies to driver object instances for policy type C<$policytype>, you should probably use C instead to get a specific driver though. =item $cache->foreach_policy( $policytype, $closure ); Runs the closure/coderef C<$closure> over each policy of type C<$policytype> supplying args: C instance, policy name, and policy driver. The closure is run over each policy in order, or until the closure calls the C method on the C instance. use Data::Dumper; use Cache::CacheFactory; $cache = Cache::CacheFactory->new( ... ); $cache->foreach_policy( 'storage', sub { my ( $cache, $policy, $driver ) = @_; print "Storage policy '$policy' has driver: ", Data::Dumper::Dumper( $driver ), "\n"; return $cache->last() if $policy eq 'file'; } ); This will print the policy name and driver object for each storage policy in turn until it encounters a C<'file'> policy. =item $cache->foreach_driver( $policytype, $method, @args ); Much like C above, this method iterates over each policy, this time invoking method C<$method> on the driver with the arguments specified in C<@args>. $cache->foreach_driver( 'storage', 'do_something', 'with', 'args' ); will call: $driver->do_something( 'with', 'args' ); on each storage driver in turn. The return value of the method called is discarded, if it's important to you then you should use C and call the method on the driver arg provided, collating the results however you wish. =item $cache->last(); Indicates that C or C should exit at the end of the current iteration. C does B exit your closure for you, if you want it to behave like perl's C construct you will want to do C<< return $cache->last() >>. =item $cache->auto_purge( 'set' | 'get' ); Attempts an auto-purge according to the C and C settings and the C<< $cache->get_last_auto_purge() >> value. =item $cache->error( $error_message ); Raise a fatal error with message given by C<$error_message>. =item $cache->warning( $warning_message ); Raise a warning with message given by C<$warning_message>. =back =head1 KNOWN ISSUES AND BUGS =over =item Pruning and validity policies are per-cache rather than per-storage Pruning and validity policies are set on a per-cache basis rather than on a per-storage-policy basis, this makes multiple storage policies largely pointless for most purposes where you'd find it useful. If you wanted the cache to transparently use a small fast memory cache first and fall back to a larger slower file cache as backup: you can't do it, because the size pruning policy would be the same for both storage policies. About the only current use of multiple storage policies is to have a memory cache and a file cache so that processes that haven't pulled a copy into their memory cache yet will retreive it from the copy another process has placed in the file cache. This might be slightly more useful than a shared memory cache since the on-file cache will persist even if there's no running processes unlike the shared memory cache. Per-storage pruning and validity settings may make it into a future version if they prove useful and won't over-complicate matters - for now it's best to create a wrapper module that internally creates the caches seperately but presents the Cache::Cache API externally. =item Add/replace aren't atomic The C<< $cache->add/replace() >> methods aren't atomic, this mostly defeats their purpose in a shared-cache situation. This could be considered a bug. =item Aren't there a million Cache::Cache replacements already? At the time I started writing L I'd been trying to find a caching solution that had the combination of features I needed, I had no luck in finding one. Since then I've found a couple of other similar modules, and more have been written and released, you may or may not find them suiting your needs more closely, so I suggest taking a good look: L - this module appears to have much the same motivation and strategy as L in terms of storage policies, however, from what I can gather, it doesn't appear to split validity/pruning policies into seperate and/or combinable modules. L - not sure how I missed this one when I was researching, it's a mature module that gives you flexibile validity/pruning policies but doesn't have such a wide range of storage policies available. Please note that these descriptions are from my own imperfect understanding of the modules concerned, by no means take them as an authorative description of their functionality. Please feel free to contact me if I've included any inaccuracies. :) =back =head1 SEE ALSO L, L, L, L, L, L, L =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc Cache::CacheFactory You can also look for information at: =over 4 =item * RT: CPAN's request tracker L =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * Search CPAN L =back =head1 AUTHORS Original author: Sam Graham Last author: $Author: illusori $ =head1 ACKNOWLEDGEMENTS DeWitt Clinton for the original L, most of the hard work is done by this module and its subclasses. Chris Winters for L, saving me the trouble of finding out what policy modules are or aren't available. John Millaway for L, which inspired the C option. =head1 COPYRIGHT & LICENSE Copyright 2008-2009 Sam Graham, all rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut