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
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
......
......@@ -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(NeoUnitTestBase):
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()
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