-----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]