Dan -

Thanks for the positive feedback!

While I haven't documented or done much with the driver interface yet, I imagine the abstraction it provides will also give driver authors the flexibility to add features to their caching backend of choice.

Thanks for the info/suggestion. I'm going to play around with that a bit as I'm developing this.
Jason

On Jul 7, 2006, at 5:00 PM, Dan Horne wrote:

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