2. What is CHI?
• Standard interface and implementation for
Perl caches - aka 'DBI for caching'
• Inspired by Cache::Cache, but improves
performance and extendibility
3. Why do we need it?
• CPAN modules that need cache can simply
ask for a CHI handle
• Implement generic caching features in a
single place
• Make new cache backends trivial to create
4. Modules that just need
"a cache"'
• Templating: HTML::Mason, Template::Toolkit
• Web frameworks: Catalyst, CGI::Application
• Sessions: CGI::Session, Apache::Session
• ORMs: Data::ObjectDriver, Rose::DB::Object
• Code processing: Perl::Tidy, Perl::Critic
• Startup impaired: Class::MOP/Moose
5. Basic API
# Choose a driver
#
my $cache = CHI->new( driver => 'Memory' );
my $cache = CHI->new(
driver => 'File',
cache_root => '/path/to/root'
);
my $cache = CHI->new(
driver => 'FastMmap',
root_dir => '/path/to/root',
cache_size => '1k'
);
# Get and set items
#
my $customer = $cache->get($name);
if ( !defined $customer ) {
$customer = get_customer_from_db($name);
$cache->set( $name, $customer, "10 minutes" );
}
$cache->remove($name);
6. Current CHI drivers
• Memory - In-process memory
• File - Hierarchical, one file per entry
• FastMmap - Mmap'ed files
• Memcached
• DBI
• BerkeleyDB
• CacheCache - Any existing Cache::Cache driver
7. Driver skeleton
package CHI::Driver::MyDriver;
use Moose;
extends 'CHI::Driver';
has 'attr' => ( ... );
sub fetch {
my ( $self, $key ) = @_;
sub store {
my ( $self, $key, $data ) = @_;
sub remove {
my ( $self, $key ) = @_;
sub clear {
my ($self) = @_;
sub get_keys {
my ($self) = @_;
sub get_namespaces {
my ($self) = @_;
8. Avoiding miss stampedes
• Problem: Many processes try to recompute
cache when it expires
• Solution 1: Probabilistic expiration
my $cache = CHI->new(..., expires_variance => 0.20);
• Solution 2: Busy locks
my $cache = CHI->new(..., busy_lock => '30 sec');
9. Recomputation latency
• Problem: Client waits while data is recomputed
• Solution 1: Background recomputation
my $customer = $cache->compute( $name,
sub { get_customer_from_db($name) }, "10 minutes" );
• Solution 2: Externally initiated recomputation
use LWP::Simple;
foreach my $url (@common_urls) {
# recompute_cache temporarily reduces expiration
get("http://www.mysite.com/$url?recompute_cache=1");
}
10. Multilevel caches
• Several caches can be chained together
• Example 1: L1 cache in front of memcached
my $cache = CHI->new(
driver => 'Memcached',
servers => [ "10.0.0.15:11211", ... ],
l1_cache => { driver => 'Memory', max_size => '1MB' } );
• Example 2: Mirror cache to migrate cache directories
my $cache = CHI->new(
driver => 'File',
root_dir => '/old/root' mirror_cache =>
{ driver => 'File', root_dir => '/new/root' } );
11. To do
• Cache statistics - collection and reporting
• Registry of storage types (like Rose::DB) with
memoization
• Background recomputation
• Close performance gap between CHI and native
• Plugins for Catalyst, CGI::Session, etc.
12. Additional resources
• Download CHI from CPAN -
http://cpan.uwinnipeg.ca/dist/CHI
• Search for drivers -
http://cpan.uwinnipeg.ca/search?query=CHI::Driver
• Subscribe to perl-cache mailing list -
http://groups.google.com/group/perl-cache-discuss