[Zope-Checkins] CVS: ZODB3/ZODB - Transaction.py:1.39.16.3

Jeremy Hylton jeremy@zope.com
Fri, 1 Nov 2002 15:43:15 -0500


Update of /cvs-repository/ZODB3/ZODB
In directory cvs.zope.org:/tmp/cvs-serv29426

Modified Files:
      Tag: ZODB3-deadlock-debug-branch
	Transaction.py 
Log Message:
Fix commit of top-level transaction and subtransaction commit.

Eliminate _commit_subtrans() and do its work in _commit_begin().  Make
sure that subjars are added to jars when the top-level transaction
commits.

# When the top-level subtransaction commits, the tm must
# call commit_sub() for each jar involved in one of the
# subtransactions.  The commit_sub() method should call
# tpc_begin() on the storage object.

# It must also call tpc_begin() on jars that were used in
# a subtransaction but don't support subtransactions.

# These operations must be performed on the jars in order.


=== ZODB3/ZODB/Transaction.py 1.39.16.2 => 1.39.16.3 ===
--- ZODB3/ZODB/Transaction.py:1.39.16.2	Fri Nov  1 14:34:37 2002
+++ ZODB3/ZODB/Transaction.py	Fri Nov  1 15:43:15 2002
@@ -26,6 +26,10 @@
 # Flag indicating whether certain errors have occurred.
 hosed=0
 
+# There is an order imposed on all jars, based on the storages they
+# serve, that must be consistent across all applications using the
+# storages.  The order is defined by the sortKey() method of the jar.
+
 def jar_cmp(j1, j2):
     # Call sortKey() every time, because a ZEO client could reconnect
     # to a different server at any time.
@@ -165,10 +169,11 @@
     def commit(self, subtransaction=None):
         'Finalize the transaction'
 
-        self.log("commit")
+        self.log("commit %s" % (subtransaction and "sub" or "top"))
 
         objects = self._objects
 
+        subjars = []
         if subtransaction:
             if self._sub is None:
                 # Must store state across multiple subtransactions
@@ -193,8 +198,6 @@
                 if self._non_st_objects is not None:
                     objects.extend(self._non_st_objects)
                     self._non_st_objects = None
-            else:
-                subjars = []
 
         if (objects or subjars) and hosed:
             # Something really bad happened and we don't
@@ -221,10 +224,9 @@
             ncommitted = 0
             jars = self._get_jars(objects, subtransaction)
             try:
-                self._commit_begin(jars, subtransaction)
+                # If not subtransaction, then jars will be modified.
+                self._commit_begin(jars, subjars, subtransaction)
                 ncommitted += self._commit_objects(objects)
-                if not subtransaction and subjars:
-                    self._commit_subtrans(subjars)
                 if not subtransaction:
                     # Unless this is a really old jar that doesn't
                     # implement tpc_vote(), it must raise an exception
@@ -281,6 +283,7 @@
 
             if subtransaction:
                 if hasattr(jar, "commit_sub"):
+                    self.log("found new subjar: %r" % jar)
                     self._sub[key] = jar
                 else:
                     if self._non_st_objects is None:
@@ -292,9 +295,10 @@
 
         return jars
 
-    def _commit_begin(self, jars, subtransaction):
-        for jar in jars:
-            if subtransaction:
+    def _commit_begin(self, jars, subjars, subtransaction):
+        if subtransaction:
+            assert not subjars
+            for jar in jars:
                 try:
                     jar.tpc_begin(self, subtransaction)
                 except TypeError:
@@ -302,9 +306,31 @@
                     # takes one argument, and that the jar doesn't
                     # support subtransactions.
                     jar.tpc_begin(self)
-            else:
-                self.log("tpc_begin %s" % jar)
-                jar.tpc_begin(self)
+        else:
+            # Merge in all the jars used by one of the subtransactions.
+
+            # When the top-level subtransaction commits, the tm must
+            # call commit_sub() for each jar involved in one of the
+            # subtransactions.  The commit_sub() method should call
+            # tpc_begin() on the storage object.
+
+            # It must also call tpc_begin() on jars that were used in
+            # a subtransaction but don't support subtransactions.
+
+            # These operations must be performed on the jars in order.
+
+            # Modify jars inplace to include the subjars, too.
+            jars += subjars
+            jars.sort(jar_cmp)
+            # assume that subjars is small, so that it's cheaper to test
+            # whether jar in subjars than to make a dict and do has_key.
+            for jar in jars:
+                if jar in subjars:
+                    self.log("commit_sub %s" % jar, BLATHER)
+                    jar.commit_sub(self)
+                else:
+                    self.log("tpc_begin %s" % jar, BLATHER)
+                    jar.tpc_begin(self)
 
     def _commit_objects(self, objects):
         ncommitted = 0
@@ -316,11 +342,6 @@
             ncommitted += 1
         return ncommitted
 
-    def _commit_subtrans(self, subjars):
-        # Commit work done in subtransactions
-        for jar in subjars:
-            jar.commit_sub(self)
-
     def _finish_one(self, jar):
         try:
             # The database can't guarantee consistency if call fails.
@@ -350,6 +371,8 @@
             raise
 
     def _commit_error(self, objects, ncommitted, jars, subjars):
+        self.log("_commit_errors:\n\tn=%d objects=%r\t\njars=%r\n"
+                 "\tsubjars=%r\n" % (ncommitted, objects, jars, subjars))
         # First, we have to abort any uncommitted objects.  The abort
         # will mark the object for invalidation, so that it's last
         # committed state will be restored.