[Zodb-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.