Commit 921e14fe authored by Tim Peters's avatar Tim Peters

Make the sample ResourceManager work more the way in which we

intend real implementations to work, and beefed up the tests
to match.  The collaboration also got a little simpler.
parent ab98a7ed
...@@ -124,13 +124,12 @@ Scenario ...@@ -124,13 +124,12 @@ Scenario
C3.regisiter(o3) C3.regisiter(o3)
T.join(C3) T.join(C3)
S1.rollback() S1.rollback()
S2.rollback() S2.discard()
T.discard() T.discard()
C1.discard() C1.discard()
C2.discard() C2.discard()
C3.discard() C3.discard()
o3.invalidate() o3.invalidate()
S2.discard()
s21.discard() # roll back changes since previous, which is r11 s21.discard() # roll back changes since previous, which is r11
C1.discard(s21) C1.discard(s21)
o1.invalidate() o1.invalidate()
......
...@@ -409,11 +409,10 @@ class ISavePoint(zope.interface.Interface): ...@@ -409,11 +409,10 @@ class ISavePoint(zope.interface.Interface):
""" """
def discard(): def discard():
"""Discard changes saved by this savepoint. """Discard changes saved by and after this savepoint.
That means changes made since the immediately preceding That means changes made since the immediately preceding
savepoint if one exists, or since the start of the transaction, savepoint if one exists, or since the start of the transaction.
until this savepoint.
Once a savepoint has been discarded, it's an error to attempt Once a savepoint has been discarded, it's an error to attempt
to rollback or discard it again. to rollback or discard it again.
......
...@@ -86,6 +86,7 @@ class ResourceManager(object): ...@@ -86,6 +86,7 @@ class ResourceManager(object):
self.transaction = None self.transaction = None
self.delta = 0 self.delta = 0
self.txn_state = None self.txn_state = None
self.first_savepoint = None
def _check_state(self, *ok_states): def _check_state(self, *ok_states):
if self.txn_state not in ok_states: if self.txn_state not in ok_states:
...@@ -215,6 +216,9 @@ class ResourceManager(object): ...@@ -215,6 +216,9 @@ class ResourceManager(object):
self.transaction = None self.transaction = None
self.prepared = False self.prepared = False
self.txn_state = None self.txn_state = None
if self.first_savepoint is not None:
self.first_savepoint.invalidate()
self.first_savepoint = None
def tpc_abort(self, transaction): def tpc_abort(self, transaction):
"""Abort a transaction """Abort a transaction
...@@ -365,7 +369,7 @@ class ResourceManager(object): ...@@ -365,7 +369,7 @@ class ResourceManager(object):
>>> r2.rollback() >>> r2.rollback()
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: ('Attempt to roll back to invalid save point', 3, 2) ValueError: operation attempted on invalid SavePoint
We can roll back to a savepoint as often as we like: We can roll back to a savepoint as often as we like:
...@@ -394,38 +398,69 @@ class ResourceManager(object): ...@@ -394,38 +398,69 @@ class ResourceManager(object):
>>> r1.rollback() >>> r1.rollback()
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: Attempt to rollback stale rollback ValueError: operation attempted on invalid SavePoint
""" """
if self.txn_state is not None: if self.txn_state is not None:
raise TypeError("Can't get savepoint during two-phase commit") raise TypeError("Can't get savepoint during two-phase commit")
self._checkTransaction(transaction) self._checkTransaction(transaction)
self.transaction = transaction self.transaction = transaction
self.sp += 1 self.sp += 1
return SavePoint(self) if self.first_savepoint is None:
savepoint = SavePoint(self, 0)
self.first_savepoint = savepoint
else:
savepoint = SavePoint(self, self.first_savepoint.delta)
self.first_savepoint.last_savepoint().next_savepoint = savepoint
return savepoint
def discard(self, transaction): def discard(self, transaction):
pass if self.first_savepoint is None:
self.delta = 0
else:
self.delta = self.first_savepoint.last_savepoint().delta
class SavePoint(object): class SavePoint(object):
def __init__(self, rm): def __init__(self, rm, olddelta):
self.rm = rm self.rm = rm
self.olddelta = olddelta
self.sp = rm.sp self.sp = rm.sp
self.delta = rm.delta self.delta = rm.delta
self.transaction = rm.transaction self.transaction = rm.transaction
self.next_savepoint = None
self.valid = True
def _valid_check(self):
if not self.valid:
raise ValueError("operation attempted on invalid SavePoint")
def rollback(self): def rollback(self):
if self.transaction is not self.rm.transaction: self._valid_check()
raise TypeError("Attempt to rollback stale rollback") if self.next_savepoint is None:
if self.rm.sp < self.sp: self.rm.discard(self.transaction)
raise TypeError("Attempt to roll back to invalid save point", else:
self.sp, self.rm.sp) self.next_savepoint.discard()
self.rm.sp = self.sp self.next_savepoint = None
self.rm.delta = self.delta
def discard(self): def discard(self):
pass self._valid_check()
if self.next_savepoint is None:
self.rm.discard(self.transaction)
else:
self.next_savepoint.discard()
self.rm.delta = self.olddelta
self.valid = False
def last_savepoint(self):
"""Return the last savepoint in the chain starting at self."""
while self.next_savepoint is not None:
self = self.next_savepoint
return self
def invalidate(self):
self.valid = False
if self.next_savepoint is not None:
self.next_savepoint.invalidate()
def test_suite(): def test_suite():
from doctest import DocTestSuite from doctest import DocTestSuite
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment