From 3b0c86e0097007808679a7180206bc42691887b3 Mon Sep 17 00:00:00 2001
From: Nicolas Delaby <nicolas@nexedi.com>
Date: Thu, 14 May 2009 14:33:06 +0000
Subject: [PATCH] Add ZODB Cache

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@26987 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5Type/CachePlugins/ZODBCache.py | 132 +++++++++++++++++++++
 1 file changed, 132 insertions(+)
 create mode 100644 product/ERP5Type/CachePlugins/ZODBCache.py

diff --git a/product/ERP5Type/CachePlugins/ZODBCache.py b/product/ERP5Type/CachePlugins/ZODBCache.py
new file mode 100644
index 0000000000..fc41b4f16a
--- /dev/null
+++ b/product/ERP5Type/CachePlugins/ZODBCache.py
@@ -0,0 +1,132 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
+#                     Nicolas Delaby <nicolas@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.
+#
+##############################################################################
+
+"""
+ZODB Based cache plugin.
+"""
+import time
+from BaseCache import BaseCache, CacheEntry
+from BTrees.OOBTree import OOBTree
+
+PRIVATE_ATTRIBUTE_ZODB_CACHE_NAME = '_zodb_cache'
+
+class ZODBCache(BaseCache):
+  """ ZODB based cache plugin."""
+
+  cache_tool = None
+  cache_expire_check_interval = 300
+
+  def __init__(self, params={}):
+    BaseCache.__init__(self)
+    self.cache_tool = params.get('cache_tool')
+
+  def initCacheStorage(self):
+    """Init cache storage
+    Btree is enough for less than 1 Million entries.
+    For higher scalability, wee need to implement cache with hierarchical Btree
+    with clever key generator
+    """
+    if getattr(self.cache_tool, PRIVATE_ATTRIBUTE_ZODB_CACHE_NAME, None) is None:
+      self.cache_tool._zodb_cache = OOBTree()
+
+  def getCacheStorage(self):
+    return getattr(self.cache_tool, PRIVATE_ATTRIBUTE_ZODB_CACHE_NAME)
+
+  def get(self, cache_id, scope, default=None):
+    cache = self.getCacheStorage()
+    try:
+      cache_entry = cache[(scope, cache_id)]
+      # Note: tracking down cache hit could be achieved by uncommenting
+      # methods below. In production environment this is likely uneeded
+      #cache_entry.markCacheHit()
+      #self.markCacheHit()
+      return cache_entry
+    except KeyError:
+      pass
+    return default
+
+  def set(self, cache_id, scope, value, cache_duration=None, calculation_time=0):
+    cache = self.getCacheStorage()
+    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._next_cache_expire_check_at):
+      ## time to check for expired cache items
+      self._next_cache_expire_check_at = now + self.cache_expire_check_interval
+      cache = self.getCacheStorage()
+      for key, value in cache.items():
+        if value.isExpired():
+          try:
+            del cache[key]
+          except KeyError:
+            # The key might have disappeared, due to multi-threading.
+            pass
+
+  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()
+    return cache.has_key((scope, cache_id))
+
+  def getScopeList(self):
+    scope_set = set()
+    for scope, cache_id in self.getCacheStorage().iterkeys():
+      scope_set.add(scope)
+    return list(scope_set)
+
+  def getScopeKeyList(self, scope):
+    key_list = []
+    for key in self.getCacheStorage().iterkeys():
+      if scope == key[0]:
+        key_list.append(key[1])
+    return key_list
+
+  def clearCache(self):
+    BaseCache.clearCache(self)
+    self.getCacheStorage().clear()
+
+  def clearCacheForScope(self, scope):
+    cache = self.getCacheStorage()
+    for key in cache.keys():
+      if key[0] == scope:
+        try:
+          del cache[key]
+        except KeyError:
+          # The key might have disappeared, due to multi-threading.
+          pass
+
+  def getCachePluginTotalMemorySize(self):
+    """Not Implemented"""
+    return 0
-- 
2.30.9