Commit e0927707 authored by Julien Muchembled's avatar Julien Muchembled

Replacement for clearAllCache

The idea is to store cache keys (or a part of cache keys) persistently,
so that modifying them makes Cache Tool looks at for cache value in a new place.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@38549 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent fd457f22
......@@ -589,17 +589,17 @@ class BaseTemplateItem(Implicit, Persistent):
if meta_type == 'Script (Python)':
meta_type = 'ERP5 Python Script'
attr_list = [ '_dav_writelocks', '_filepath', '_owner', 'uid',
'workflow_history', '__ac_local_roles__' ]
attr_set = set(('_dav_writelocks', '_filepath', '_owner', 'uid',
'workflow_history', '__ac_local_roles__'))
if export:
attr_list += {
attr_set.update({
'ERP5 Python Script': (#'func_code', 'func_defaults', '_code',
'_lazy_compilation', 'Python_magic'),
#'Z SQL Method': ('_arg', 'template',),
}.get(meta_type, ())
}.get(meta_type, ()))
for attr in attr_list:
if attr in obj.__dict__:
for attr in obj.__dict__.keys():
if attr in attr_set or attr.startswith('_cache_cookie_'):
delattr(obj, attr)
if meta_type == 'ERP5 PDF Form':
......
......@@ -29,9 +29,13 @@
import string
from time import time
from AccessControl.SecurityInfo import allow_class
from AccessControl import allow_class, ClassSecurityInfo
from Acquisition import aq_base
from BTrees.Length import Length
from CachePlugins.BaseCache import CachedMethodError
from persistent import Persistent
from zLOG import LOG, WARNING
from Products.ERP5Type import Permissions
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable, _MARKER
from Products.ERP5Type.Utils import simple_decorator
from warnings import warn
......@@ -55,6 +59,50 @@ def initializePortalCachingProperties(self):
# itself will cause cache misses that we want to ignore
is_cache_ready = 1
class Cookie(Persistent):
value = 0
def __getstate__(self):
return self.value
def __setstate__(self, value):
self.value = value
def _p_resolveConflict(self, old_state, saved_state, new_state):
return 1 + max(saved_state, new_state)
def _p_independent(self):
return 1
class CacheCookieMixin:
"""Provides methods managing (ZODB) persistent keys to access caches
"""
security = ClassSecurityInfo()
security.declareProtected(Permissions.AccessContentsInformation,
'getCacheCookie')
def getCacheCookie(self, cache_name='default'):
"""Get key of valid cache for this object"""
cache_name = '_cache_cookie_' + cache_name
try:
return self.__dict__[cache_name].value
except KeyError:
self.__dict__[cache_name] = Cookie()
return Cookie.value
security.declareProtected(Permissions.ModifyPortalContent, 'newCacheCookie')
def newCacheCookie(self, cache_name):
"""Invalidate cache for this object"""
cache_name = '_cache_cookie_' + cache_name
try:
self.__dict__[cache_name].value += 1
except KeyError:
self.__dict__[cache_name] = Cookie()
class CacheFactory:
""" CacheFactory is a RAM based object which contains different cache plugin
objects ordered in a list.
......@@ -157,9 +205,20 @@ class CachingMethod:
## cache factories will be initialized for every ERP5 site
factories = {}
def _default_cache_id_generator(method_id, *args, **kw):
""" Generate proper cache id based on *args and **kw """
## generate cache id out of arguments passed.
## depending on arguments we may have different
## cache_id for same method_id
return str((method_id, args, kw))
@staticmethod
def erasable_cache_id_generator(method_id, obj, *args, **kw):
return str((method_id, obj.getCacheCookie(method_id), args, kw))
def __init__(self, callable_object, id, cache_duration=180,
cache_factory=DEFAULT_CACHE_FACTORY,
cache_id_generator=None):
cache_id_generator=_default_cache_id_generator):
"""Wrap a callable object in a caching method.
callable_object must be callable.
......@@ -177,7 +236,7 @@ class CachingMethod:
self.callable_object = callable_object
self.cache_duration = cache_duration
self.cache_factory = cache_factory
self.cache_id_generator = cache_id_generator
self.generateCacheId = cache_id_generator
def __call__(self, *args, **kwd):
"""Call the method or return cached value using appropriate cache plugin """
......@@ -220,16 +279,6 @@ class CachingMethod:
for cp in cache_factory.getCachePluginList():
cp.delete(cache_id, scope)
def generateCacheId(self, method_id, *args, **kwd):
""" Generate proper cache id based on *args and **kwd """
## generate cache id out of arguments passed.
## depending on arguments we may have different
## cache_id for same method_id
cache_id_generator = self.cache_id_generator
if cache_id_generator is not None:
return cache_id_generator(method_id, *args, **kwd)
return str((method_id, args, kwd))
allow_class(CachingMethod)
# TransactionCache is a cache per transaction. The purpose of this cache is
......@@ -274,16 +323,16 @@ def generateCacheIdWithoutFirstArg(method_id, *args, **kwd):
def caching_instance_method(*args, **kw):
kw.setdefault('cache_id_generator', generateCacheIdWithoutFirstArg)
@simple_decorator
def wrapped(method):
def decorator(function):
# The speed of returned function must be fast
# so we instanciate CachingMethod now.
caching_method = CachingMethod(method, *args, **kw)
caching_method = CachingMethod(function, *args, **kw)
# Here, we can't return caching_method directly because an instanciated
# class with a __call__ method does not behave exactly like a simple
# function: if the decorator is used to create a method, the instance on
# which the method is called would not be passed as first parameter.
return lambda *args, **kw: caching_method(*args, **kw)
return wrapped
return decorator
def transactional_cached(key_method=lambda *args: args):
@simple_decorator
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment