Commit ec1ca201 authored by Vincent Pelletier's avatar Vincent Pelletier

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.

git-svn-id: https://svn.erp5.org/repos/neo/trunk@2469 71dcc9de-d417-0410-9af5-da40c76e7ee4
parent 2d81f101
...@@ -24,6 +24,64 @@ import neo ...@@ -24,6 +24,64 @@ import neo
TID_LOW_OVERFLOW = 2**32 TID_LOW_OVERFLOW = 2**32
TID_LOW_MAX = TID_LOW_OVERFLOW - 1 TID_LOW_MAX = TID_LOW_OVERFLOW - 1
SECOND_PER_TID_LOW = 60.0 / TID_LOW_OVERFLOW 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): class Transaction(object):
""" """
...@@ -169,26 +227,15 @@ class TransactionManager(object): ...@@ -169,26 +227,15 @@ class TransactionManager(object):
def _nextTID(self): def _nextTID(self):
""" Compute the next TID based on the current time and check collisions """ """ 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() tm = time()
gmt = gmtime(tm) gmt = gmtime(tm)
upper = make_upper(gmt.tm_year, gmt.tm_mon, gmt.tm_mday, gmt.tm_hour, tid = packTID((
gmt.tm_min) (gmt.tm_year, gmt.tm_mon, gmt.tm_mday, gmt.tm_hour,
lower = int((gmt.tm_sec % 60 + (tm - int(tm))) / SECOND_PER_TID_LOW) gmt.tm_min),
tid = pack('!LL', upper, lower) int((gmt.tm_sec % 60 + (tm - int(tm))) / SECOND_PER_TID_LOW)
))
if self._last_tid is not None and tid <= self._last_tid: if self._last_tid is not None and tid <= self._last_tid:
upper, lower = unpack('!LL', self._last_tid) tid = addTID(self._last_tid, 1)
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)
self._last_tid = tid self._last_tid = tid
return self._last_tid return self._last_tid
......
...@@ -21,6 +21,7 @@ from struct import pack, unpack ...@@ -21,6 +21,7 @@ from struct import pack, unpack
from neo.tests import NeoUnitTestBase from neo.tests import NeoUnitTestBase
from neo.master.transactions import Transaction, TransactionManager from neo.master.transactions import Transaction, TransactionManager
from neo.master.transactions import packTID, unpackTID, addTID
class testTransactionManager(NeoUnitTestBase): class testTransactionManager(NeoUnitTestBase):
...@@ -188,5 +189,31 @@ class testTransactionManager(NeoUnitTestBase): ...@@ -188,5 +189,31 @@ class testTransactionManager(NeoUnitTestBase):
self.assertEqual(t3.getUUIDList(), [storage_2_uuid]) self.assertEqual(t3.getUUIDList(), [storage_2_uuid])
self.assertTrue(t3.lock(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__': 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