RAM Cache¶
Introduction¶
The RAM cache is a Zope facility to create custom in-process caches.
Using memcached backend¶
By default, Zope uses an in-process memory cache. It is possible to replace
this with memcached
.
Advantages:
All front-end clients share the cache.
Cache survives over a client restart.
Memoizers¶
Memoize’s RAM cache can be replaced with a memcached
backend with the
following snippet.
See the set-up for the https://plone.org/ site as an example:
RAM Cache¶
The RAM cache is used e.g. as a rendered template cache backend.
You can add MemcachedManager
to your Zope setup, and replace the
RamCache instance in the Management Interface with a new instance of MemcachedManager
(keep the id the same).
Using custom RAM cache¶
You want to use a custom cache if you think cache size or saturation will pose problems.
The following advanced example shows how to enhance existing content type text and description accessors by performing HTML transformations and caching the result in a custom RAM cache.
Example:
import logging
import lxml.html
from zope.app.cache import ram
from Products.feedfeeder.content.item import FeedFeederItem
from gomobile.xhtmlmp.transformers.xhtmlmp_safe import clean_xhtml_mp
logger = logging.getLogger("GoMobile")
logger.info("Running in feedfeeder monkey-patches")
# Cache storing transformed XHTML
xhtml_cache = ram.RAMCache()
xhtml_cache.update(maxAge=86400, maxEntries=1000)
# Dummy object to mark missing values from cache
_marker = object()
def cache(name):
""" Special cache decorator which generates cache key based on context object and cache name """
def decorator(fun):
def replacement(context):
key = str(context.UID()) + "." + name
cached_value = xhtml_cache.query(key, default=_marker)
if cached_value is _marker:
cached_value = fun(context)
xhtml_cache.set(cached_value, key)
return cached_value
return replacement
return decorator
def flush_cache(name, context):
""" Clear entry in RAMCache
global_key is function specific key, key is context specific key.
"""
key = context.UID() + "." + name
xhtml_cache.invalidate(key)
#
# Modify existing body text and description accessors so that
# 1) HTML is cleaned
# 2) The result cleaned HTML is cached in RAM
#
# We do not persistently want to store cleaned HTML,
# since our cleaner might be b0rked and we want to
# regenerate cleaned HTML when needed.
#
# Run in monkey patching
FeedFeederItem._old_getText = FeedFeederItem.getText
FeedFeederItem._old_setText = FeedFeederItem.setText
FeedFeederItem._old_Description = FeedFeederItem.Description
FeedFeederItem._old_setDescription = FeedFeederItem.setDescription
@cache("text")
def _getText(self):
""" Body text accessor """
text = FeedFeederItem._old_getText(self)
if text:
# can be None
clean = clean_xhtml_mp(text)
print "Cleaned text:" + clean
return clean
return text
def _setText(self, value):
FeedFeederItem._old_setText(self, value)
flush_cache("text", self)
@cache("description")
def _Description(self):
""" Description accessor """
text = FeedFeederItem._old_Description(self)
#print "Accessing description:" + str(text)
# Remove any HTML formatting in the description
if text:
parsed = lxml.html.fromstring(text.decode("utf-8"))
clean = lxml.html.tostring(parsed, encoding="utf-8", method="text").decode("utf-8")
#print "Cleaned decsription:" + clean
return clean
return text
def _setDescription(self, value):
FeedFeederItem._old_setDescription(self, value)
flush_cache("description", self)
FeedFeederItem.getText = _getText
FeedFeederItem.setText = _setText
FeedFeederItem.Description = _Description
FeedFeederItem.setDescription = _setDescription
ZCacheable¶
ZCacheable
is an ancient Zope design pattern for caching. It allows
persistent objects that are subclasses of OFS.Cacheable
to have the
cache backend configured externally.
The cache type (cache id) in use is stored
persistently per cache user object,
but the cache can be created at runtime (RAM cache) or externally
(memcached
) depending on the situation.
Note
Do not use ZCacheable
in new code.
It takes optional backends which must be explicitly set:
def enableCaching():
pas=getPAS()
if pas.ZCacheable_getManager() is None:
pas.ZCacheable_setManagerId(manager_id="RAMCache")
getLDAPPlugin().ZCacheable_setManagerId(manager_id="RAMCache")
The RAMCache
above is per thread.
Clearing this cache for all ZEO clients is hard.
Some hints:
It is enabled per persistent object:
>>> app.test2.acl_users.ZCacheable_isCachingEnabled()
<Products.StandardCacheManagers.RAMCacheManager.RAMCache instance at 0x10a064cf8>
>>> app.test2.acl_users.ZCacheable_enabled()
1
Get known cache backends:
>>> app.test2.acl_users.ZCacheable_getManagerIds()
({'id': 'caching_policy_manager', 'title': ''}, {'id': 'HTTPCache', 'title': ''}, {'id': 'RAMCache', 'title': ''}, {'id': 'ResourceRegistryCache', 'title': 'Cache for saved ResourceRegistry files'})
Disabling it (persistent change):
>>> app.test2.acl_users.ZCacheable_setManagerId(None)
>>> app.test2.acl_users.ZCacheable_enabled()
1
>>> app.test2.acl_users.ZCacheable_getManagerIds()
({'id': 'caching_policy_manager', 'title': ''}, {'id': 'HTTPCache', 'title': ''}, {'id': 'RAMCache', 'title': ''}, {'id': 'ResourceRegistryCache', 'title': 'Cache for saved ResourceRegistry files'})
>>> app.test2.acl_users.ZCacheable_isCachingEnabled()
>>> app.test2.acl_users.ZCacheable_setEnabled(False)
More info: