[Zodb-checkins] SVN: ZODB/trunk/src/ZODB/ Fixed multiple undo in a transaction.

Shane Hathaway shane at zope.com
Fri Feb 3 01:07:48 EST 2006


Log message for revision 41550:
  Fixed multiple undo in a transaction.
  
  For multiple undo to succeed, the undo operations have to be
  performed in a specific order.  The order was not being retained
  by ZODB and was instead semi-random (the order depended on the
  id() function.)  Now undo operations (as well as all
  ResourceManagers) are sorted by creation order.
  
  

Changed:
  U   ZODB/trunk/src/ZODB/DB.py
  U   ZODB/trunk/src/ZODB/tests/testZODB.py

-=-
Modified: ZODB/trunk/src/ZODB/DB.py
===================================================================
--- ZODB/trunk/src/ZODB/DB.py	2006-02-03 00:50:51 UTC (rev 41549)
+++ ZODB/trunk/src/ZODB/DB.py	2006-02-03 06:07:47 UTC (rev 41550)
@@ -679,6 +679,9 @@
     def versionEmpty(self, version):
         return self._storage.versionEmpty(version)
 
+resource_counter_lock = threading.Lock()
+resource_counter = 0
+
 class ResourceManager(object):
     """Transaction participation for a version or undo resource."""
 
@@ -689,8 +692,20 @@
         self.tpc_finish = self._db._storage.tpc_finish
         self.tpc_abort = self._db._storage.tpc_abort
 
+        # Get a number from a simple thread-safe counter, then
+        # increment it, for the purpose of sorting ResourceManagers by
+        # creation order.  This ensures that multiple ResourceManagers
+        # within a transaction commit in a predictable sequence.
+        resource_counter_lock.acquire()
+        try:
+            global resource_counter
+            self._count = resource_counter
+            resource_counter += 1
+        finally:
+            resource_counter_lock.release()
+
     def sortKey(self):
-        return "%s:%s" % (self._db._storage.sortKey(), id(self))
+        return "%s:%016x" % (self._db._storage.sortKey(), self._count)
 
     def tpc_begin(self, txn, sub=False):
         if sub:

Modified: ZODB/trunk/src/ZODB/tests/testZODB.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/testZODB.py	2006-02-03 00:50:51 UTC (rev 41549)
+++ ZODB/trunk/src/ZODB/tests/testZODB.py	2006-02-03 06:07:47 UTC (rev 41550)
@@ -728,6 +728,42 @@
         cn.close()
         cn2.close()
 
+    def checkMultipleUndoInOneTransaction(self):
+        # Verify that it's possible to perform multiple undo
+        # operations within a transaction.  If ZODB performs the undo
+        # operations in a nondeterministic order, this test will often
+        # fail.
+
+        conn = self._db.open()
+        try:
+            root = conn.root()
+
+            # Add transactions that set root["state"] to (0..5)
+            for state_num in range(6):
+                transaction.begin()
+                root['state'] = state_num
+                transaction.get().note('root["state"] = %d' % state_num)
+                transaction.commit()
+
+            # Undo all but the first.  Note that no work is actually
+            # performed yet.
+            transaction.begin()
+            log = self._db.undoLog()
+            for i in range(5):
+                self._db.undo(log[i]['id'])
+            transaction.get().note('undo states 1 through 5')
+
+            # Now attempt all those undo operations.
+            transaction.commit()
+
+            # Sanity check: we should be back to the first state.
+            self.assertEqual(root['state'], 0)
+        finally:
+            transaction.abort()
+            conn.close()
+
+        
+
 class PoisonedError(Exception):
     pass
 



More information about the Zodb-checkins mailing list