[Zodb-checkins] SVN: ZODB/branches/3.4/src/transaction/ Make transactions uncommitable if savepoint rollback fails.

Jim Fulton jim at zope.com
Sun Apr 24 11:26:40 EDT 2005


Log message for revision 30147:
  Make transactions uncommitable if savepoint rollback fails.
  
  Added demonstration of transaction non-commitability after savepoint
  or savepoint rollback failure.
  
  Updated "previous commit failed" error to "previous operation failed".
  

Changed:
  U   ZODB/branches/3.4/src/transaction/_transaction.py
  U   ZODB/branches/3.4/src/transaction/savepoint.txt

-=-
Modified: ZODB/branches/3.4/src/transaction/_transaction.py
===================================================================
--- ZODB/branches/3.4/src/transaction/_transaction.py	2005-04-24 15:26:37 UTC (rev 30146)
+++ ZODB/branches/3.4/src/transaction/_transaction.py	2005-04-24 15:26:39 UTC (rev 30147)
@@ -231,17 +231,17 @@
 
     # Raise TransactionFailedError, due to commit()/join()/register()
     # getting called when the current transaction has already suffered
-    # a commit failure.
-    def _prior_commit_failed(self):
+    # a commit/savepoint failure.
+    def _prior_operation_failed(self):
         from ZODB.POSException import TransactionFailedError
         assert self._failure_traceback is not None
-        raise TransactionFailedError("commit() previously failed, "
-                "with this traceback:\n\n%s" %
+        raise TransactionFailedError("An operation previously failed, "
+                "with traceback:\n\n%s" %
                 self._failure_traceback.getvalue())
 
     def join(self, resource):
         if self.status is Status.COMMITFAILED:
-            self._prior_commit_failed() # doesn't return
+            self._prior_operation_failed() # doesn't return
 
         if self.status is not Status.ACTIVE:
             # TODO: Should it be possible to join a committing transaction?
@@ -261,15 +261,15 @@
 
     def savepoint(self, optimistic=False):
         if self.status is Status.COMMITFAILED:
-            self._prior_commit_failed() # doesn't return, it raises
+            self._prior_operation_failed() # doesn't return, it raises
 
         try:
-            savepoint = Savepoint(optimistic)
+            savepoint = Savepoint(self, optimistic)
             for resource in self._resources:
                 savepoint.join(resource)
         except:
             self._cleanup(self._resources)
-            self._saveCommitishError() # doesn't return, it raises!
+            self._saveCommitishError() # reraises!
             
         if self._last_savepoint is not None:
             savepoint.previous = self._last_savepoint
@@ -330,7 +330,7 @@
             return
         
         if self.status is Status.COMMITFAILED:
-            self._prior_commit_failed() # doesn't return
+            self._prior_operation_failed() # doesn't return
 
         self._callBeforeCommitHooks()
 
@@ -598,7 +598,8 @@
     """
     interface.implements(interfaces.ISavepoint)
 
-    def __init__(self, optimistic):
+    def __init__(self, transaction, optimistic):
+        self.transaction = transaction
         self._savepoints = []
         self.valid = True
         self.next = self.previous = None
@@ -620,8 +621,12 @@
         if not self.valid:
             raise interfaces.InvalidSavepointRollbackError
         self._invalidate_next()
-        for savepoint in self._savepoints:
-            savepoint.rollback()
+        try:
+            for savepoint in self._savepoints:
+                savepoint.rollback()
+        except:
+            # Mark the transaction as failed
+            self.transaction._saveCommitishError() # reraises!
 
     def _invalidate_next(self):
         self.valid = False

Modified: ZODB/branches/3.4/src/transaction/savepoint.txt
===================================================================
--- ZODB/branches/3.4/src/transaction/savepoint.txt	2005-04-24 15:26:37 UTC (rev 30146)
+++ ZODB/branches/3.4/src/transaction/savepoint.txt	2005-04-24 15:26:39 UTC (rev 30147)
@@ -219,3 +219,57 @@
     ...
     TypeError: ('Savepoints unsupported', {'name': 'sam'})
 
+Failures
+--------
+
+If a failure occurs when creating or rolling back a savepoint, the
+transaction state will be uncertain and the transaction will become
+uncommitable.  From that point on, most transaction operations,
+including commit, will fail until the transaction is aborted.
+
+In the previous example, we got an error when we tried to rollback the
+savepoint. If we try to commit the transaction, the commit will fail:
+
+    >>> transaction.commit() # doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    TransactionFailedError: An operation previously failed, with traceback:
+    ...
+    TypeError: ('Savepoints unsupported', {'name': 'sam'})
+    <BLANKLINE>
+
+We have to abort it to make any progress:
+
+    >>> transaction.abort()
+
+Similarly, in our earlier example, where we tried to take a savepoint
+with a data manager that didn't support savepoints:
+
+    >>> dm_no_sp['name'] = 'sally'
+    >>> dm['name'] = 'sally'
+    >>> savepoint = transaction.savepoint()
+    Traceback (most recent call last):
+    ...
+    TypeError: ('Savepoints unsupported', {'name': 'sue'})
+
+    >>> transaction.commit() # doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    TransactionFailedError: An operation previously failed, with traceback:
+    ...
+    TypeError: ('Savepoints unsupported', {'name': 'sue'})
+    <BLANKLINE>
+    
+    >>> transaction.abort()
+ 
+After clearing the transaction with an abort, we can get on with new
+transactions: 
+
+    >>> dm_no_sp['name'] = 'sally'
+    >>> dm['name'] = 'sally'
+    >>> transaction.commit()
+    >>> dm_no_sp['name']
+    'sally'
+    >>> dm['name']
+    'sally'
+    



More information about the Zodb-checkins mailing list