[Erp5-report] r11034 - /erp5/trunk/products/ERP5Type/CachePlugins/

nobody at svn.erp5.org nobody at svn.erp5.org
Wed Nov 1 12:25:24 CET 2006


Author: ivan
Date: Wed Nov  1 12:25:16 2006
New Revision: 11034

URL: http://svn.erp5.org?rev=11034&view=rev
Log:
Initial import of Cache Plugins.

Added:
    erp5/trunk/products/ERP5Type/CachePlugins/
    erp5/trunk/products/ERP5Type/CachePlugins/BaseCache.py
    erp5/trunk/products/ERP5Type/CachePlugins/DistributedRamCache.py
    erp5/trunk/products/ERP5Type/CachePlugins/DummyCache.py
    erp5/trunk/products/ERP5Type/CachePlugins/RamCache.py
    erp5/trunk/products/ERP5Type/CachePlugins/SQLCache.py
    erp5/trunk/products/ERP5Type/CachePlugins/__init__.py

Added: erp5/trunk/products/ERP5Type/CachePlugins/BaseCache.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/CachePlugins/BaseCache.py?rev=11034&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/CachePlugins/BaseCache.py (added)
+++ erp5/trunk/products/ERP5Type/CachePlugins/BaseCache.py Wed Nov  1 12:25:16 2006
@@ -1,0 +1,107 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                     Ivan Tyagov <ivan at 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.
+#
+##############################################################################
+
+""" 
+Base Cache plugin.
+"""
+
+import time
+
+class CachedMethodError(Exception): 
+  pass
+
+class CacheEntry(object):
+  """ Cachable entry.  Used as a wrapper around real values stored in cache.
+  value
+  cache_duration
+  stored_at
+  cache_hits
+  calculation_time
+  TODO: Based on above data we can have a different invalidation policy
+  """
+    
+  def __init__(self, value, cache_duration=None, calculation_time=0):
+    self.value = value
+    self.cache_duration = cache_duration
+    self.stored_at = int(time.time())
+    self.cache_hits = 0
+    self.calculation_time = calculation_time
+
+       
+  def isExpired(self):
+    """ check cache entry for expiration """
+    if self.cache_duration is None or self.cache_duration==0:
+      ## cache entry can stay in cache forever until zope restarts
+      return False
+    now = int(time.time())
+    if now > (self.stored_at + int(self.cache_duration)):
+      return True
+    else:
+      return False
+    
+  def markCacheHit(self, delta=1):
+    """ mark a read to this cache entry """
+    self.cache_hits = self.cache_hits + delta 
+    
+  def getValue(self):
+    """ return cached value """ 
+    return getattr(self, 'value', None)
+
+
+class BaseCache(object):
+  """ Base Cache class """
+    
+    
+  ## Time interval (s) to check for expired objects
+  cache_expire_check_interval = 60
+    
+  def __init__(self, params={}):
+    self._last_cache_expire_check_at = time.time()
+    self._cache_hits = 0
+    self._cache_misses = 0
+        
+  def markCacheHit(self, delta=1):
+    """ Mark a read operation from cache """
+    self._cache_hits = self._cache_hits + delta
+
+  def markCacheMiss(self, delta=1):
+    """ Mark a write operation to cache """
+    self._cache_misses = self._cache_misses + delta
+
+  def getCacheHits(self):
+    """ get cache hits """
+    return self._cache_hits
+
+  def getCacheMisses(self):
+    """ get cache missess """
+    return self._cache_misses
+    
+  def clearCache(self):
+    """ Clear cache """
+    self._cache_hits = 0
+    self._cache_misses = 0

Added: erp5/trunk/products/ERP5Type/CachePlugins/DistributedRamCache.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/CachePlugins/DistributedRamCache.py?rev=11034&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/CachePlugins/DistributedRamCache.py (added)
+++ erp5/trunk/products/ERP5Type/CachePlugins/DistributedRamCache.py Wed Nov  1 12:25:16 2006
@@ -1,0 +1,144 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                     Ivan Tyagov <ivan at 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.
+#
+##############################################################################
+
+"""
+Memcached based cache plugin.
+"""
+
+from BaseCache import *
+from time import time
+
+try:
+  import memcache
+except ImportError:
+  raise CachedMethodError, "Memcache module is not available"
+
+MEMCACHED_SERVER_MAX_KEY_LENGTH = memcache.SERVER_MAX_KEY_LENGTH
+## number of seconds before creating a new connection to memcached server
+##KEEP_ALIVE_MEMCACHED_CONNECTION_INTERVAL = 30  
+ 
+class DistributedRamCache(BaseCache):
+  """ Memcached based cache plugin. """
+
+  def __init__(self, params):
+    self._servers = params.get('server', '')
+    self._debugLevel = params.get('debugLevel', 7)
+    self._last_cache_conn_creation_time = time()
+    BaseCache.__init__(self)
+        
+  def getCacheStorage(self):
+    ## if we use one connection object this causes  "MemCached: while expecting 'STORED', got unexpected response 'END'"
+    ## messages in log files and thus sometimes can block the thread. For the moment we create
+    ## a new conn object for every memcache access which in turns means another socket. 
+    ## See  addiionaly expireOldCacheEntries() comments for one or many connections.
+    try:
+      from Products.ERP5Type.Utils import get_request
+      request = get_request()
+    except ImportError:
+      request = None
+      
+    if request:
+      ## Zope/ERP5 environment
+      memcache_conn = request.get('_erp5_memcache_connection', None)
+      if not memcache_conn:
+        ## we have not memcache_conn for this request
+        memcache_conn = memcache.Client(self._servers.split('\n'), debug=self._debugLevel)
+        request.set('_erp5_memcache_connection', memcache_conn)
+        return memcache_conn      
+      else:
+        ## we have memcache_conn for this request
+        return memcache_conn
+    else:
+      ## run from unit tests
+      return  memcache.Client(self._servers.split('\n'), debug=self._debugLevel)
+       
+  def checkAndFixCacheId(self, cache_id, scope):
+    ## memcached doesn't support namespaces (cache scopes) so to "emmulate"
+    ## such behaviour when constructing cache_id we add scope in front
+    cache_id = "%s.%s" %(scope, cache_id) 
+    ## memcached will fail to store cache_id longer than MEMCACHED_SERVER_MAX_KEY_LENGTH.
+    if len(cache_id) > MEMCACHED_SERVER_MAX_KEY_LENGTH:
+      cache_id = cache_id[:MEMCACHED_SERVER_MAX_KEY_LENGTH]
+    return cache_id
+    
+  def get(self, cache_id, scope, default=None):
+    cache_storage = self.getCacheStorage()
+    cache_id = self.checkAndFixCacheId(cache_id, scope)
+    cache_entry = cache_storage.get(cache_id)
+    self.markCacheHit()
+    return cache_entry
+       
+  def set(self, cache_id, scope, value, cache_duration= None, calculation_time=0):
+    cache_storage = self.getCacheStorage()
+    cache_id = self.checkAndFixCacheId(cache_id, scope)
+    if not cache_duration:
+      ## what should be default cache_duration when None is specified?
+      ## currently when 'None' it means forever so give it  big value of 100 hours
+      cache_duration = 360000
+    cache_entry = CacheEntry(value, cache_duration, calculation_time)
+    cache_storage.set(cache_id, cache_entry, cache_duration)
+    self.markCacheMiss()
+   
+  def expireOldCacheEntries(self, forceCheck = False):
+    """ Memcache has its own built in expire policy """
+    ## we can not use one connection to memcached server for time being of DistributedRamCache
+    ## because if memcached is restarted for any reason our connection object will have its socket 
+    ## to memcached server closed.
+    ## The workaround of this problem is to create a new connection for every cache access
+    ## but that's too much overhead or create a new connection when cache is to be expired. 
+    ## This way we can catch memcached server failures. BTW: This hack is forced by the lack functionality in python-memcached 
+    #self._cache = memcache.Client(self._servers.split('\n'), debug=self._debugLevel) 
+    pass    
+        
+  def delete(self, cache_id, scope):
+    cache_storage = self.getCacheStorage()
+    cache_id = self.checkAndFixCacheId(cache_id, scope)
+    cache_storage.delete(cache_id)
+        
+  def has_key(self, cache_id, scope):
+    if self.get(cache_id, scope):
+      return True
+    else:
+      return False
+
+  def getScopeList(self):
+    ## memcached doesn't support namespaces (cache scopes) neither getting cached key list 
+    return []
+    
+  def getScopeKeyList(self, scope):
+    ## memcached doesn't support namespaces (cache scopes) neither getting cached key list 
+    return []
+
+  def clearCache(self):
+    BaseCache.clearCache(self)
+    cache_storage = self.getCacheStorage()
+    cache_storage.flush_all()
+    
+  def clearCacheForScope(self, scope):
+    ## memcached doesn't support namespaces (cache scopes) neither getting cached key list 
+    pass       

Added: erp5/trunk/products/ERP5Type/CachePlugins/DummyCache.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/CachePlugins/DummyCache.py?rev=11034&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/CachePlugins/DummyCache.py (added)
+++ erp5/trunk/products/ERP5Type/CachePlugins/DummyCache.py Wed Nov  1 12:25:16 2006
@@ -1,0 +1,73 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                     Ivan Tyagov <ivan at 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.
+#
+##############################################################################
+
+"Dummy (no cache)"
+
+from BaseCache import *
+import time
+
+class DummyCache(BaseCache):
+  """ Dummy cache plugin. """
+    
+  def __init__(self, params):
+    BaseCache.__init__(self)
+ 
+  def __call__(self, callable_object, cache_id, cache_duration=None, *args, **kwd):
+    ## Just calculate and return result - no caching 
+    return callable_object(*args, **kwd)
+        
+  def getCacheStorage(self):
+    pass
+    
+  def get(self, cache_id, scope, default=None):
+    pass
+       
+  def set(self, cache_id, scope, value, cache_duration= None, calculation_time=0):
+    pass
+
+  def expireOldCacheEntries(self, forceCheck = False):
+    pass
+        
+  def delete(self, cache_id, scope):
+    pass
+        
+  def has_key(self, cache_id, scope):
+    pass
+        
+  def getScopeList(self):
+    pass
+        
+  def getScopeKeyList(self, scope):
+    pass
+        
+  def clearCache(self):
+    pass
+        
+  def clearCacheForScope(self, scope):
+    pass
+    

Added: erp5/trunk/products/ERP5Type/CachePlugins/RamCache.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/CachePlugins/RamCache.py?rev=11034&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/CachePlugins/RamCache.py (added)
+++ erp5/trunk/products/ERP5Type/CachePlugins/RamCache.py Wed Nov  1 12:25:16 2006
@@ -1,0 +1,113 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                     Ivan Tyagov <ivan at 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.
+#
+##############################################################################
+
+"""
+Local RAM based cache plugin.
+"""
+
+
+from BaseCache import *
+import time
+
+class RamCache(BaseCache):
+  """ RAM based cache plugin."""
+   
+  _cache_dict = {}
+  cache_expire_check_interval = 300
+    
+  def __init__(self, params={}):
+    BaseCache.__init__(self)
+            
+  def getCacheStorage(self):
+    return self._cache_dict
+    
+  def get(self, cache_id, scope, default=None):
+    cache = self.getCacheStorage()
+    if self.has_key(cache_id, scope):
+      cache_entry = cache[scope].get(cache_id, default)
+      cache_entry.markCacheHit()
+      self.markCacheHit()
+      return cache_entry
+    else:
+      return default
+            
+  def set(self, cache_id, scope, value, cache_duration=None, calculation_time=0):
+    cache = self.getCacheStorage()
+    if not cache.has_key(scope):
+      ## cache scope not initialized
+      cache[scope] = {}
+    cache[scope][cache_id] = CacheEntry(value, cache_duration, calculation_time)
+    self.markCacheMiss()
+
+  def expireOldCacheEntries(self, forceCheck = False):
+    now = time.time()
+    if forceCheck or (now > (self._last_cache_expire_check_at + self.cache_expire_check_interval)):
+      ## time to check for expired cache items
+      #print "EXPIRE ", self, self.cache_expire_check_interval
+      self._last_cache_expire_check_at = now
+      cache = self.getCacheStorage()        
+      for scope in cache.keys():
+        for (cache_id, cache_item) in cache[scope].items():
+          if cache_item.isExpired()==True:
+            del cache[scope][cache_id]
+
+  def delete(self, cache_id, scope):
+    try:
+      del self.getCacheStorage()[scope][cache_id]
+    except KeyError:
+      pass
+
+  def has_key(self, cache_id, scope):
+    cache = self.getCacheStorage()
+    if not cache.has_key(scope):
+      ## cache scope not initialized
+      cache[scope] = {}
+    return cache[scope].has_key(cache_id)
+
+  def getScopeList(self):
+    scope_list = []
+    ## some cache scopes in RAM Cache can have no cache_ids keys but 
+    ## they do exists. To have consistent behaviour with SQLCache plugin 
+    ## where cache scope will not exists without its cache_ids we filter them.  
+    for scope, item in self.getCacheStorage().items():
+      if item!={}:
+        scope_list.append(scope)
+    return scope_list
+    
+  def getScopeKeyList(self, scope):
+    return self.getCacheStorage()[scope].keys()
+    
+  def clearCache(self):
+    BaseCache.clearCache(self)
+    self._cache_dict = {} 
+
+  def clearCacheForScope(self, scope):
+    try:
+      self.getCacheStorage()[scope] = {}
+    except KeyError:
+      pass

Added: erp5/trunk/products/ERP5Type/CachePlugins/SQLCache.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/CachePlugins/SQLCache.py?rev=11034&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/CachePlugins/SQLCache.py (added)
+++ erp5/trunk/products/ERP5Type/CachePlugins/SQLCache.py Wed Nov  1 12:25:16 2006
@@ -1,0 +1,253 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                     Ivan Tyagov <ivan at 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.
+#
+##############################################################################
+
+"""
+SQL (MySQL) based cache plugin.
+"""
+
+from BaseCache import *
+import time, base64
+
+try:
+  import cPickle as pickle
+except ImportError:
+  import pickle
+    
+try:
+  import MySQLdb
+except ImportError:
+  raise CachedMethodError, "MySQLdb module is not available"
+
+class SQLCache(BaseCache):
+  """ SQL based cache plugin. """
+    
+  cache_expire_check_interval = 3600
+    
+  create_table_sql = '''CREATE TABLE %s(cache_id VARCHAR(970) NOT NULL, 
+                                         value LONGTEXT,
+                                         scope VARCHAR(20),
+                                         stored_at INT,
+                                         cache_duration INT DEFAULT 0,
+                                         calculation_time FLOAT,
+                                         UNIQUE(cache_id, scope))
+                      '''
+
+  insert_key_sql = '''INSERT INTO %s (cache_id, value, scope, stored_at, cache_duration, calculation_time) 
+                              VALUES("%s", "%s", "%s", %s, %s, %s)
+                    '''
+                            
+  has_key_sql = '''SELECT count(*)
+                    FROM %s
+                    WHERE cache_id = "%s" and scope="%s"
+                    '''
+                      
+  get_key_sql = '''SELECT value, cache_duration, calculation_time 
+                    FROM %s
+                    WHERE cache_id = "%s" and scope="%s"
+                    '''
+                            
+  delete_key_sql = '''DELETE
+                       FROM %s
+                       WHERE cache_id = "%s" and scope="%s"
+                    '''
+    
+  delete_all_keys_sql = '''DELETE 
+                            FROM %s
+                         '''
+    
+  delete_all_keys_for_scope_sql = '''DELETE 
+                                      FROM %s 
+                                      WHERE scope="%s"
+                                    '''
+        
+  delete_expired_keys_sql = '''DELETE 
+                                FROM %s
+                                WHERE cache_duration + stored_at < %s and cache_duration!=0
+                              '''
+  
+  get_scope_list_sql = '''SELECT scope 
+                           FROM %s 
+                           GROUP BY scope
+                        '''
+                           
+  get_scope_key_list_sql = '''SELECT cache_id 
+                                FROM %s 
+                                WHERE scope="%s"
+                            '''
+
+  def __init__(self, params):
+    BaseCache.__init__(self)
+    self._dbConn = None
+    self._db_server = params.get('server', '')
+    self._db_user = params.get('user', '')
+    self._db_passwd = params.get('passwd', '')
+    self._db_name = params.get('db', '')
+    self._db_cache_table_name = params.get('cache_table_name')
+    
+    ## since SQL cache is persistent check for expired objects
+    #self.expireOldCacheEntries(forceCheck=True)
+    
+  def getCacheStorage(self):
+    """ 
+    Return current DB connection or create a new one for his thread.
+    See http://sourceforge.net/docman/display_doc.php?docid=32071&group_id=22307
+    especially threadsafety part why we create every time a new MySQL db connection object.
+    """
+    try:
+      from Products.ERP5Type.Utils import get_request
+      request = get_request()
+    except ImportError:
+      request = None
+      
+    if request:
+      ## Zope/ERP5 environment
+      dbConn = request.get('_erp5_dbcache_connection', None)
+      if not dbConn:
+        ## we have not dbConn for this request
+        dbConn = MySQLdb.connect(host=self._db_server, \
+                                 user=self._db_user,\
+                                 passwd=self._db_passwd, \
+                                 db=self._db_name)
+        request.set('_erp5_dbcache_connection', dbConn)
+        return dbConn      
+      else:
+        ## we have already dbConn for this request
+        return dbConn
+    else:
+      ## run from unit tests
+      dbConn = MySQLdb.connect(host=self._db_server, \
+                             user=self._db_user,\
+                             passwd=self._db_passwd, \
+                             db=self._db_name)
+      return dbConn
+    
+  def get(self, cache_id, scope, default=None):
+    sql_query = self.get_key_sql %(self._db_cache_table_name, cache_id, scope)
+    cursor =  self.execSQLQuery(sql_query)
+    if cursor:
+      ## count return one row only
+      result = cursor.fetchall()
+      if 0 < len(result):
+        ## we found results
+        result = result[0]
+        decoded_result = pickle.loads(base64.decodestring(result[0]))
+        self.markCacheHit()
+        cache_entry = CacheEntry(decoded_result, result[1], result[2])
+        return cache_entry
+      else:
+        ## no such cache_id in DB
+        return None
+    else:
+      ## DB not available
+      return None
+        
+  def set(self, cache_id, scope, value, cache_duration=None, calculation_time=0):
+    value = base64.encodestring(pickle.dumps(value,2))
+    if not cache_duration:
+      ## should live forever ==> setting cache_duration = 0 will make it live forever
+      cache_duration = 0
+    else:
+      ## we have strict cache_duration defined. we calculate seconds since start of epoch
+      cache_duration = int(cache_duration)
+    ## Set key in DB
+    stored_at = int(time.time())
+    sql_query = self.insert_key_sql %(self._db_cache_table_name, cache_id, value, scope, stored_at, cache_duration, calculation_time)
+    self.execSQLQuery(sql_query)
+    self.markCacheMiss()
+            
+  def expireOldCacheEntries(self, forceCheck = False):
+    now = time.time()
+    if forceCheck or (now > (self._last_cache_expire_check_at + self.cache_expire_check_interval)):
+      ## time to check for expired cache items
+      self._last_cache_expire_check_at = now
+      my_query = self.delete_expired_keys_sql %(self._db_cache_table_name, now)
+      self.execSQLQuery(my_query)
+
+  def delete(self, cache_id, scope):
+    my_query = self.delete_key_sql %(self._db_cache_table_name, cache_id, scope)
+    self.execSQLQuery(my_query)
+    
+  def has_key(self, cache_id, scope):
+    my_query = self.has_key_sql %(self._db_cache_table_name, cache_id, scope)
+    cursor =  self.execSQLQuery(my_query)
+    if cursor:
+      ## count() SQL function will return one row only
+      result = cursor.fetchall()
+      result = result[0][0] 
+      if result == 0:
+        ## no such key in DB
+        return False
+      elif result==1:
+        ## we have this key in DB
+        return True
+      else:
+        ## something wrong in DB model
+        raise CachedMethodError, "Invalid cache table reltion format. cache_id MUST be unique!"
+    else:
+      ## DB not available
+      return False
+
+  def getScopeList(self):
+    rl = []
+    my_query = self.get_scope_list_sql %(self._db_cache_table_name)
+    cursor =  self.execSQLQuery(my_query)
+    results = cursor.fetchall() 
+    for result in results:
+        rl.append(result[0])
+    return rl
+    
+  def getScopeKeyList(self, scope):
+    rl = []
+    my_query = self.get_scope_key_list_sql %(self._db_cache_table_name, scope)
+    cursor =  self.execSQLQuery(my_query)
+    results = cursor.fetchall() 
+    for result in results:
+        rl.append(result[0])
+    return rl
+    
+  def clearCache(self):
+    BaseCache.clearCache(self)
+    ## SQL Cache is a persistent storage rather than delete all entries
+    ## just expire them 
+    ## self.expireOldCacheEntries(forceCheck = True):
+    my_query = self.delete_all_keys_sql  %(self._db_cache_table_name)
+    self.execSQLQuery(my_query)
+    
+  def clearCacheForScope(self, scope):
+    my_query = self.delete_all_keys_for_scope_sql  %(self._db_cache_table_name, scope)
+    self.execSQLQuery(my_query)
+
+  def execSQLQuery(self, sql_query):
+    """ 
+    Try to execute sql query.
+    Return cursor object because some queris can return result
+    """
+    dbConn = self.getCacheStorage()
+    cursor = dbConn.cursor()
+    cursor.execute(sql_query)
+    return cursor

Added: erp5/trunk/products/ERP5Type/CachePlugins/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/CachePlugins/__init__.py?rev=11034&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/CachePlugins/__init__.py (added)
+++ erp5/trunk/products/ERP5Type/CachePlugins/__init__.py Wed Nov  1 12:25:16 2006
@@ -1,0 +1,3 @@
+"""
+Cache plugin classes.
+"""




More information about the Erp5-report mailing list