Template::Provider::Preload - Preload templates to save memory when forking
my $template = Template->new( LOAD_TEMPLATES => [ Template::Provider::Preload->new( PRECACHE => 1, PREFETCH => '*.tt', INCLUDE_PATH => 'my/templates', COMPILE_DIR => 'my/cache', # Parser options should go here instead of to the # parent Template constructor. INTERPOLATE => 1, PRE_CHOMP => 1, ); ], );
One of the nicer things that the Template Toolkit modules do in the default Template::Provider is provide two powerful caching features.
The first is to cache the result of the slow and expensive compilation phase, storing the Perl version of the template in a specific cache directory. This mechanism is disabled by default, and enabled with the
The second is that the compiled templates will be cached in memory the first time they are used, based on template path. This feature is enabled by default and permitted to grow to infinite size. It can be limited or disabled via the
The default cache strategy works just fine in a single-process application, and in fact in many such cases is the optimum caching strategy.
However, the default cache strategy can perform horribly in several situations relating to large-scale and high-demand applications.
Template::Provider::Preload can be used to create caching strategies that are more appropriate for heavily forking applications such as clustered high-traffic mod_perl systems.
While Template::Provider::Preload is useful in many high-forking scenarios, we will use the (dominant) case of a forking Apache application in all of the following examples. You should be able to exchange all uses of terms like "Apache child" with your equivalent interchangably.
In some very high security environments, the web user will not have the right to create any files whatsoever, including temporary files.
This prevents the use of the compilation cache, and the template update checks in the provider greatly complicate the possibility of building the cache in advance offsite.
By allowing all templates to be compiled to memory in advance, you can use templates at their full speed without the penalty of parsing and compiling every template once per Apache child process.
Most of the following cases also assume a well-control static production environment, where the template content will not change (and a web server restart is done each time a new version of the application is deployed).
Under the default cache strategy (with a compilation directory enabled) the first Apache child that uses each template will compile and cache the template. Each Apache child that uses the templates will then need to separately load the compiled templates into memory.
With web servers often having 20 or 50 or 100 child processes each, templates that expand into 10 meg of memory for a single process (which can be quite possible with a reasonable number of templates) can easily expand into a gigabyte of memory that contributes nothing other than to eat into your more useful object or disk caches.
With large numbers of large templates on multi-core servers with many many child processes, you can even put yourself in the situation of needing to requisition additional web servers due to memory contraints, rather than CPU constraints.
Memory saved by loading a template once instead of 100 times can be retasked to enable higher throughput (by providing more children) or reduced latency (by boosting various caches).
In cluster environments where all front-end servers will use a common back-end Network-Attached Storage to store the website, reducing the number, frequency and size of disk interations (both reads and stats) is an important element in reducing or eliminating disk contention and network load on what is often a critical shared resource.
Reducing the number of template-related requests serves a triple purpose of reducing the size, speed, capacity and cost of required networking equipment, allowing additional front-end server growth with lower change of requiring (highly disruptive) upgrades to central network or storage kit, and eliminating high-latency network IO requests from the web pipeline.
By compiling and loading all of the common templates in advance into a seperate pre-fork memory cache (hereafter "precache") you can create an environment in which the individual Apache children will not need issue network filesystem requests except in the case of rare and unusual website requests that load rarely used templates.
This can be taken to the extreme by loading every possible template into the precache, which will eliminate template entirely and enable more-unusual tricks like allowing the web server to disconnect entirely from the source of the templates.
Template::Provider::Preload provides two additional caching features that can be used on their own to implement the most common caching strategy, or in combination with the default cache settings to allow for more varied and subtle caching strategies.
Template::Provider::Preload provides the
prefetch method that can be used to search the template include path and compile/load templates in bulk.
The set of templates to load can be defined very flexibly, allowing you to preload templates at various levels. Typical examples might be loading the entire template library, loading only the common include templates (page headers and footers etc), or loading all templates accessible by the public while not loading rarely used staff or administration templates.
Template::Provider::Preload adds an optional third cache that sits between the disk compilation cache and the run-time memory cache.
The precache is designed to cache templates in memory separately from the run-time memory cache. Holding pre-fork templates in a separate cache allows the normal cache to behave appropriately at run-time for templates that aren't in the precache, without being distracted by the precache entries and without having to stat the precache templates
my $provider = Template::Provider::Preload->new( PRECACHE => 1, PREFETCH => '*.tt', INCLUDE_PATH => 'my/templates', COMPILE_DIR => 'my/cache', );
new constructor accepts all the same parameters as the underlying Template::Provider class, with two additions.
PRECACHE param indicates that the secondary prefetch cache should be created and used for this provider. The precatch is not size-constrained by the
CACHE_SIZE param, that option only affects the default run-time cache.
PREFETCH param indicates that an immediate
prefetch request should be made at constructor time to pre-populate the cache (the precache if it exists, or the run-time cache otherwise) with a specified set of templates.
The value of
PREFETCH can be either a single string or a File::Find::Rule object, which will be passed to prefetch. Alternatively, if the
PREFETCH param is an
ARRAY reference, the reference will be flattened to a list, allowing you to provide more than one file type string to
Returns a Template::Provider::Preload object, or throws an excpetion (dies) on error.
# Load all .tt templates into memory $provider->prefetch; # Load all .html and .eml templates into memory $provider->prefetch('*.html', '*.eml'); # Load all templates inside a SVN checkout into memory use File::Find::Rule; use File::Find::Rule::VCS; $provider->prefetch( File::Find::Rule->ignore_svn->file->readable->ascii );
prefetch method is used to specify that a set of template files should be immediately compiled (with the compiled templates cached if possible) and then the compiled templates are loaded into memory.
When used in combination with
prefetch method creates a caching strategy where the template files will never be looked at once the call to
prefetch has completed. Both positive (template found) and negative (not found or error) results will be cached.
When filling the precache, the use of the internal cache will be temporarily disabled to avoid polluting the run-time cache state.
Selection of the files to compile is done via a File::Find::Rule search across all
INCLUDE_PATH directories. If the same file exists within more than one
INCLUDE_PATH directory, only the first one will be compiled.
In the canonical usage, the
prefetch method takes a single parameter, which should be a File::Find::Rule object. The method will call
relative on the filter you pass in, so you should consider the
prefetch method to be destructive to the filter.
As a convenience, if the method is passed a series of strings, a new rule object will be created and the strings will be used to specific the required files to compile via a call to the
As a further convenience, if the method is passed no params, a default filter will be created for all files ending in .tt.
Returns true on success, or throws an exception (dies) on error.
The Template::Provider::Preload precaching logic assumes a stable production environment in which the template files will not be changed.
It is assumed that if a production release is made, then there will be a server or application restart that allows the caches to be refilled and then the children reforked.
Bugs should be reported via the CPAN bug tracker at
For other issues, or commercial enhancement or support, contact the author.
Adam Kennedy <firstname.lastname@example.org>
Copyright 2008 Adam Kennedy.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the LICENSE file included with this module.