[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/rdb/ Store database
connections in a thread local instead of a volatile attribute.
Stuart Bishop
stuart at stuartbishop.net
Wed Jun 8 03:34:58 EDT 2005
Log message for revision 30682:
Store database connections in a thread local instead of a volatile attribute.
This ensures that database adapters created using rdb:provideConnection
do not share their connections between threads, giving the same behavior
as these database adapters when they happen to be stored in the ZODB.
Changed:
U Zope3/trunk/src/zope/app/rdb/__init__.py
U Zope3/trunk/src/zope/app/rdb/tests/test_zopedatabaseadapter.py
-=-
Modified: Zope3/trunk/src/zope/app/rdb/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/rdb/__init__.py 2005-06-08 07:30:35 UTC (rev 30681)
+++ Zope3/trunk/src/zope/app/rdb/__init__.py 2005-06-08 07:34:56 UTC (rev 30682)
@@ -19,7 +19,7 @@
$Id$
"""
-import types, string
+import types, string, time, random, thread
from types import StringTypes
from urllib import unquote_plus
@@ -39,6 +39,7 @@
from zope.app.rdb.interfaces import ISQLCommand
from zope.app.rdb.interfaces import IManageableZopeDatabaseAdapter
from zope.app.rdb.interfaces import IZopeDatabaseAdapter
+from zope.thread import local
def sqlquote(x):
@@ -109,11 +110,31 @@
class ZopeDatabaseAdapter(Persistent, Contained):
implements(IManageableZopeDatabaseAdapter)
- _v_connection = None
+ # We need to store our connections in a thread local to ensure that
+ # different threads do not accidently use the same connection. This
+ # is important when instantiating database adapters using
+ # rdb:provideConnection as the same ZopeDatabaseAdapter instance will
+ # be used by all threads.
+ _connections = local()
+
def __init__(self, dsn):
self.setDSN(dsn)
+ self._unique_id = '%s.%s.%s' % (
+ time.time(), random.random(), thread.get_ident()
+ )
+ def _get_v_connection(self):
+ """We used to store the ZopeConnection in a volatile attribute.
+ However this was not always thread safe.
+ """
+ return getattr(ZopeDatabaseAdapter._connections, self._unique_id, None)
+
+ def _set_v_connection(self, value):
+ setattr(ZopeDatabaseAdapter._connections, self._unique_id, value)
+
+ _v_connection = property(_get_v_connection, _set_v_connection)
+
def _connection_factory(self):
"""This method should be overwritten by all subclasses"""
conn_info = parseDSN(self.dsn)
@@ -137,15 +158,13 @@
except Exception, error:
raise DatabaseException, str(error)
-
def disconnect(self):
if self.isConnected():
self._v_connection.close()
self._v_connection = None
def isConnected(self):
- return hasattr(self, '_v_connection') and \
- self._v_connection is not None
+ return self._v_connection is not None
def __call__(self):
self.connect()
Modified: Zope3/trunk/src/zope/app/rdb/tests/test_zopedatabaseadapter.py
===================================================================
--- Zope3/trunk/src/zope/app/rdb/tests/test_zopedatabaseadapter.py 2005-06-08 07:30:35 UTC (rev 30681)
+++ Zope3/trunk/src/zope/app/rdb/tests/test_zopedatabaseadapter.py 2005-06-08 07:34:56 UTC (rev 30682)
@@ -18,6 +18,7 @@
import unittest
from zope.app.rdb import ZopeDatabaseAdapter
from zope.app.rdb import ZopeConnection
+from threading import Thread
class ConnectionStub(object):
@@ -51,6 +52,9 @@
da = self._da
da.disconnect()
self.assertEqual(None, da._v_connection)
+ da.connect()
+ da.disconnect()
+ self.assertEqual(None, da._v_connection)
def testIsConnected(self):
da = self._da
@@ -70,7 +74,20 @@
conv = da.getConverter('any')
self.assert_(conv is identity, "default converter is wrong")
+ def testThreading(self):
+ # Ensure that different threads get distinct connections
+ cons = []
+ def get_con():
+ self._da.connect()
+ cons.append(self._da._v_connection)
+ get_con()
+ t = Thread(target=get_con)
+ t.start()
+ t.join()
+ self.failUnlessEqual(len(cons), 2)
+ self.failIfEqual(id(cons[0]), id(cons[1]))
+
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestZopeDatabaseAdapter))
More information about the Zope3-Checkins
mailing list