Home | Wiki | OI 1.x Docs | OI 2.x Docs OI logo

Caching in OpenInteract

As of version 1.51, caching of generated content is supported in OpenInteract. This can provide a dramatic speedup to content that has a lot of template processing. Caching of SPOPS objects is not officially supported yet.

There are two different levels of caching for content in OpenInteract:

Fortunately, the first one is done in the Template Toolkit and in the OpenInteract implementation for the provider. How to control this from OpenInteract is discussed briefly below. The authoritative voice on Template Toolkit caching is in the excellent TT documentation.

Content Caching

Content caching is still fairly young in OpenInteract, and it's not appropriate (or useful) for all purposes. It's best when used on pages that contain a lot of data or require a good deal of processing.

Global Configuration

The following keys in your server configuration are used in caching:

Identifying Cached Data

Each piece of cached data needs to be uniquely identified. We use two pieces of information for this: a basic key and a set of parameters and values. For instance, you can cache a listing of news entries but there can be multiple types of listings depending on the news section the user filters by. So your information would be:

We describe below how to set this up.

How to Setup

Here are the four requirements for caching content:

First: your handler needs to subclass either OpenInteract::Handler::GenericDispatcher or OpenInteract::CommonHandler. (Most handlers already do this so it's not much of a burden.)

Second: caching must be configured in your action using the parameter cache_key and, optionally, cache_expire.

Third: you must explicitly check in your handler to see if there is cached content.

Fourth: you must call a new method to generate the content.

Handler Parents

Just do one of the following, assuming your handler is named MySite::Handler::MyHandler:

use base qw( OpenInteract::Handler::GenericDispatcher );

use base qw( OpenInteract::CommonHandler );

@MySite::Handler::MyHandler::ISA = qw( OpenInteract::Handler::GenericDispatcher );

@MySite::Handler::MyHandler::ISA = qw( OpenInteract::CommonHandler );

Action Configuration

Each method has its own basic key. This key is used along with parameters to uniquely identify the cached content so it can be retrieved properly. In your action configuration you set the basic key for each method that is going to produce cached content. For instance, here we set the key for the method 'listing':

     mypkg/conf/action.perl
     ----------------------------------------
     $action = {
        myaction => {
           class    => 'OpenInteract::Handler::MyHandler',
           security => 'no',
           cache_key => {
               listing => 'myaction::listing',
           },
        },
     };
     

You can also set expiration times here. These will override the setting in the server configuration under cache_info.data.default_expire:

     mypkg/conf/action.perl
     ----------------------------------------
     $action = {
        myaction => {
           class    => 'OpenInteract::Handler::MyHandler',
           security => 'no',
           cache_key => {
               listing => 'myaction::listing',
           },
           cache_expire => {
               listing => 1800,
           },
        },
     };
     

Checking for Cached Content

Every handler deriving from OpenInteract::Handler::GenericDispatcher gains the method check_cache(). Here is a typical call:

    sub listing {
        my ( $class, $p ) = @_;
        my $R = OpenInteract::Request->instance;
        my $num_items = $R->apache->param( 'num_items' )
                        || DEFAULT_NUM_ITEMS;
        my %cache_params = ( num_items => $num_items );
        my $cached = $class->check_cache( $p, \%cache_params );
        return $cached if ( $cached );
    }
    

Here we are saying that the cached content depends on the number of items requested. Once we have that information we can check to see if there is anything in the cache matching it.

It's necessary to pass $p to the caching methods since our handlers are class methods rather than objects. If they were objects we could save the action state in them. As it is, we need to work around it. The next version of OpenInteract makes these full-fledged objects.

Generating Content for the Cache

Every handler deriving from OpenInteract::Handler::GenericDispatcher also gains the method generate content(). Here is a typical call (the first part is copied from above):

    sub listing {
        my ( $class, $p ) = @_;
        my $R = OpenInteract::Request->instance;
        my $num_items = $R->apache->param( 'num_items' )
                        || DEFAULT_NUM_ITEMS;
        my %cache_params = ( num_items => $num_items );
        my $cached = $class->check_cache( $p, \%cache_params );
        return $cached if ( $cached );
        my $item_list = $R->item->fetch_group({ limit => $num_items,
                                                order => 'created_on DESC' });
        my %vars = ( item_list => $item_list );
        return $class->generate_content( $p, \%cache_params, \%vars,
                                         { name => 'mypkg::mytmpl' } );
    }
    

Previously we might have called for the return statement:

        return $R->template->handler( {}, \%vars,
                                      { name => 'mypkg::mytmpl' } );
    

There are three differences -- besides the name! -- between these two statements:

Clearing the Cache

You have the option of clearing the cache whenever you manipulate data. For instance, if you edit the title of a news story you do not want the old title to appear in the story listing. And if you delete a story and mark it as inactive because it's inappropriate, you do not want it in your headline listing.

So whenever you modify data, it's normally best to call clear_cache(). This method is inherited from OpenInteract::Handler::GenericDispatcher like the others. Here's an example:

    sub edit {
        my ( $class, $p ) = @_;
        my $R = OpenInteract::Request->instance;
        my $thingy_id = $R->apache->param( 'thingy_id' );
        my $thingy = $R->thingy->fetch( $thingy_id );
        $thingy->{foo} = $R->apache->param( 'foo' );
        eval { $thingy->save };
        if ( $@ ) {
            return $class->listing({ error_msg => "Blarg: $@" });
        }
        else {
            $class->clear_cache();
            return $class->listing({ status_msg => 'Cool' });
        }
    }
    

So when the 'listing' method is called after a successful save() on the object, the previously cached content will have been deleted and the content will be regenerated anew.

When Not to Cache

If you're an admin user you frequently see functionality that normal users do not see: Edit or Remove links next to an object, etc. You do not want to cache this content, since users shouldn't see this information. (Normal users modifying the object shouldn't be an issue, since security should take care of it.)

As a result, any user defined as an administrator will not view or save cached content. "Defined as an administrator" means that a call to the following will return true:

    my $is_admin = $R->{auth}{is_admin};
    

Example: news

The 'news' package shipped with OpenInteract has an implementation of caching that you can experiment with.

Template Caching

Instead of parsing a template every time you request it, the Template Toolkit (TT) will translate the template to Perl code and, if allowed, cache it in memory. Keeping templates in memory will make your website much faster.

TT will also save your compiled template to the filesystem. This is useful for successive starts of your website -- if the template if found in the compile directory TT doesn't need to parse it again, even though you've stopped and restarted your server since it was first read.

Configuration

The following keys from your server configuration control caching and compiling:


Home | Wiki | OI 1.x Docs | OI 2.x Docs
SourceForge Logo