[Zope-Checkins] CVS: Zope/lib/python/ZODB - Connection.py:1.79.6.1 DB.py:1.45.6.1 ExportImport.py:1.15.2.1 ZApplication.py:1.11.44.1

Shane Hathaway shane@zope.com
Tue, 3 Dec 2002 11:03:14 -0500


Update of /cvs-repository/Zope/lib/python/ZODB
In directory cvs.zope.org:/tmp/cvs-serv13538

Modified Files:
      Tag: shane-local-transactions-branch
	Connection.py DB.py ExportImport.py ZApplication.py 
Log Message:
Enabled decoupling of ZODB connections and transactions, a feature
requested often on the zodb-dev list.

The Connection class now has a method setLocalTransaction().
(Suggestions for a better method name accepted.)  Once your app calls
this, your app is expected to use connection.getTransaction() or
ob._p_jar.getTransaction() instead of the get_transaction() function
to commit or abort the transaction.  Doing this enables your
application to have multiple writable connections open in a single
thread, which is especially usefully in GUI applications.

If you don't call setLocalTransaction(), ZODB behaves as it always
has, with transactions bound to threads.

This feature is experimental, but the implementation seems about
right.  Unfortunately I had to change the CommitVersion, AbortVersion,
and TransactionalUndo classes so that instances don't automatically
register themselves with the transaction, but they should not have
been doing that in the first place, and hopefully these classes aren't
being used directly by applications.  Most apps will see no change.



=== Zope/lib/python/ZODB/Connection.py 1.79 => 1.79.6.1 ===
--- Zope/lib/python/ZODB/Connection.py:1.79	Mon Nov 18 18:17:40 2002
+++ Zope/lib/python/ZODB/Connection.py	Tue Dec  3 11:03:13 2002
@@ -22,6 +22,7 @@
 from zLOG import LOG, ERROR, BLATHER, WARNING
 from coptimizations import new_persistent_id
 from ConflictResolution import ResolvedSerial
+from Transaction import Transaction, get_transaction
 
 from cPickle import Unpickler, Pickler
 from cStringIO import StringIO
@@ -55,6 +56,7 @@
     _debug_info=()
     _opened=None
     _code_timestamp = 0
+    _transaction = None
 
     # Experimental. Other connections can register to be closed
     # when we close by putting something here.
@@ -80,6 +82,18 @@
         self._load_count = 0   # Number of objects unghosted
         self._store_count = 0  # Number of objects stored
 
+    def getTransaction(self):
+        t = self._transaction
+        if t is None:
+            # Fall back to thread-bound transactions
+            t = get_transaction()
+        return t
+
+    def setLocalTransaction(self):
+        if self._transaction is None:
+            self._transaction = Transaction()
+        return self._transaction
+
     def _cache_items(self):
         # find all items on the lru list
         items = self._cache.lru_items()
@@ -268,7 +282,7 @@
         if self.__onCommitActions is None:
             self.__onCommitActions = []
         self.__onCommitActions.append((method_name, args, kw))
-        get_transaction().register(self)
+        self.getTransaction().register(self)
 
     def commit(self, object, transaction):
         if object is self:
@@ -483,7 +497,7 @@
         assert object._p_jar is self
         # XXX Figure out why this assert causes test failures
         # assert object._p_oid is not None
-        get_transaction().register(object)
+        self.getTransaction().register(object)
 
     def root(self):
         return self['\0\0\0\0\0\0\0\0']
@@ -513,7 +527,7 @@
             # read.
             if self._invalid(oid):
                 if not hasattr(object.__class__, '_p_independent'):
-                    get_transaction().register(self)
+                    self.getTransaction().register(self)
                     raise ReadConflictError(object=object)
                 invalid = 1
             else:
@@ -541,7 +555,7 @@
                     except KeyError:
                         pass
                 else:
-                    get_transaction().register(self)
+                    self.getTransaction().register(self)
                     raise ConflictError(object=object)
 
         except ConflictError:
@@ -692,7 +706,7 @@
         self._db.finish_invalidation()
 
     def sync(self):
-        get_transaction().abort()
+        self.getTransaction().abort()
         sync=getattr(self._storage, 'sync', 0)
         if sync != 0: sync()
         self._cache.invalidate(self._invalidated)
@@ -723,7 +737,7 @@
         new._p_oid=oid
         new._p_jar=self
         new._p_changed=1
-        get_transaction().register(new)
+        self.getTransaction().register(new)
         self._cache[oid]=new
 
 class tConnection(Connection):


=== Zope/lib/python/ZODB/DB.py 1.45 => 1.45.6.1 ===
--- Zope/lib/python/ZODB/DB.py:1.45	Mon Nov 18 18:17:40 2002
+++ Zope/lib/python/ZODB/DB.py	Tue Dec  3 11:03:13 2002
@@ -153,8 +153,10 @@
                 self._temps=t
         finally: self._r()
 
-    def abortVersion(self, version):
-        AbortVersion(self, version)
+    def abortVersion(self, version, transaction=None):
+        if transaction is None:
+            transaction = get_transaction()
+        transaction.register(AbortVersion(self, version))
 
     def cacheDetail(self):
         """Return information on objects in the various caches
@@ -248,8 +250,10 @@
     def close(self):
         self._storage.close()
 
-    def commitVersion(self, source, destination=''):
-        CommitVersion(self, source, destination)
+    def commitVersion(self, source, destination='', transaction=None):
+        if transaction is None:
+            transaction = get_transaction()
+        transaction.register(CommitVersion(self, source, destination))
 
     def exportFile(self, oid, file=None):
         raise 'Not yet implemented'
@@ -545,7 +549,7 @@
 
     def cacheStatistics(self): return () # :(
 
-    def undo(self, id):
+    def undo(self, id, transaction=None):
         storage=self._storage
         try: supportsTransactionalUndo = storage.supportsTransactionalUndo
         except AttributeError:
@@ -555,7 +559,9 @@
 
         if supportsTransactionalUndo:
             # new style undo
-            TransactionalUndo(self, id)
+            if transaction is None:
+                transaction = get_transaction()
+            transaction.register(TransactionalUndo(self, id))
         else:
             # fall back to old undo
             for oid in storage.undo(id):
@@ -579,7 +585,6 @@
         self.tpc_vote=s.tpc_vote
         self.tpc_finish=s.tpc_finish
         self._sortKey=s.sortKey
-        get_transaction().register(self)
 
     def sortKey(self):
         return "%s:%s" % (self._sortKey(), id(self))


=== Zope/lib/python/ZODB/ExportImport.py 1.15 => 1.15.2.1 ===
--- Zope/lib/python/ZODB/ExportImport.py:1.15	Mon Dec  2 17:04:37 2002
+++ Zope/lib/python/ZODB/ExportImport.py	Tue Dec  3 11:03:13 2002
@@ -76,7 +76,7 @@
                 return customImporters[magic](self, file, clue)
             raise POSException.ExportError, 'Invalid export header'
 
-        t = get_transaction()
+        t = self.getTransaction()
         if clue: t.note(clue)
 
         return_oid_list = []


=== Zope/lib/python/ZODB/ZApplication.py 1.11 => 1.11.44.1 ===
--- Zope/lib/python/ZODB/ZApplication.py:1.11	Wed Aug 14 18:07:09 2002
+++ Zope/lib/python/ZODB/ZApplication.py	Tue Dec  3 11:03:13 2002
@@ -31,7 +31,7 @@
             root=conn.root()
             if not root.has_key(name):
                 root[name]=klass()
-                get_transaction().commit()
+                conn.getTransaction().commit()
             conn.close()
             self._klass=klass