[z3-checkins] r23429 - in z3/sqlos/trunk/src/sqlos: . tests

jinty at codespeak.net jinty at codespeak.net
Thu Feb 16 23:50:33 CET 2006


Author: jinty
Date: Thu Feb 16 23:50:02 2006
New Revision: 23429

Modified:
   z3/sqlos/trunk/src/sqlos/configure.zcml
   z3/sqlos/trunk/src/sqlos/connection.py
   z3/sqlos/trunk/src/sqlos/tests/test_doctests.py
Log:
Implement per thread caching of connections and clean the cache every time we change sites.

Modified: z3/sqlos/trunk/src/sqlos/configure.zcml
==============================================================================
--- z3/sqlos/trunk/src/sqlos/configure.zcml	(original)
+++ z3/sqlos/trunk/src/sqlos/configure.zcml	Thu Feb 16 23:50:02 2006
@@ -149,4 +149,15 @@
         />
   </configure>
 
+  <subscriber
+      for="zope.app.component.interfaces.ISite
+           zope.app.publication.interfaces.IBeforeTraverseEvent"
+      handler=".connection.clearCacheSubscriber"
+      />
+
+  <subscriber
+      for="zope.app.publication.interfaces.IEndRequestEvent"
+      handler=".connection.clearCacheSubscriber"
+      />
+
 </configure>

Modified: z3/sqlos/trunk/src/sqlos/connection.py
==============================================================================
--- z3/sqlos/trunk/src/sqlos/connection.py	(original)
+++ z3/sqlos/trunk/src/sqlos/connection.py	Thu Feb 16 23:50:02 2006
@@ -16,9 +16,53 @@
 from zope.component import ComponentLookupError
 from zope.app import zapi
 from zope.app.rdb.interfaces import IZopeDatabaseAdapter
+from zope.thread import local
 
 from sqlos.interfaces import IZopeSQLConnection, IConnectionName
 
+class ConnectionCache(local):
+    """A per thread cache for adapted connections."""
+
+    def __init__(self):
+        self.connections = {}
+
+    def clear(self):
+        self.connections.clear()
+
+    def queryConnection(self, name):
+        return self.connections.get(name, None)
+
+    def setConnection(self, name, value):
+        self.connections[name] = value
+
+conn_cache = ConnectionCache()
+
+def clearCacheSubscriber(*args):
+    """A subscriber to clear the connection cache at site boundaries.
+
+    This subscriber serves a dual purpose. Firstly it makes sure that local
+    sites will work with the cache. Secondly it ensures that the cached
+    connections are not kept around forever, allowing zope.app.rdb to
+    recover from problems such as [1].
+
+    Subscribed to BeforeTraverseEvent and EndTraverseEvent.
+
+    [1] http://mail.zope.org/pipermail/zope3-dev/2005-December/017052.html
+
+    Lets just test it a little:
+
+        >>> conn_cache.clear()
+
+        >>> conn_cache.setConnection('a', 'a1')
+        >>> conn_cache.setConnection('b', 'b2')
+        >>> conn_cache.queryConnection('a')
+        'a1'
+        >>> clearCacheSubscriber('dummy')
+        >>> conn_cache.queryConnection('a') is None
+        True
+    """
+    conn_cache.clear()
+
 
 class SQLObjectWarning(UserWarning):
     pass
@@ -30,6 +74,7 @@
         self.name = name
 
     def __get__(self, inst, cls=None):
+        # get and cache the connection name
         name = self.name
         if name is None:
             try:
@@ -42,15 +87,16 @@
                               SQLObjectWarning, 2)
                 return
             name = ut.name
-        newconn = zapi.queryUtility(IZopeDatabaseAdapter, name)
-        if newconn is None:
-            warnings.warn("Couldn't find a rdb connection by the "
-                          "name %s. Please verify your setup." % name,
-                          SQLObjectWarning, 3)
-        try:
-            return IZopeSQLConnection(newconn())
-        except ComponentLookupError:
-            return self
+        # try get the connection from the cache, or make a new one
+        conn = conn_cache.queryConnection(name)
+        if conn is None:
+            zda = zapi.getUtility(IZopeDatabaseAdapter, name)
+            try:
+                conn = IZopeSQLConnection(zda())
+            except ComponentLookupError:
+                return self
+            conn_cache.setConnection(name, conn)
+        return conn
 
     def __set__(self, inst, value):
         # Ignore, so we don't get overriden.

Modified: z3/sqlos/trunk/src/sqlos/tests/test_doctests.py
==============================================================================
--- z3/sqlos/trunk/src/sqlos/tests/test_doctests.py	(original)
+++ z3/sqlos/trunk/src/sqlos/tests/test_doctests.py	Thu Feb 16 23:50:02 2006
@@ -10,6 +10,7 @@
 def test_suite():
     return unittest.TestSuite([
             DocTestSuite('sqlos.container', optionflags=doctest.ELLIPSIS),
+            DocTestSuite('sqlos.connection'),
             DocTestSuite('sqlos._transaction'),
             DocTestSuite('sqlos._sqlos')
             ])


More information about the z3-checkins mailing list