[Neo-report] r2469 vincent - in /trunk/neo: master/ tests/master/

nobody at svn.erp5.org nobody at svn.erp5.org
Thu Dec 2 13:47:21 CET 2010


Author: vincent
Date: Thu Dec  2 13:47:20 2010
New Revision: 2469

Log:
Add function to pack, unpack and offset TIDs.

This fixes a bug where, if current time was more than a minute before
_last_tid, returned TID would be decreasing because gmt is reused in
collision resolution code instead of reading _last_tid's date.

Modified:
    trunk/neo/master/transactions.py
    trunk/neo/tests/master/testTransactions.py

Modified: trunk/neo/master/transactions.py
==============================================================================
--- trunk/neo/master/transactions.py [iso-8859-1] (original)
+++ trunk/neo/master/transactions.py [iso-8859-1] Thu Dec  2 13:47:20 2010
@@ -24,6 +24,64 @@ import neo
 TID_LOW_OVERFLOW = 2**32
 TID_LOW_MAX = TID_LOW_OVERFLOW - 1
 SECOND_PER_TID_LOW = 60.0 / TID_LOW_OVERFLOW
+TID_CHUNK_RULES = (
+    (-1900, 0),
+    (-1, 12),
+    (-1, 31),
+    (0, 24),
+    (0, 60),
+)
+
+def packTID(utid):
+    """
+    Pack given 2-tuple containing:
+    - a 5-tuple containing year, month, day, hour and minute
+    - seconds scaled to 60:2**32
+    into a 64 bits TID.
+    """
+    higher, lower = utid
+    assert len(higher) == len(TID_CHUNK_RULES), higher
+    packed_higher = 0
+    for value, (offset, multiplicator) in zip(higher, TID_CHUNK_RULES):
+        assert isinstance(value, (int, long)), value
+        value += offset
+        assert 0 <= value, (value, offset, multiplicator)
+        assert multiplicator == 0 or value < multiplicator, (value,
+            offset, multiplicator)
+        packed_higher *= multiplicator
+        packed_higher += value
+    assert isinstance(lower, (int, long)), lower
+    assert 0 <= lower < TID_LOW_OVERFLOW, hex(lower)
+    return pack('!LL', packed_higher, lower)
+
+def unpackTID(ptid):
+    """
+    Unpack given 64 bits TID in to a 2-tuple containing:
+    - a 5-tuple containing year, month, day, hour and minute
+    - seconds scaled to 60:2**32
+    """
+    packed_higher, lower = unpack('!LL', ptid)
+    higher = []
+    append = higher.append
+    for offset, multiplicator in reversed(TID_CHUNK_RULES):
+        if multiplicator:
+            packed_higher, value = divmod(packed_higher, multiplicator)
+        else:
+            packed_higher, value = 0, packed_higher
+        append(value - offset)
+    higher.reverse()
+    return (tuple(higher), lower)
+
+def addTID(ptid, offset):
+    """
+    Offset given packed TID.
+    """
+    higher, lower = unpackTID(ptid)
+    high_offset, lower = divmod(lower + offset, TID_LOW_OVERFLOW)
+    if high_offset:
+        d = datetime(*higher) + timedelta(0, 60 * high_offset)
+        higher = (d.year, d.month, d.day, d.hour, d.minute)
+    return packTID((higher, lower))
 
 class Transaction(object):
     """
@@ -169,26 +227,15 @@ class TransactionManager(object):
 
     def _nextTID(self):
         """ Compute the next TID based on the current time and check collisions """
-        def make_upper(year, month, day, hour, minute):
-            return ((((year - 1900) * 12 + month - 1) * 31 \
-                  + day - 1) * 24 + hour) * 60 + minute
         tm = time()
         gmt = gmtime(tm)
-        upper = make_upper(gmt.tm_year, gmt.tm_mon, gmt.tm_mday, gmt.tm_hour,
-            gmt.tm_min)
-        lower = int((gmt.tm_sec % 60 + (tm - int(tm))) / SECOND_PER_TID_LOW)
-        tid = pack('!LL', upper, lower)
+        tid = packTID((
+            (gmt.tm_year, gmt.tm_mon, gmt.tm_mday, gmt.tm_hour,
+                gmt.tm_min),
+            int((gmt.tm_sec % 60 + (tm - int(tm))) / SECOND_PER_TID_LOW)
+        ))
         if self._last_tid is not None and tid <= self._last_tid:
-            upper, lower = unpack('!LL', self._last_tid)
-            if lower == TID_LOW_MAX:
-                # This should not happen usually.
-                d = datetime(gmt.tm_year, gmt.tm_mon, gmt.tm_mday,
-                             gmt.tm_hour, gmt.tm_min) + timedelta(0, 60)
-                upper = make_upper(d.year, d.month, d.day, d.hour, d.minute)
-                lower = 0
-            else:
-                lower += 1
-            tid = pack('!LL', upper, lower)
+            tid  = addTID(self._last_tid, 1)
         self._last_tid = tid
         return self._last_tid
 

Modified: trunk/neo/tests/master/testTransactions.py
==============================================================================
--- trunk/neo/tests/master/testTransactions.py [iso-8859-1] (original)
+++ trunk/neo/tests/master/testTransactions.py [iso-8859-1] Thu Dec  2 13:47:20 2010
@@ -21,6 +21,7 @@ from struct import pack, unpack
 from neo.tests import NeoUnitTestBase
 
 from neo.master.transactions import Transaction, TransactionManager
+from neo.master.transactions import packTID, unpackTID, addTID
 
 class testTransactionManager(NeoUnitTestBase):
 
@@ -188,5 +189,31 @@ class testTransactionManager(NeoUnitTest
         self.assertEqual(t3.getUUIDList(), [storage_2_uuid])
         self.assertTrue(t3.lock(storage_2_uuid))
 
+    def testTIDUtils(self):
+        """
+        Tests packTID/unpackTID/addTID.
+        """
+        min_tid = pack('!LL', 0, 0)
+        min_unpacked_tid = ((1900, 1, 1, 0, 0), 0)
+        max_tid = pack('!LL', 2**32 - 1, 2 ** 32 - 1)
+        # ((((9917 - 1900) * 12 + (10 - 1)) * 31 + (14 - 1)) * 24 + 4) * 60 +
+        # 15 == 2**32 - 1
+        max_unpacked_tid = ((9917, 10, 14, 4, 15), 2**32 - 1)
+
+        self.assertEqual(unpackTID(min_tid), min_unpacked_tid)
+        self.assertEqual(unpackTID(max_tid), max_unpacked_tid)
+        self.assertEqual(packTID(min_unpacked_tid), min_tid)
+        self.assertEqual(packTID(max_unpacked_tid), max_tid)
+
+        self.assertEqual(addTID(min_tid, 1), pack('!LL', 0, 1))
+        self.assertEqual(addTID(pack('!LL', 0, 2**32 - 1), 1),
+            pack('!LL', 1, 0))
+        self.assertEqual(addTID(pack('!LL', 0, 2**32 - 1), 2**32 + 1),
+            pack('!LL', 2, 0))
+        # Check impossible dates are avoided (2010/11/31 doesn't exist)
+        self.assertEqual(
+            unpackTID(addTID(packTID(((2010, 11, 30, 23, 59), 2**32 - 1)), 1)),
+            ((2010, 12, 1, 0, 0), 0))
+
 if __name__ == '__main__':
     unittest.main()




More information about the Neo-report mailing list