Catalyst Advent - Day 11 - Caching

NOTE: This article was written in 2005. Make sure to check the Catalyst Wiki (http://dev.catalyst.perl.org/wiki) for updated information.

Catalyst makes it easy to employ several different types of caching to speed up your applications.

Cache Plugins

There are three wrapper plugins around common CPAN cache modules: Cache::FastMmap, Cache::FileCache, and Cache::Memcached. These can be used to cache the result of slow operations.

This very page you're viewing makes use of the FileCache plugin to cache the rendered XHTML version of the source POD document. This is an ideal application for a cache because the source document changes infrequently but may be viewed many times.

    use Catalyst qw/Cache::FileCache/;

    ...

    use File::stat;
    sub render_pod : Local {
        my ( self, $c ) = @_;

        # the cache is keyed on the filename and the modification time
        # to check for updates to the file.
        my $file  = $c->path_to( 'root', '2005', '11.pod' );
        my $mtime = ( stat $file )->mtime;

        my $cached_pod = $c->cache->get("$file $mtime");
        if ( !$cached_pod ) {
            $cached_pod = do_slow_pod_rendering();
            # cache the result for 12 hours
            $c->cache->set( "$file $mtime", $cached_pod, '12h' );
        }
        $c->stash->{pod} = $cached_pod;
    }

We could actually cache the result forever, but using a value such as 12 hours allows old entries to be automatically expired when they are no longer needed.

Page Caching

Another method of caching is to cache the entire HTML page. While this is traditionally handled by a front-end proxy server like Squid, the Catalyst PageCache plugin makes it trivial to cache the entire output from frequently-used or slow actions.

Many sites have a busy content-filled front page that might look something like this. It probably takes a while to process, and will do the exact same thing for every single user who views the page.

    sub front_page : Path('/') {
        my ( $self, $c ) = @_;

        $c->forward( 'get_news_articles' );
        $c->forward( 'build_lots_of_boxes' );
        $c->forward( 'more_slow_stuff' );

        $c->stash->{template} = 'index.tt';
    }

We can add the PageCache plugin to speed things up.

    use Catalyst qw/Cache::FileCache PageCache/;

    sub front_page : Path ('/') {
        my ( $self, $c ) = @_;

        $c->cache_page( 300 );

        # same processing as above
    }

Now the entire output of the front page, from <html> to </html>, will be cached for 5 minutes. After 5 minutes, the next request will rebuild the page and it will be re-cached.

Note that the page cache is keyed on the page URI plus all parameters, so requests for / and /?foo=bar will result in different cache items. Also, only GET requests will be cached by the plugin.

You can even get that front-end Squid proxy to help out by enabling HTTP headers for the cached page.

    MyApp->config->{page_cache}->{set_http_headers} = 1;

This would now set the following headers so proxies and browsers may cache the content themselves.

    Cache-Control: max-age=($expire_time - time)
    Expires: $expire_time
    Last-Modified: $cache_created_time

Template Caching

Template Toolkit provides support for caching compiled versions of your templates. To enable this in Catalyst, use the following configuration. TT will cache compiled templates keyed on the file mtime, so changes will still be automatically detected.

    package MyApp::View::TT;

    use strict;
    use warnings;
    use base 'Catalyst::View::TT';

    __PACKAGE__->config(
        COMPILE_DIR => '/tmp/template_cache',
    );

    1;

More Info