Commit 609ef821 authored by Vincent Pelletier's avatar Vincent Pelletier

Add conflict detection for multiple "undo" in same transaction.

Detect if a client requests multiple undo/store based on wrong revisions.
Also, don't try to acquire lock when we know we have it.

git-svn-id: https://svn.erp5.org/repos/neo/trunk@2600 71dcc9de-d417-0410-9af5-da40c76e7ee4
parent 4d69f401
...@@ -239,16 +239,28 @@ class TransactionManager(object): ...@@ -239,16 +239,28 @@ class TransactionManager(object):
self._app.executeQueuedEvents() self._app.executeQueuedEvents()
# Attemp to acquire lock again. # Attemp to acquire lock again.
locking_tid = self._store_lock_dict.get(oid) locking_tid = self._store_lock_dict.get(oid)
if locking_tid == tid: if locking_tid in (None, tid):
neo.logging.info('Transaction %s storing %s more than once',
dump(tid), dump(oid))
elif locking_tid is None:
# check if this is generated from the latest revision. # check if this is generated from the latest revision.
history_list = self._app.dm.getObjectHistory(oid) if locking_tid == tid:
if history_list and history_list[0][0] != serial: # If previous store was an undo, next store must be based on
# undo target.
_, _, _, _, previous_serial = self._transaction_dict[
tid].getObject(oid)
if previous_serial is None:
# XXX: use some special serial when previous store was not
# an undo ? Maybe it should just not happen.
neo.logging.info('Transaction %s storing %s more than '
'once', dump(tid), dump(oid))
else:
previous_serial = None
if previous_serial is None:
history_list = self._app.dm.getObjectHistory(oid)
if history_list:
previous_serial = history_list[0][0]
if previous_serial is not None and previous_serial != serial:
neo.logging.info('Resolvable conflict on %r:%r', dump(oid), neo.logging.info('Resolvable conflict on %r:%r', dump(oid),
dump(tid)) dump(tid))
raise ConflictError(history_list[0][0]) raise ConflictError(previous_serial)
neo.logging.info('Transaction %s storing %s', dump(tid), dump(oid)) neo.logging.info('Transaction %s storing %s', dump(tid), dump(oid))
self._store_lock_dict[oid] = tid self._store_lock_dict[oid] = tid
elif locking_tid > tid: elif locking_tid > tid:
......
...@@ -372,7 +372,9 @@ class TransactionManagerTests(NeoUnitTestBase): ...@@ -372,7 +372,9 @@ class TransactionManagerTests(NeoUnitTestBase):
self.manager.updateObjectDataForPack(oid, orig_serial, None, None) self.manager.updateObjectDataForPack(oid, orig_serial, None, None)
self.assertEqual(self.manager.getObjectFromTransaction(locking_serial, self.assertEqual(self.manager.getObjectFromTransaction(locking_serial,
oid), None) oid), None)
self.manager.abort(locking_serial, even_if_locked=True)
# Object known, but doesn't point at orig_serial, it is not updated # Object known, but doesn't point at orig_serial, it is not updated
self.manager.register(uuid, locking_serial)
self.manager.storeObject(locking_serial, ram_serial, oid, 0, 512, self.manager.storeObject(locking_serial, ram_serial, oid, 0, 512,
'bar', None) 'bar', None)
orig_object = self.manager.getObjectFromTransaction(locking_serial, orig_object = self.manager.getObjectFromTransaction(locking_serial,
...@@ -380,7 +382,9 @@ class TransactionManagerTests(NeoUnitTestBase): ...@@ -380,7 +382,9 @@ class TransactionManagerTests(NeoUnitTestBase):
self.manager.updateObjectDataForPack(oid, orig_serial, None, None) self.manager.updateObjectDataForPack(oid, orig_serial, None, None)
self.assertEqual(self.manager.getObjectFromTransaction(locking_serial, self.assertEqual(self.manager.getObjectFromTransaction(locking_serial,
oid), orig_object) oid), orig_object)
self.manager.abort(locking_serial, even_if_locked=True)
self.manager.register(uuid, locking_serial)
self.manager.storeObject(locking_serial, ram_serial, oid, None, None, self.manager.storeObject(locking_serial, ram_serial, oid, None, None,
None, other_serial) None, other_serial)
orig_object = self.manager.getObjectFromTransaction(locking_serial, orig_object = self.manager.getObjectFromTransaction(locking_serial,
...@@ -388,21 +392,26 @@ class TransactionManagerTests(NeoUnitTestBase): ...@@ -388,21 +392,26 @@ class TransactionManagerTests(NeoUnitTestBase):
self.manager.updateObjectDataForPack(oid, orig_serial, None, None) self.manager.updateObjectDataForPack(oid, orig_serial, None, None)
self.assertEqual(self.manager.getObjectFromTransaction(locking_serial, self.assertEqual(self.manager.getObjectFromTransaction(locking_serial,
oid), orig_object) oid), orig_object)
self.manager.abort(locking_serial, even_if_locked=True)
# Object known and points at undone data it gets updated # Object known and points at undone data it gets updated
# ...with data_serial: getObjectData must not be called # ...with data_serial: getObjectData must not be called
self.manager.register(uuid, locking_serial)
self.manager.storeObject(locking_serial, ram_serial, oid, None, None, self.manager.storeObject(locking_serial, ram_serial, oid, None, None,
None, orig_serial) None, orig_serial)
self.manager.updateObjectDataForPack(oid, orig_serial, new_serial, self.manager.updateObjectDataForPack(oid, orig_serial, new_serial,
None) None)
self.assertEqual(self.manager.getObjectFromTransaction(locking_serial, self.assertEqual(self.manager.getObjectFromTransaction(locking_serial,
oid), (oid, None, None, None, new_serial)) oid), (oid, None, None, None, new_serial))
self.manager.abort(locking_serial, even_if_locked=True)
# with data # with data
self.manager.register(uuid, locking_serial)
self.manager.storeObject(locking_serial, ram_serial, oid, None, None, self.manager.storeObject(locking_serial, ram_serial, oid, None, None,
None, orig_serial) None, orig_serial)
self.manager.updateObjectDataForPack(oid, orig_serial, None, self.manager.updateObjectDataForPack(oid, orig_serial, None,
getObjectData) getObjectData)
self.assertEqual(self.manager.getObjectFromTransaction(locking_serial, self.assertEqual(self.manager.getObjectFromTransaction(locking_serial,
oid), (oid, compression, checksum, value, None)) oid), (oid, compression, checksum, value, None))
self.manager.abort(locking_serial, even_if_locked=True)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
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