[Zope3-checkins] CVS: Zope3/src/transaction - manager.py:1.5 txn.py:1.5

Jeremy Hylton jeremy@zope.com
Fri, 7 Mar 2003 18:38:10 -0500


Update of /cvs-repository/Zope3/src/transaction
In directory cvs.zope.org:/tmp/cvs-serv19879/src/transaction

Modified Files:
	manager.py txn.py 
Log Message:
Catch errors that current during 2nd phase of 2PC.

It's not clear what the right answer is here.  The implementation
aborts any remaining resource managers and hopes for the best, which
isn't very satisfying.  Perhaps the hosed feature should come back.

Add some comments about the issues involved.  Revise the tests to
cover the current behavior.


=== Zope3/src/transaction/manager.py 1.4 => 1.5 ===
--- Zope3/src/transaction/manager.py:1.4	Thu Mar  6 19:17:55 2003
+++ Zope3/src/transaction/manager.py	Fri Mar  7 18:37:37 2003
@@ -1,4 +1,5 @@
 import logging
+import sys
 
 from transaction.interfaces import *
 from transaction.txn import Transaction, Status, Set
@@ -38,14 +39,28 @@
 
     def _finishCommit(self, txn):
         self.logger.debug("%s: commit", txn)
-        # finish the two-phase commit
-        for r in txn._resources:
-            r.commit(txn)
-        txn._status = Status.COMMITTED
+        try:
+            for r in txn._resources:
+                r.commit(txn)
+            txn._status = Status.COMMITTED
+        except:
+            # An error occured during the second phase of 2PC.  We can
+            # no longer guarantee the system is in a consistent state.
+            # The best we can do is abort() all the resource managers
+            # that haven't already committed and hope for the best.
+            error = sys.exc_info()
+            txn._status = Status.FAILED
+            self.abort(txn)
+            msg = ("Transaction failed during second phase of two-"
+                   "phase commmit")
+            self.logger.critical(msg, exc_info=error)
+            raise TransactionError("Transaction failed during second "
+                                   "phase of two-phase commit")
 
     def abort(self, txn):
         self.logger.debug("%s: abort", txn)
-        assert txn._status in (Status.ACTIVE, Status.PREPARED, Status.FAILED)
+        assert txn._status in (Status.ACTIVE, Status.PREPARED, Status.FAILED,
+                               Status.ABORTED)
         txn._status = Status.PREPARING
         for r in txn._resources:
             r.abort(txn)


=== Zope3/src/transaction/txn.py 1.4 => 1.5 ===
--- Zope3/src/transaction/txn.py:1.4	Thu Mar  6 19:18:10 2003
+++ Zope3/src/transaction/txn.py	Fri Mar  7 18:37:37 2003
@@ -64,6 +64,8 @@
     def abort(self):
         """Rollback to initial state."""
         assert self._manager is not None
+        if self._status == Status.ABORTED:
+            return
         if self._status not in (Status.ACTIVE, Status.PREPARED, Status.FAILED):
             raise IllegalStateError("abort", self._status)
         self._manager.abort(self)