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]

Reply via email to