Commit 45686651 authored by Ivan Tyagov's avatar Ivan Tyagov Committed by Julien Muchembled

Introduce consistent set / get API amonst Cache Factory, Cache Bag and

different kinds of Cache Plugins.
This API change allows developer to create complex cache structures which
can contain arbitrary set of Cache Plugins and / or Cache Bags (Cache Factory).
Extend Cache Factory so it can return RAM based Cache Plugin instance by its Uid.
Fix tests to reflect changes.
parent e3f2a0a2
...@@ -200,6 +200,14 @@ class CacheFactory: ...@@ -200,6 +200,14 @@ class CacheFactory:
return cp return cp
return None return None
def getCachePluginByUid(self, uid):
""" get cache plugin by its Uid """
for cp in self.cache_plugins:
if uid == cp.uid:
return cp
return None
def clearCache(self): def clearCache(self):
""" clear cache for this cache factory """ """ clear cache for this cache factory """
for cp in self.cache_plugins: for cp in self.cache_plugins:
......
...@@ -94,10 +94,11 @@ class BaseCache(object): ...@@ -94,10 +94,11 @@ class BaseCache(object):
## Time interval (s) to check for expired objects ## Time interval (s) to check for expired objects
cache_expire_check_interval = 60 cache_expire_check_interval = 60
def __init__(self, params={}): def __init__(self, uid, params={}):
self._next_cache_expire_check_at = time() self._next_cache_expire_check_at = time()
self._cache_hit_count = 0 self._cache_hit_count = 0
self._cache_miss_count = 0 self._cache_miss_count = 0
self.uid = uid
def markCacheHit(self, delta=1): def markCacheHit(self, delta=1):
""" Mark a read operation from cache """ """ Mark a read operation from cache """
......
...@@ -55,14 +55,14 @@ class DistributedRamCache(BaseCache): ...@@ -55,14 +55,14 @@ class DistributedRamCache(BaseCache):
interfaces.ICachePlugin interfaces.ICachePlugin
) )
def __init__(self, params={}): def __init__(self, uid, params={}):
self._servers = params.get('server', '') self._servers = params.get('server', '')
self._expiration_time = params.get('expiration_time', 0) self._expiration_time = params.get('expiration_time', 0)
self._server_max_key_length = params.get('server_max_key_length', 250) self._server_max_key_length = params.get('server_max_key_length', 250)
self._server_max_value_length = params.get('server_max_value_length', 1024*1024) self._server_max_value_length = params.get('server_max_value_length', 1024*1024)
self._debug_level = params.get('debug_level', 0) self._debug_level = params.get('debug_level', 0)
self._key_prefix = params.get('key_prefix', '') self._key_prefix = params.get('key_prefix', '')
BaseCache.__init__(self) BaseCache.__init__(self, uid)
def initCacheStorage(self): def initCacheStorage(self):
""" Init cache storage """ """ Init cache storage """
......
...@@ -59,9 +59,9 @@ class RamCache(BaseCache): ...@@ -59,9 +59,9 @@ class RamCache(BaseCache):
cache_expire_check_interval = 300 cache_expire_check_interval = 300
def __init__(self, params={}): def __init__(self, uid, params={}):
self._cache_dict = {} self._cache_dict = {}
BaseCache.__init__(self) BaseCache.__init__(self, uid)
def initCacheStorage(self): def initCacheStorage(self):
""" Init cache storage """ """ Init cache storage """
......
...@@ -58,6 +58,7 @@ class CacheFactory(XMLObject): ...@@ -58,6 +58,7 @@ class CacheFactory(XMLObject):
, PropertySheet.SimpleItem , PropertySheet.SimpleItem
, PropertySheet.Folder , PropertySheet.Folder
, PropertySheet.CacheFactory , PropertySheet.CacheFactory
, PropertySheet.SortIndex
) )
def getCacheUid(self): def getCacheUid(self):
...@@ -71,9 +72,34 @@ class CacheFactory(XMLObject): ...@@ -71,9 +72,34 @@ class CacheFactory(XMLObject):
assert relative_url[:14] == 'portal_caches/' assert relative_url[:14] == 'portal_caches/'
return relative_url[14:] return relative_url[14:]
def getCachePluginList(self): security.declareProtected(Permissions.AccessContentsInformation, 'get')
def get(self, cache_id, default=None):
"""
Get value or return default from all contained Cache Bag
or Cache Plugin.
"""
cache_plugin_list = self.getCachePluginList(list(self.allowed_types) + ['ERP5 Cache Bag'])
for cache_plugin in cache_plugin_list:
value = cache_plugin.get(cache_id, default)
if value is not None:
return value
return default
security.declareProtected(Permissions.AccessContentsInformation, 'set')
def set(self, cache_id, value):
"""
Set value to all contained cache plugin or cache bag.
"""
cache_plugin_list = self.getCachePluginList(list(self.allowed_types) + ['ERP5 Cache Bag'])
for cache_plugin in cache_plugin_list:
cache_plugin.set(cache_id, value)
def getCachePluginList(self, allowed_types=None):
""" get ordered list of installed cache plugins in ZODB """ """ get ordered list of installed cache plugins in ZODB """
cache_plugins = self.objectValues(self.allowed_types) if allowed_types is None:
# fall back to default ones
allowed_types = self.allowed_types
cache_plugins = self.objectValues(allowed_types)
cache_plugins = map(None, cache_plugins) cache_plugins = map(None, cache_plugins)
cache_plugins.sort(key=lambda x: x.getIntIndex(0)) cache_plugins.sort(key=lambda x: x.getIntIndex(0))
return cache_plugins return cache_plugins
...@@ -99,5 +125,5 @@ class CacheFactory(XMLObject): ...@@ -99,5 +125,5 @@ class CacheFactory(XMLObject):
def clearCache(self): def clearCache(self):
""" clear cache for this cache factory """ """ clear cache for this cache factory """
for cp in self.getRamCacheFactory().getCachePluginList(): for cp in self.getRamCacheFactoryPluginList():
cp.clearCache() cp.clearCache()
...@@ -31,8 +31,9 @@ from AccessControl import ClassSecurityInfo ...@@ -31,8 +31,9 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type import PropertySheet from Products.ERP5Type import PropertySheet
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.mixin.cache_provider import CacheProviderMixIn
class DistributedRamCache(XMLObject): class DistributedRamCache(CacheProviderMixIn, XMLObject):
""" """
DistributedRamCache is a Zope (persistent) representation of DistributedRamCache is a Zope (persistent) representation of
the Distributed RAM Cache real cache plugin object. the Distributed RAM Cache real cache plugin object.
......
...@@ -32,8 +32,9 @@ from AccessControl import ClassSecurityInfo ...@@ -32,8 +32,9 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type import PropertySheet from Products.ERP5Type import PropertySheet
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.mixin.cache_provider import CacheProviderMixIn
class RamCache(XMLObject): class RamCache(CacheProviderMixIn, XMLObject):
""" """
RamCache is a Zope (persistent) representation of RamCache is a Zope (persistent) representation of
the RAM based real cache plugin object. the RAM based real cache plugin object.
......
...@@ -69,8 +69,9 @@ class CacheTool(BaseTool): ...@@ -69,8 +69,9 @@ class CacheTool(BaseTool):
def getRamCachePlugin(cp): def getRamCachePlugin(cp):
cp_meta_type = cp.meta_type cp_meta_type = cp.meta_type
uid = cp.getCacheUid()
if cp_meta_type == 'ERP5 Ram Cache': if cp_meta_type == 'ERP5 Ram Cache':
return RamCache() return RamCache(uid)
if cp_meta_type == 'ERP5 Distributed Ram Cache': if cp_meta_type == 'ERP5 Distributed Ram Cache':
## even thougn we have such plugin in ZODB that doens't mean ## even thougn we have such plugin in ZODB that doens't mean
## we have corresponding memcache module installed ## we have corresponding memcache module installed
...@@ -84,7 +85,7 @@ class CacheTool(BaseTool): ...@@ -84,7 +85,7 @@ class CacheTool(BaseTool):
'server_max_key_length': memcached_plugin.getServerMaxKeyLength(), 'server_max_key_length': memcached_plugin.getServerMaxKeyLength(),
'server_max_value_length': memcached_plugin.getServerMaxValueLength(), 'server_max_value_length': memcached_plugin.getServerMaxValueLength(),
'key_prefix': getattr(self, 'erp5_site_global_id', '')} 'key_prefix': getattr(self, 'erp5_site_global_id', '')}
return DistributedRamCache(init_dict) return DistributedRamCache(uid, init_dict)
rd = {} rd = {}
for cf in self.objectValues(['ERP5 Cache Factory', 'ERP5 Cache Bag']): for cf in self.objectValues(['ERP5 Cache Factory', 'ERP5 Cache Bag']):
......
...@@ -36,7 +36,7 @@ class ICachePlugin(Interface): ...@@ -36,7 +36,7 @@ class ICachePlugin(Interface):
"""CachePlugin Interface Specification """CachePlugin Interface Specification
""" """
def __init__(params={}): def __init__(uid, params={}):
"""Initialise default values """Initialise default values
""" """
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2012 Nexedi SA and Contributors. All Rights Reserved.
# Ivan Tyagov <ivan@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
class CacheProviderMixIn:
"""
Generic Cache Plugin set / get API implementation.
"""
security = ClassSecurityInfo()
def _getRamCachePlugin(self):
"""
Get RAM based cache plugin for this ZODB cache plugin.
"""
return self.getParentValue().getRamCacheFactory().getCachePluginByUid(self.getCacheUid())
security.declareProtected(Permissions.AccessContentsInformation, 'get')
def get(self, cache_id, default=None):
"""
Get value from cache plugin.
"""
cache_plugin = self._getRamCachePlugin()
value = cache_plugin.get(cache_id, DEFAULT_CACHE_SCOPE, default)
if value is not None:
value = value.getValue()
return value
security.declareProtected(Permissions.AccessContentsInformation, 'set')
def set(self, cache_id, value):
"""
Set value to cache plugin.
"""
cache_duration = self.getParentValue().getRamCacheFactory().cache_duration
cache_plugin = self._getRamCachePlugin()
cache_plugin.set(cache_id, DEFAULT_CACHE_SCOPE, value, cache_duration)
def getCacheUid(self):
"""
Get a common Cache Factory / Cache Bag UID in this
case relative to portal_caches.
It's required to use relative url (i.e. mainly ID) due
to CachingMethod legacy.
"""
relative_url = self.getRelativeUrl()
assert relative_url[:14] == 'portal_caches/'
return relative_url[14:]
...@@ -50,8 +50,9 @@ class TestRamCache(ERP5TypeTestCase): ...@@ -50,8 +50,9 @@ class TestRamCache(ERP5TypeTestCase):
return "Cache" return "Cache"
def afterSetUp(self): def afterSetUp(self):
self.cache_plugins = (RamCache(), self.cache_plugins = (RamCache('ram_cache'),
DistributedRamCache({'server': '127.0.0.1:11211', DistributedRamCache('distributed_ram_cache',
{'server': '127.0.0.1:11211',
'debug_level': 7, 'debug_level': 7,
'server_max_key_length': 250, 'server_max_key_length': 250,
'server_max_value_length': 1048576,}), 'server_max_value_length': 1048576,}),
......
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