Apache::AxKit::Language::XSPCaching - eXtensible Server Pages with AxKit caching



NAME

Apache::AxKit::Language::XSPCaching - eXtensible Server Pages with AxKit caching


SYNOPSIS

  <Files page.xsp>
    AxAddStyleMap application/x-xsp +Apache::AxKit::Language::XSPCaching
  </Files>


DESCRIPTION

This module allows the existing AxKit caching mechanism to function for XSP-generated content.

Note that there is a separate AxKit caching mechanism for XSP-generated content which can be enabled by defining methods for has_changed and cache_params in your XSP script. However, that mechanism only caches the direct output of the XSP. If your output is processed further, by XSLT for example, the final output is not cached. This module enables caching even for XSP output that is further processed through an AxKit pipeline.

It is very important to understand that you must do more work beyond simply enabling this module for caching to work properly. You must inform AxKit of the dependencies in your XSP script, and you must inform AxKit whether the output of your XSP script varies depending on various parameters such as the query string, a cookie, HTTP request header, etc.

If you do not take care of these details, then your page will not be updated when its content changes. Even worse, if your page is customized for a specific user, then one user would see a different user's page!

Dependencies

You can inform AxKit of the dependencies in your XSP script by using the function AxKit::add_depends. For example, if your XSP script reads in a configuration file /etc/axkit/xsp.conf and generates different output depending on the content of that file, then you should place this function call in your XSP script:

  AxKit::add_depends( '{content}' . '/etc/axkit/xsp.conf' );

AxKit will record this dependency in its cache. If the timestamp of this file is later than the timestamp of the cached output, then AxKit will call your XSP script to regenerate the output.

Suggestion: If your XSP script depends on some content from a database or some other source that cannot be tracked by a timestamp on a file, then create an AxKit plugin that will determine the last-modified-time of that content and then stamp a file with that time. For example:

  package My::AxKit::Plugin::LastModifiedTimestamp;
  use Apache ();
  use Apache::Constants ();
  sub handler {
    my $r = shift;
    my $time = last_modified(); # determine last-modified-time of content
    my $file = '/var/cache/axkit/myscript.timestamp';
    if ( not -e $file ) {
        my $fh = Apache->gensym();
        open( $fh, ">$file" ) or die "Could not open $file: $!";
        print $fh "The timestamp of this file is updated when\n";
        print $fh "the dependencies of myscript.xsp have changed.\n";
        close( $fh ) or die "Could not close $file: $!";
    }
    utime( $time, $time, $file ) or die "Could not timestamp $file: $!";
    return Apache::Constants::OK;
  }

Enable your plugin like this:

  <Files myscript.xsp>
    AxAddPlugin +My::AxKit::Plugin::LastModifiedTimestamp
  </Files>

Cache Parameters

You can inform AxKit whether the output of your XSP script varies by query string using the QueryStringCache plugin:

  <Files myscript.xsp>
    AxAddPlugin +Apache::AxKit::Plugin::QueryStringCache
  </Files>

AxKit will then create a separate cache file for different values of your query string. Also see QueryStringCacheRegexp.

If your output varies depending on other parameters, such as a cookie or HTTP request header, then create your own plugin and set the "axkit_cache_extra" Apache notes variable. For example:

  package My::AxKit::Plugin::CacheParams;
  use Apache::Cookie ();
  use Apache::Constants ();
  sub handler {
    my $r = shift;
    my $extra = $r->notes( 'axkit_cache_extra' );
    my %cookies = Apache::Cookie->fetch;
    my $cookie = $cookies{COOKIENAME};
    $extra .= ":COOKIENAME=" . join( '&', $cookie->value ) if $cookie;
    # (I chose a colon prefix because that's what AxKit uses to separate extras)
    $r->notes( axkit_cache_extra => $extra );
    return Apache::Constants::OK;
  }
  <Files script.xsp>
    AxAddPlugin +My::AxKit::Plugin::CacheParams
  </Files>

Note about memory usage

For improved performance, AxKit uses memory to cache a list of stylesheets for each document. However, when extra cache parameters are set using any of the above methods, AxKit uses separate cache entries for each different setting of "axkit_cache_extra", even though the list of stylesheets is probably the same for these different settings. This use of separate cache entries can lead to wasted memory, especially when caching personalized pages for thousands of users. To avoid the extra memory usage, patch AxKit so that it uses a cache entry independent of "axkit_cache_extra". Replace the following line in the get_styles subroutine of AxKit.pm:

    my $key = $cache->key();

with this line:

    my $key = join(':', $provider->key(), $style, $media);

Note well: only replace this one line. Do not replace any other instances of "$cache->key()".

The above patch can wreak havoc if you dynamically define the list of stylesheets using AxAddDynamicProcessor based either directly or indirectly on information contained in "axkit_cache_extra". In this case you can either comment out the caching of stylesheet lists entirely, or make sure that you define a different "preferred_style" for each different dynamic list of stylesheets so that the $key will be different for each one.

Other Issues

If your XSP script performs some tasks such as authentication or setting HTTP response headers, then you need to move these tasks into a plugin so that they will run even when AxKit serves your XSP output from its cache. Remember: AxKit will never run your XSP script again once the output becomes cached! Except, of course, when a dependency was updated (AxKit::add_depends) or the cache parameters change (axkit_cache_extra).

The AxKit cache is not automatically cleaned. If space is an issue, such as when you are caching pages that are customized for individual users, then you need to run a cron job or something similar to delete stale files from the cache. For example, see Apache::AxKit::CacheCleaner.

Specifics

This module overrides the $r->no_cache method which gets called from Apache::AxKit::Language::XSP so that caching remains on.

You can turn caching back off in your XSP script by calling AxKit::Apache::no_cache($r,1). You may wish to do this for error pages for example. This will invoke the following methods for you:

  Apache::no_cache($r,1); # send "no-cache" headers to browser
  $AxKit::Cache->no_cache(1). # do not store in AxKit cache

If you want AxKit caching turned off but you do not want the "no-cache" headers sent to the browser, then just invoke $AxKit::Cache->no_cache(1).

Alternatively, if you want to send the "no-cache" headers to the browser but keep AxKit caching turned on, then just call Apache::no_cache($r,1);


SEE ALSO

AxKit, Apache::AxKit::Plugin::QueryStringCache, Apache::AxKit::Plugin::QueryStringCacheRegexp, Apache::AxKit::CacheCleaner


AUTHOR

Ken Neighbors <ken@nsds.com>


COPYRIGHT and LICENSE

Copyright (c) 2005-2006 Ken Neighbors. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

 Apache::AxKit::Language::XSPCaching - eXtensible Server Pages with AxKit caching