Hi Jason Great work - I could really use this!
One thing I like about HTML::Mason's caching is that it can effectively get one process to cache a new version of an expired page and set the expiration time on the old one a bit more into the future so that other processes still get the old cached version. This is useful if your cache expires, and it takes quite a lot of time to build a fresh copy. For example, I used to work on a reasonably busy site where the web servers and the database would get absolutely thrashed until the expired cache was rebuilt. Unfortunately, this led to a race condition, where every request to the expired page would try to build the cache. Mason's approach can fix that. I added a wishlist request to get Mason's solution added to Cache::Cache itself (http://rt.cpan.org/Public/Bug/Display.html?id=19586), but maybe your module could do what Mason does in the meantime. (Unfortunately, it only works for Cache::Cache - it would be great if the same approach could be done with Memcached, but I can't see a how - maybe someone else can). Dan > -----Original Message----- > From: Jason A. Crome [mailto:[EMAIL PROTECTED] > Sent: Saturday, 8 July 2006 08:40 > To: [email protected] > Subject: [cgiapp] RFC v2: CGI::Application::Plugin::Cache > > > Howdy, > > Here's the second (and hopefully final!) RFC for > CGI::Application::Plugin::Cache. I received a LOT of good feedback, > both on and off list, and a whole bunch more at YAPC. I wish > I could > say a lot of ideas were mine - I have to give Cees, hide, > and Perrin > a lot of props for helping me out with it. > > The one thing I am not sure what to do with it remove(). Should it > behave like Perl's delete and return the value of the key it's > deleting, or should it return the number of items that it's deleting? > > Looking forward to your feedback, > Jason > > NAME > CGI::Application::Plugin::Cache - Generic caching framework for > CGI::Application. > > VERSION > Version 0.01 > > SYNOPSIS > use CGI::Application; > use CGI::Application::Plugin::Cache; > > # > # Configure the cache. Do this in setup() or cgiapp_init(). > # > > # Create an unnamed cache with a Cache::FastMmap backend > $self->cache->config({ > driver => 'CacheFastMmap', > share_file => '/home/webapp/cache/mmapcache', > cache_size => '1m', > }); > > # The cache in action! > $self->cache->set( $key, $value ); > my $value = $self->cache->get( $key ); > my $value = $self->cache->remove( $key ); > > # Remove any expired objects > my $count = $self->cache->purge_expired; > > # Clear the entire cache > $self->cache->clear; > > DESCRIPTION > "CGI::Application::Plugin::Cache" provides a generic caching > interface > for CGI::Application-derived web applications. A single method, > "cache", > gives the developer complete control over defining and using a > cache in > their application. > > "CGI::Application::Plugin::Cache" is dependent on one or more > drivers to > provide functionality from various caching backends. > "CGI::Application::Plugin::Cache" comes with drivers for the > Cache::Cache suite of caching modules, Cache::FastMmap, > Cache::Memcached, and Tie::Cache::LRU. For information about > writing > your own drivers, see CGI::Application::Plugin::Cache::Driver. > > Named Caches > You may wish to provide one or more data caches to your > application, and > you may wish to store those caches in different types of > storage > medium > (disk, memory, shared memory, network cache, etc.). > "CGI::Application::Plugin::Cache" makes it easy to provide this > functionality in your applications through the use of > named caches: > > $self->cache( 'memory' )->config({ > driver => 'CacheFastMmap', > share_file => '/home/webapp/cache/mmapcache', > cache_size => '1m', > }); > > $self->cache( 'disk' )->config({ > driver => 'CacheFileCache', > namespace => 'Default', > cache_root => '/home/webapp/cache/filecache', > }); > > $self->cache( 'memory' )->set( "yourkey", "yourvalue" ); > my $value = $self->cache( 'disk' )->get( "mykey" ); > > Named caches, while handy, add a great deal of complexity to your > application. As such, developers need to understand how > "CGI::Application::Plugin::Cache" deals with named caches. > > The best and most clear way of dealing with named caches is to > provide > "cache()" with a list of caches to operate on. Caches are used > in the > order provided. For example: > > $self->cache( 'memory', 'disk' )->set( "mykey", > "thisvalue" ); > > adds the key "mykey" to the memory cache first and sets it to > "thisvalue". "set()" then adds the key/value combination to the > disk > cache. > > Likewise: > > my ($value1, $value2) = $self->cache( 'memory', 'network')- > >get( 'thiskey', 'thatkey' ); > > looks first in the memory cache for the key "thiskey". If it's > found, > $value1 is populated with the value in the memory cache, > regardless of > whether or not "thiskey" exists in the network cache. If > the key > is not > found in memory, the network cache is searched. If neither cache > contains "thiskey", $value1 is set to "undef". $value2 is > populated in > this same manner. > > If this is too much typing for you, you can pass an additional > argument > to "config()" called "default": > > $self->cache->config({ default => ['memory', 'network'] }); > > You can now write the above "get()" operation as such: > > my ($value1, $value2) = $self->cache->get( 'thiskey', > 'thatkey' ); > > and get the result in the same manner. Note that you can define > many > caches in your application, but you do not have to make all of > them part > of the default list. > > More specific examples are provided throughout this > documentation. > > EXPORTED METHODS > cache() > Everything in "CGI::Application::Plugin::Cache" is controlled > through > this method. "cache()" returns a > "CGI::Application::Plugin::Cache" > object unless it was called as a class method, in which case it > returns > the name of the class. > > When using the cache, always invoke this method in one of the > following > two ways: > > $self->cache->method_to_invoke( @args ); > > - or - > > __PACKAGE__->cache->method_to_invoke (@args ); > > When using multiple caches, you may provide a cache name (or > list of > cache names) to be used by "cache()": > > $self->cache( 'memory', 'disk' )->set( "yourkey", > "yourvalue" ); > my $value = $self->cache( 'network', 'disk' )->get( > "mykey" ); > > The desired operation will be performed on each of the > specified > caches. > If a cache is not expressly stated, > "CGI::Application::Plugin::Cache" > uses the methodology described in "Named Caches" to > determine which > caches to operate on. > > "CGI::Application::Plugin::Cache" uses lazy-loading to > prevent the > unnecessary use of caching resources by your application. This > means > that if you configure a cache but never end up using it, the > caching > backend is never instantiated. The potential savings offered by > lazy > loading are huge, as many caching backends are somewhat on the > heavy > side. > > METHODS > config() > Configures the caching module. It can be called as an object > method or a > class method. It should be called in either your application's > "cgiapp_init()" or "setup()" events. > > "config()" can take one or more hash arguments that > define which > caches > are being defined, and what drivers are implementing > that cache. > It also > takes a "default" argument which specifies which caches to use > (and in > what order) when a cache is not explicitly stated. > > Let's look at some sample configurations: > > # The simplest config call possible! > $self->cache->config; > > This example implements a simple Cache::FileCache-based > cache. A > simple > cache is created in your system's temporary directory > (determined with > File::Temp) using reasonable defaults. While not the most > advanced or > efficient caching option, it will work for most circumstances. > > # Another simple configuration when implementing a single > caching mechanism > # in your application. > $self->cache->config({ > driver => 'CacheFastMmap', > share_file => '/home/webapp/cache/mmapcache', > cache_size => '1m', > }); > > This creates a single cache using Cache::FastMmap. This also > illustrates > how to pass additional arguments to your underlying driver > mechanism. > > # Using a named configurations to distinguish these > from the > above > # configuration. > $self->cache( 'disk' )->config({ > driver => 'CacheFileCache', > namespace => 'Default', > cache_root => '/home/webapp/cache/filecache', > }); > > $self->cache( 'memory' )->config({ > driver => 'CacheFastMmap', > share_file => '/home/webapp/cache/mmapcache', > cache_size => '1m', > }); > > $self->cache( 'network' )->config({ > driver => 'CacheMemcached', > servers => [ '192.168.0.10:11211', > '192.168.0.20:11211' ], > debug => 0, > namespace => 'myapp', > }); > > This example shows us how to create multiple, named caches > within our > web application. We can access each one of these caches > individually > within our application by specifying the name of the cache we'd > like to > use as an argument to "cache()". > > # Specify the default cache for cache operations > $self->cache->config({ default => ['memory'] }); > > When using multiple caches, this specifies which cache (or > caches) to > use as the default for caching operations. > > # Or, as a single method call. . . > $self->cache->config({ > { > name => 'memory', > driver => 'CacheFastMmap', > share_file => '/home/webapp/cache/mmapcache', > cache_size => '1m', > }, > { > name => 'disk', > driver => 'CacheFileCache', > namespace => 'Default', > cache_root => '/home/webapp/cache/filecache', > }], > default => [ 'memory' ], > }); > > This is a convenient way to do all of the above in one call to > "config()". > > Driver Notes > When configuring a driver, you must specify two parameters: > "name" and > "driver". "name" is the name of the cache you are configuring a > driver > for, and can be provided as an argument to "cache()". It can > also be > specified as a key to the driver's configuration hash. See the > examples > above for more details. > > "driver" is the name of the driver module being used to > power your > cache. For sake of simplicity, you can omit > "CGI::Application::Plugin::Cache::Driver::" when specifying > which driver > to use. > > If the underlying caching module requires additional > configuration > parameters, you may pass these as additional key/value > combinations in > the configuration hash. See the examples above for more details. > > set() > $self->cache()->set( $key, $value, [ $expiry, { > %options } ]); > $self->cache( 'memory' )->set( $key, $value, [ $expiry, { % > options } ]); > $self->cache( 'disk', 'memory' )->set( $key, $value, > [ $expiry, { %options } ]); > > Sets the specified key to the value provided in the specified > cache. If > no cache was specified, the key/value combination is added to the > default cache. > > Note: not all caching backends support parameter-level > expiration. In > particular, Cache::FastMmap does not provide the ability > to expire > different parameters at different times. This > functionality may, > at some > point in the future, be added to drivers for backends that do not > provide that functionality. > > Any options specified in %options are passed directly to the > underlying > caching mechanism. > > get() > my ($value) = $self->cache->get( $key ); > my ($value1, $value2, $value3) = $self->cache( 'network' )- > >get([ "key1", "key2", "key3" ]); > my ($value1, $value2) = $self->cache( 'network', 'disk' )- > >get([ "key1", "key2" ]); > > Returns a list of values for the key or keys provided as > arguments from > the specified cache. If a cache is not specified, values are > retrieved > from the default cache. If multiple caches are provided (or > there are > multiple default caches), caches are searched in the order > provided. The > value returned comes from the first cache in the list that > contains the > desired key. > > If a key is not found, "get()" returns "undef". > > remove() > my ($value) = $self->cache->remove( $key ); > my ($value1, $value2, $value3) = $self->cache( 'disk' )- > >remove([ "key1", "key2", "key3" ]); > my ($value1, $value2) = $self->cache( 'disk', 'memory' )- > >remove([ "key1", "key2" ]); > > Removes the specified key(s) (and the associated values) from the > specified cache. If a cache was not specified, the key/value > combination > is removed from the default cache. "remove()" returns the list > of values > that were deleted. > > purge_expired() > my $count = $self->cache->purge_expired; > my $count = $self->cache( 'memory' )->purge_expired; > my $count = $self->cache( 'disk', 'memory' )->purge_expired; > > Removes all expired objects from the specified cache. If no > cache is > specified, all expired items are purged from the default cache. > Returns > the number of items that were expired and removed. > > clear() > $self->cache->clear; > $self->cache( 'network' )->clear; > $self->cache( 'memory', 'network' )->clear; > > Removes all objects from the specified cache. If no cache was > specified, > all objects are removed from the default cache. > > object() > $self->cache->object->some_cache_object_method( $arg1, > $arg2, ...); > $self->cache( 'disk' )->object->some_cache_object_method > ( $arg1, $arg2, ...); > > Provides direct access to the underlying caching object. This > method > *should* only be rarely used - i.e., for circumstances where > there is > some functionality provided by the underlying caching mechanism > that is > not supported by the standard driver interface. Using "object()" > bypasses many of the benefits provided by > CGI::Application::Plugin::Cache, and as such, it's regular use > is not > endorsed. > > MOTIVATIONS > It was originally my intent to create a simple caching plugin > based upon > Cache::Cache. My need was very simple: I had a drop-down > list of > items > that was populated from the database on every request to a > particular > page. The data in those lists changed once or twice a year. > Because of > the static nature of this data, it became a great candidate for > caching. > The performance benefits were obvious to me. > > Some issues with Cache::Cache were pointed out to me, > particularly the > (lack of) efficency when it came to determining the expiration > time of a > value and purging those expired values. Cache::FastMmap was > recommended > to me as a great alternative. Unfortunately, Cache::FastMmap > doesn't > play nicely with Windows, which is what half of our web > applications run > on. Wanting to implement caching throughout our suite of > applications, > but not wanting to write custom caching code for each platform > we deploy > to, drove me to creating a generic, reusable caching interface. > > ACKNOWLEDGEMENTS > A big thanks to Cees Hek for helping me transform my idea for a > simple > Cache::Cache plugin to a fully-featured generic caching > interface, > Perrin Harkins for sharing some of his caching expertise with > me, and, > as always, thanks to the usual crowd in #cgiapp on > irc.perl.org for > keeping me company and making these projects worthwhile. > > BUGS > Please report any bugs or feature requests to > "[EMAIL PROTECTED]", or > through the web > interface at > <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CGI-Application- > Plugin-C > ache>. I will be notified, and then you'll automatically be > notified of > progress on your bug as I make changes. > > SEE ALSO > CGI::Application, Cache::Cache, Cache::FastMmap, > Cache::Memcached. > > AUTHOR > Jason A. Crome, "<[EMAIL PROTECTED]>" > > COPYRIGHT & LICENSE > Copyright 2006 Jason A. Crome, all rights reserved. > > This program is free software; you can redistribute it and/or > modify it > under the same terms as Perl itself. > > > > --------------------------------------------------------------------- > Web Archive: http://www.mail-archive.com/[email protected]/ > http://marc.theaimsgroup.com/?l=cgiapp&r=1&w=2 > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: [EMAIL PROTECTED] > > --------------------------------------------------------------------- Web Archive: http://www.mail-archive.com/[email protected]/ http://marc.theaimsgroup.com/?l=cgiapp&r=1&w=2 To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
