[Checkins] SVN: transaction/branches/chrism-fix-attempts/ - Fix "for attempt in transaction.attempts(x)" machinery, which would not
Chris McDonough
cvs-admin at zope.org
Thu Mar 29 06:45:37 UTC 2012
Log message for revision 124787:
- Fix "for attempt in transaction.attempts(x)" machinery, which would not
retry a transaction if its implicit call to ``.commit()`` itself raised a
transient error. Symptom: seeing conflict errors even though you thought
you were retrying some number of times via the "attempts" machinery (the
first attempt to generate an exception during commit would cause that
exception to be raised).
Changed:
U transaction/branches/chrism-fix-attempts/CHANGES.txt
U transaction/branches/chrism-fix-attempts/transaction/_manager.py
A transaction/branches/chrism-fix-attempts/transaction/tests/test_attempt.py
-=-
Modified: transaction/branches/chrism-fix-attempts/CHANGES.txt
===================================================================
--- transaction/branches/chrism-fix-attempts/CHANGES.txt 2012-03-29 00:57:38 UTC (rev 124786)
+++ transaction/branches/chrism-fix-attempts/CHANGES.txt 2012-03-29 06:45:32 UTC (rev 124787)
@@ -6,6 +6,13 @@
- Python 3.3 compatibility.
+- Fix "for attempt in transaction.attempts(x)" machinery, which would not
+ retry a transaction if its implicit call to ``.commit()`` itself raised a
+ transient error. Symptom: seeing conflict errors even though you thought
+ you were retrying some number of times via the "attempts" machinery (the
+ first attempt to generate an exception during commit would cause that
+ exception to be raised).
+
1.2.0 (2011-12-05)
------------------
Modified: transaction/branches/chrism-fix-attempts/transaction/_manager.py
===================================================================
--- transaction/branches/chrism-fix-attempts/transaction/_manager.py 2012-03-29 00:57:38 UTC (rev 124786)
+++ transaction/branches/chrism-fix-attempts/transaction/_manager.py 2012-03-29 06:45:32 UTC (rev 124787)
@@ -135,8 +135,15 @@
def __exit__(self, t, v, tb):
if v is None:
- self.manager.commit()
+ try:
+ self.manager.commit()
+ except TransientError:
+ self.manager.abort()
+ return True # swallow
+ except:
+ self.manager.abort()
+ return False # don't swallow
else:
retry = self.manager._retryable(t, v)
self.manager.abort()
- return retry
+ return retry # swallow exception if True, else don't swallow
Added: transaction/branches/chrism-fix-attempts/transaction/tests/test_attempt.py
===================================================================
--- transaction/branches/chrism-fix-attempts/transaction/tests/test_attempt.py (rev 0)
+++ transaction/branches/chrism-fix-attempts/transaction/tests/test_attempt.py 2012-03-29 06:45:32 UTC (rev 124787)
@@ -0,0 +1,85 @@
+import unittest
+
+class TestAttempt(unittest.TestCase):
+ def _makeOne(self, manager):
+ from transaction._manager import Attempt
+ return Attempt(manager)
+
+ def test___enter__(self):
+ manager = DummyManager()
+ inst = self._makeOne(manager)
+ inst.__enter__()
+ self.assertTrue(manager.entered)
+
+ def test___exit__no_exc_no_commit_exception(self):
+ manager = DummyManager()
+ inst = self._makeOne(manager)
+ result = inst.__exit__(None, None, None)
+ self.assertFalse(result)
+ self.assertTrue(manager.committed)
+
+ def test___exit__no_exc_nonretryable_commit_exception(self):
+ manager = DummyManager(raise_on_commit=ValueError)
+ inst = self._makeOne(manager)
+ result = inst.__exit__(None, None, None)
+ self.assertFalse(result)
+
+ def test___exit__no_exc_abort_exception_after_nonretryable_commit_exc(self):
+ manager = DummyManager(raise_on_abort=ValueError,
+ raise_on_commit=KeyError)
+ inst = self._makeOne(manager)
+ self.assertRaises(ValueError, inst.__exit__, None, None, None)
+ self.assertTrue(manager.committed)
+ self.assertTrue(manager.aborted)
+
+ def test___exit__no_exc_retryable_commit_exception(self):
+ from transaction.interfaces import TransientError
+ manager = DummyManager(raise_on_commit=TransientError)
+ inst = self._makeOne(manager)
+ result = inst.__exit__(None, None, None)
+ self.assertTrue(result)
+ self.assertTrue(manager.committed)
+ self.assertTrue(manager.aborted)
+
+ def test___exit__with_exception_value_retryable(self):
+ from transaction.interfaces import TransientError
+ manager = DummyManager()
+ inst = self._makeOne(manager)
+ result = inst.__exit__(TransientError, TransientError(), None)
+ self.assertTrue(result)
+ self.assertFalse(manager.committed)
+ self.assertTrue(manager.aborted)
+
+ def test___exit__with_exception_value_nonretryable(self):
+ manager = DummyManager()
+ inst = self._makeOne(manager)
+ result = inst.__exit__(KeyError, KeyError(), None)
+ self.assertFalse(result)
+ self.assertFalse(manager.committed)
+ self.assertTrue(manager.aborted)
+
+class DummyManager(object):
+ entered = False
+ committed = False
+ aborted = False
+
+ def __init__(self, raise_on_commit=None, raise_on_abort=None):
+ self.raise_on_commit = raise_on_commit
+ self.raise_on_abort = raise_on_abort
+
+ def _retryable(self, t, v):
+ from transaction._manager import TransientError
+ return issubclass(t, TransientError)
+
+ def __enter__(self):
+ self.entered = True
+
+ def abort(self):
+ self.aborted = True
+ if self.raise_on_abort:
+ raise self.raise_on_abort
+
+ def commit(self):
+ self.committed = True
+ if self.raise_on_commit:
+ raise self.raise_on_commit
More information about the checkins
mailing list