Caching¶
About¶
Beaker’s caching system was originally based off the Perl Cache::Cache module, which was ported for use in Myghty. Beaker was then extracted from this code, and has been substantially rewritten and modernized.
Several concepts still exist from this origin though. Beaker’s caching (and its sessions, though its behind the scenes) utilize the concept of NamespaceManager, and Container objects to handle storing cached data.
Each back-end utilizes a customized version of each of these objects to handle storing data appropriately depending on the type of the back-end.
The CacheManager
is responsible for getting the
appropriate NamespaceManager, which then stores the cached values. Each
namespace corresponds to a single thing
that should be cached. Usually
a single thing
to be cached might vary slightly depending on parameters,
for example a template might need several different copies of itself stored
depending on whether a user is logged in or not. Each one of these copies
is then keyed
under the NamespaceManager and stored in a Container.
There are three schemes for using Beaker’s caching, the first and more
traditional style is the programmatic API. This exposes the namespace’s
and retrieves a Cache
object that handles storing
keyed values in a NamespaceManager with Container objects.
The more elegant system, introduced in Beaker 1.3, is to use the cache decorators, these also support the use of Cache Regions.
Introduced in Beaker 1.5 is a more flexible cache_region()
decorator capable of decorating functions for use with Beaker’s
Cache Regions before Beaker has been configured. This makes
it possible to easily use Beaker’s region caching decorator on functions in
the module level.
Creating the CacheManager Instance¶
Before using Beaker’s caching, an instance of the
CacheManager
class should be created. All of the
examples below assume that it has already been created.
Creating the cache instance:
from beaker.cache import CacheManager
from beaker.util import parse_cache_config_options
cache_opts = {
'cache.type': 'file',
'cache.data_dir': '/tmp/cache/data',
'cache.lock_dir': '/tmp/cache/lock'
}
cache = CacheManager(**parse_cache_config_options(cache_opts))
Additional configuration options are documented in the Configuration section of the Beaker docs.
Programmatic API¶
To store data for a cache value, first, a NamespaceManager has to be
retrieved to manage the keys for a thing
to be cached:
# Assuming that cache is an already created CacheManager instance
tmpl_cache = cache.get_cache('mytemplate.html', type='dbm', expire=3600)
Note
In addition to the defaults supplied to the
CacheManager
instance, any of the Cache options
can be changed on a per-namespace basis, as this example demonstrates
by setting a type
, and expire
option.
Individual values should be stored using a creation function, which will be called anytime the cache has expired or a new copy needs to be made. The creation function must not accept any arguments as it won’t be called with any. Options affecting the created value can be passed in by using closure scope on the creation function:
search_param = 'gophers'
def get_results():
# do something to retrieve data
data = get_data(search_param)
return data
# Cache this function, based on the search_param, using the tmpl_cache
# instance from the prior example
results = tmpl_cache.get(key=search_param, createfunc=get_results)
Invalidating¶
All of the values for a particular namespace can be removed by calling the
clear()
method:
tmpl_cache.clear()
Note that this only clears the key’s in the namespace that this particular Cache instance is aware of. Therefore its recommend to manually clear out specific keys in a cache namespace that should be removed:
tmpl_cache.remove_value(key=search_param)
Decorator API¶
When using the decorator API, a namespace does not need to be specified and will instead be created for you with the name of the module + the name of the function that will have its output cached.
Since its possible that multiple functions in the same module might have the same name, additional arguments can be provided to the decorators that will be used in the namespace to prevent multiple functions from caching their values in the same location.
For example:
# Assuming that cache is an already created CacheManager instance
@cache.cache('my_search_func', expire=3600)
def get_results(search_param):
# do something to retrieve data
data = get_data(search_param)
return data
results = get_results('gophers')
The non-keyword arguments to the cache()
method are the additional ones used to ensure this function’s cache results
won’t clash with another function in this module called get_results
.
The cache expire argument is specified as a keyword argument. Other valid
arguments to the get_cache()
method such
as type
can also be passed in.
When using the decorator, the function to cache can have arguments, which will be used as the key was in the Programmatic API for the data generated.
Warning
These arguments can not be keyword arguments.
Invalidating¶
Since the cache()
decorator hides the
namespace used, manually removing the key requires the use of the
invalidate()
function. To invalidate
the ‘gophers’ result that the prior example referred to:
cache.invalidate(get_results, 'my_search_func', 'gophers')
If however, a type was specified for the cached function, the type must
also be given to the invalidate()
function so that it can remove the value from the appropriate back-end.
Example:
# Assuming that cache is an already created CacheManager instance
@cache.cache('my_search_func', type="file", expire=3600)
def get_results(search_param):
# do something to retrieve data
data = get_data(search_param)
return data
cache.invalidate(get_results, 'my_search_func', 'gophers', type="file")
Note
Both the arguments used to specify the additional namespace info to the
cache decorator and the arguments sent to the function need to be
given to the region_invalidate()
function so that it can properly locate the namespace and cache key
to remove.
Cache Regions¶
Rather than having to specify the expiration, or toggle the type used for
caching different functions, commonly used cache parameters can be defined
as Cache Regions. These user-defined regions than may be used
with the region()
decorator rather than
passing the configuration.
This can be useful if there are a few common cache schemes used by an application that should be setup in a single place then used as appropriate throughout the application.
Setting up cache region’s is documented in the cache region options section in Configuration.
Assuming a long_term
and short_term
region were setup, the
region()
decorator can be used:
@cache.region('short_term', 'my_search_func')
def get_results(search_param):
# do something to retrieve data
data = get_data(search_param)
return data
results = get_results('gophers')
Or using the cache_region()
decorator:
@cache_region('short_term', 'my_search_func')
def get_results(search_param):
# do something to retrieve data
data = get_data(search_param)
return data
results = get_results('gophers')
The only difference with the cache_region()
decorator is
that the cache does not need to be configured when its used. This allows one
to decorate functions in a module before the Beaker cache is configured.
Invalidating¶
Since the region()
decorator hides the
namespace used, manually removing the key requires the use of the
region_invalidate()
function. To invalidate
the ‘gophers’ result that the prior example referred to:
cache.region_invalidate(get_results, None, 'my_search_func', 'gophers')
Or when using the cache_region()
decorator, the
beaker.cache.region_invalidate()
function should be used:
region_invalidate(get_results, None, 'my_search_func', 'gophers')
Note
Both the arguments used to specify the additional namespace info to the
cache decorator and the arguments sent to the function need to be
given to the region_invalidate()
function so that it can properly locate the namespace and cache key
to remove.