[Neo-report] r2108 vincent - in /trunk/neo: client/ client/handlers/ tests/client/

nobody at svn.erp5.org nobody at svn.erp5.org
Fri May 14 01:30:37 CEST 2010


Author: vincent
Date: Fri May 14 01:30:37 2010
New Revision: 2108

Log:
Allow multiple conflict resolutions for a single object and transaction.

As we don't (and must not, otherwise it would deadlock) take a write lock
on a storage node when detecting a conflict, it is possible that multiple
conflicts get successively reported for a single object in a single
transaction. To solve this, client must tolerate multiple conflict for a
single object. The last resolved conflict being sent to all storage nodes,
transaction data will be consistent at tpc_finish.

- conflict_serial_dict and resolved_conflict_serial_dict become
  multivalued (sets instead of scalar)
- each resolution only resolve conflict with highest serial if greater
  than previous resolutions for the same object
  NOTE: this assumes that ("r" being conflict resolution method, "Tn"
  being the TID on which object is based, and "Cn" being a conflict with
  transaction n) when r(T0, C1) generates T1 but triggers C2,
    r(T0, C1) + r(T1, C2) == r(T0, C2)

Modified:
    trunk/neo/client/app.py
    trunk/neo/client/handlers/storage.py
    trunk/neo/tests/client/testClientApp.py
    trunk/neo/tests/client/testStorageHandler.py

Modified: trunk/neo/client/app.py
==============================================================================
--- trunk/neo/client/app.py [iso-8859-1] (original)
+++ trunk/neo/client/app.py [iso-8859-1] Fri May 14 01:30:37 2010
@@ -609,7 +609,14 @@
         object_serial_dict = local_var.object_serial_dict
         conflict_serial_dict = local_var.conflict_serial_dict
         resolved_conflict_serial_dict = local_var.resolved_conflict_serial_dict
-        for oid, conflict_serial in conflict_serial_dict.items():
+        for oid, conflict_serial_set in conflict_serial_dict.items():
+            resolved_serial_set = resolved_conflict_serial_dict.setdefault(
+                oid, set())
+            conflict_serial = max(conflict_serial_set)
+            if resolved_serial_set and conflict_serial <= max(resolved_serial_set):
+                # A later serial has already been resolved, skip.
+                resolved_serial_set.update(conflict_serial_dict.pop(oid))
+                continue
             serial, version = object_serial_dict[oid]
             data = data_dict[oid]
             tid = local_var.tid
@@ -621,8 +628,7 @@
                     logging.info('Conflict resolution succeed for %r:%r with %r',
                         dump(oid), dump(serial), dump(conflict_serial))
                     # Mark this conflict as resolved
-                    resolved_conflict_serial_dict[oid] = \
-                        conflict_serial_dict.pop(oid)
+                    resolved_serial_set.update(conflict_serial_dict.pop(oid))
                     # Try to store again
                     self.store(oid, conflict_serial, new_data, version,
                         local_var.txn)

Modified: trunk/neo/client/handlers/storage.py
==============================================================================
--- trunk/neo/client/handlers/storage.py [iso-8859-1] (original)
+++ trunk/neo/client/handlers/storage.py [iso-8859-1] Fri May 14 01:30:37 2010
@@ -75,24 +75,14 @@
             logging.info('%r report a conflict for %r with %r', conn,
                         dump(oid), dump(serial))
             conflict_serial_dict = local_var.conflict_serial_dict
-            pending_serial = conflict_serial_dict.get(oid)
-            resolved_serial = local_var.resolved_conflict_serial_dict.get(oid)
-            if pending_serial not in (None, serial) or \
-                    resolved_serial not in (None, serial):
-                raise NEOStorageError, 'Multiple conflicts for a single ' \
-                    'object (%s) in a single store: %s, %s, %s' % (
-                        dump(oid), dump(pending_serial),
-                        dump(resolved_serial), dump(serial))
             if serial in object_stored_counter_dict:
                 raise NEOStorageError, 'A storage accepted object for ' \
                     'serial %s but another reports a conflict for it.' % (
                         dump(serial), )
             # If this conflict is not already resolved, mark it for
             # resolution.
-            if resolved_serial is None:
-                # Note: we might overwrite an entry, but above test protects
-                # against overwriting a different value.
-                conflict_serial_dict[oid] = serial
+            if serial not in local_var.resolved_conflict_serial_dict.get(oid, ()):
+                conflict_serial_dict.setdefault(oid, set()).add(serial)
         else:
             object_stored_counter_dict[serial] = \
                 object_stored_counter_dict.get(serial, 0) + 1

Modified: trunk/neo/tests/client/testClientApp.py
==============================================================================
--- trunk/neo/tests/client/testClientApp.py [iso-8859-1] (original)
+++ trunk/neo/tests/client/testClientApp.py [iso-8859-1] Fri May 14 01:30:37 2010
@@ -462,7 +462,7 @@
         self.assertRaises(ConflictError, app.waitStoreResponses,
             failing_tryToResolveConflict)
         self.assertTrue(oid not in app.local_var.data_dict)
-        self.assertEquals(app.local_var.conflict_serial_dict[oid], tid)
+        self.assertEquals(app.local_var.conflict_serial_dict[oid], set([tid, ]))
         self.assertEquals(app.local_var.object_stored_counter_dict[oid], {})
         self.checkAskStoreObject(conn)
 

Modified: trunk/neo/tests/client/testStorageHandler.py
==============================================================================
--- trunk/neo/tests/client/testStorageHandler.py [iso-8859-1] (original)
+++ trunk/neo/tests/client/testStorageHandler.py [iso-8859-1] Fri May 14 01:30:37 2010
@@ -97,7 +97,7 @@
         local_var.conflict_serial_dict = {}
         local_var.resolved_conflict_serial_dict = {}
         self.handler.answerStoreObject(conn, 1, oid, tid)
-        self.assertEqual(local_var.conflict_serial_dict[oid], tid)
+        self.assertEqual(local_var.conflict_serial_dict[oid], set([tid, ]))
         self.assertEqual(local_var.object_stored_counter_dict[oid], {})
         self.assertFalse(oid in local_var.resolved_conflict_serial_dict)
         # object was already accepted by another storage, raise
@@ -115,24 +115,23 @@
         # resolution-pending conflict
         local_var = self.app.local_var
         local_var.object_stored_counter_dict = {oid: {}}
-        local_var.conflict_serial_dict = {oid: tid}
+        local_var.conflict_serial_dict = {oid: set([tid, ])}
         local_var.resolved_conflict_serial_dict = {}
         self.handler.answerStoreObject(conn, 1, oid, tid)
-        self.assertEqual(local_var.conflict_serial_dict[oid], tid)
+        self.assertEqual(local_var.conflict_serial_dict[oid], set([tid, ]))
         self.assertFalse(oid in local_var.resolved_conflict_serial_dict)
         self.assertEqual(local_var.object_stored_counter_dict[oid], {})
         # object was already accepted by another storage, raise
         local_var.object_stored_counter_dict = {oid: {tid: 1}}
-        local_var.conflict_serial_dict = {oid: tid}
+        local_var.conflict_serial_dict = {oid: set([tid, ])}
         local_var.resolved_conflict_serial_dict = {}
         self.assertRaises(NEOStorageError, self.handler.answerStoreObject,
             conn, 1, oid, tid)
-        # detected conflict is different, raise
-        local_var.object_stored_counter_dict = {oid: {}}
-        local_var.conflict_serial_dict = {oid: tid}
-        local_var.resolved_conflict_serial_dict = {}
-        self.assertRaises(NEOStorageError, self.handler.answerStoreObject,
-            conn, 1, oid, tid_2)
+        # detected conflict is different, don't raise
+        local_var.object_stored_counter_dict = {oid: {}}
+        local_var.conflict_serial_dict = {oid: set([tid, ])}
+        local_var.resolved_conflict_serial_dict = {}
+        self.handler.answerStoreObject(conn, 1, oid, tid_2)
 
     def test_answerStoreObject_3(self):
         conn = self.getConnection()
@@ -146,17 +145,17 @@
         local_var = self.app.local_var
         local_var.object_stored_counter_dict = {oid: {tid_2: 1}}
         local_var.conflict_serial_dict = {}
-        local_var.resolved_conflict_serial_dict = {oid: tid}
+        local_var.resolved_conflict_serial_dict = {oid: set([tid, ])}
         self.handler.answerStoreObject(conn, 1, oid, tid)
         self.assertFalse(oid in local_var.conflict_serial_dict)
-        self.assertEqual(local_var.resolved_conflict_serial_dict[oid], tid)
+        self.assertEqual(local_var.resolved_conflict_serial_dict[oid],
+            set([tid, ]))
         self.assertEqual(local_var.object_stored_counter_dict[oid], {tid_2: 1})
-        # detected conflict is different, raise
+        # detected conflict is different, don't raise
         local_var.object_stored_counter_dict = {oid: {tid: 1}}
         local_var.conflict_serial_dict = {}
-        local_var.resolved_conflict_serial_dict = {oid: tid}
-        self.assertRaises(NEOStorageError, self.handler.answerStoreObject,
-            conn, 1, oid, tid_2)
+        local_var.resolved_conflict_serial_dict = {oid: set([tid, ])}
+        self.handler.answerStoreObject(conn, 1, oid, tid_2)
 
     def test_answerStoreObject_4(self):
         conn = self.getConnection()





More information about the Neo-report mailing list