Commit 848d152c authored by Julien Muchembled's avatar Julien Muchembled

neoctl: tid given as floats are processed as timestamps

parent 93376a47
...@@ -17,15 +17,86 @@ ...@@ -17,15 +17,86 @@
import socket import socket
from binascii import a2b_hex, b2a_hex from binascii import a2b_hex, b2a_hex
from datetime import timedelta, datetime
from hashlib import sha1 from hashlib import sha1
from Queue import deque from Queue import deque
from struct import pack, unpack from struct import pack, unpack
from time import gmtime
SOCKET_CONNECTORS_DICT = { SOCKET_CONNECTORS_DICT = {
socket.AF_INET : 'SocketConnectorIPv4', socket.AF_INET : 'SocketConnectorIPv4',
socket.AF_INET6: 'SocketConnectorIPv6', socket.AF_INET6: 'SocketConnectorIPv6',
} }
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 tidFromTime(tm):
gmt = gmtime(tm)
return packTID(
(gmt.tm_year, gmt.tm_mon, gmt.tm_mday, gmt.tm_hour, gmt.tm_min),
int((gmt.tm_sec + (tm - int(tm))) / SECOND_PER_TID_LOW))
def packTID(higher, lower):
"""
higher: a 5-tuple containing year, month, day, hour and minute
lower: seconds scaled to 60:2**32 into a 64 bits TID
"""
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
# If the machine is configured in such way that gmtime() returns leap
# seconds (e.g. TZ=right/UTC), then the best we can do is to use
# TID_LOW_MAX, because TID format was not designed to support them.
# For more information about leap seconds on Unix, see:
# http://en.wikipedia.org/wiki/Unix_time
# http://www.madore.org/~david/computers/unix-leap-seconds.html
return pack('!LL', packed_higher, min(lower, TID_LOW_MAX))
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)
def u64(s): def u64(s):
return unpack('!Q', s)[0] return unpack('!Q', s)[0]
......
...@@ -14,79 +14,11 @@ ...@@ -14,79 +14,11 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from time import time, gmtime from time import time
from struct import pack, unpack from struct import pack, unpack
from neo.lib.protocol import ZERO_TID
from datetime import timedelta, datetime
from neo.lib import logging from neo.lib import logging
from neo.lib.protocol import uuid_str from neo.lib.protocol import uuid_str, ZERO_TID
from neo.lib.util import dump, u64 from neo.lib.util import dump, u64, addTID, tidFromTime
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
# If the machine is configured in such way that gmtime() returns leap
# seconds (e.g. TZ=right/UTC), then the best we can do is to use
# TID_LOW_MAX, because TID format was not designed to support them.
# For more information about leap seconds on Unix, see:
# http://en.wikipedia.org/wiki/Unix_time
# http://www.madore.org/~david/computers/unix-leap-seconds.html
return pack('!LL', packed_higher, min(lower, TID_LOW_MAX))
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 DelayedError(Exception): class DelayedError(Exception):
pass pass
...@@ -274,13 +206,7 @@ class TransactionManager(object): ...@@ -274,13 +206,7 @@ class TransactionManager(object):
When constraints allow, prefer decreasing generated TID, to avoid When constraints allow, prefer decreasing generated TID, to avoid
fast-forwarding to future dates. fast-forwarding to future dates.
""" """
tm = time() tid = tidFromTime(time())
gmt = gmtime(tm)
tid = packTID((
(gmt.tm_year, gmt.tm_mon, gmt.tm_mday, gmt.tm_hour,
gmt.tm_min),
int((gmt.tm_sec + (tm - int(tm))) / SECOND_PER_TID_LOW)
))
min_tid = self._last_tid min_tid = self._last_tid
if tid <= min_tid: if tid <= min_tid:
tid = addTID(min_tid, 1) tid = addTID(min_tid, 1)
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
from operator import itemgetter from operator import itemgetter
from .neoctl import NeoCTL, NotReadyException from .neoctl import NeoCTL, NotReadyException
from neo.lib.util import p64, u64 from neo.lib.util import p64, u64, tidFromTime
from neo.lib.protocol import uuid_str, ClusterStates, NodeTypes, \ from neo.lib.protocol import uuid_str, ClusterStates, NodeTypes, \
UUID_NAMESPACES, ZERO_TID UUID_NAMESPACES, ZERO_TID
...@@ -58,6 +58,8 @@ class TerminalNeoCTL(object): ...@@ -58,6 +58,8 @@ class TerminalNeoCTL(object):
return getattr(ClusterStates, value.upper()) return getattr(ClusterStates, value.upper())
def asTID(self, value): def asTID(self, value):
if '.' in value:
return tidFromTime(float(value))
return p64(int(value, 0)) return p64(int(value, 0))
asNode = staticmethod(uuid_int) asNode = staticmethod(uuid_int)
...@@ -288,7 +290,8 @@ class Application(object): ...@@ -288,7 +290,8 @@ class Application(object):
def usage(self, message): def usage(self, message):
output_list = (message, 'Available commands:', self._usage(action_dict), output_list = (message, 'Available commands:', self._usage(action_dict),
"TID arguments must be integers, e.g. '257684787499560686 or" "TID arguments can be either integers or timestamps as floats,"
" '0x3937af2eeeeeeee' for 2012-01-01 12:34:56 UTC") " e.g. '257684787499560686', '0x3937af2eeeeeeee' or '1325421296.'"
" for 2012-01-01 12:34:56 UTC")
return '\n'.join(output_list) return '\n'.join(output_list)
...@@ -19,9 +19,8 @@ from mock import Mock ...@@ -19,9 +19,8 @@ from mock import Mock
from struct import pack from struct import pack
from .. import NeoUnitTestBase from .. import NeoUnitTestBase
from neo.lib.protocol import NodeTypes from neo.lib.protocol import NodeTypes
from neo.lib.util import packTID, unpackTID, addTID
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):
...@@ -192,8 +191,8 @@ class testTransactionManager(NeoUnitTestBase): ...@@ -192,8 +191,8 @@ class testTransactionManager(NeoUnitTestBase):
self.assertEqual(unpackTID(min_tid), min_unpacked_tid) self.assertEqual(unpackTID(min_tid), min_unpacked_tid)
self.assertEqual(unpackTID(max_tid), max_unpacked_tid) self.assertEqual(unpackTID(max_tid), max_unpacked_tid)
self.assertEqual(packTID(min_unpacked_tid), min_tid) self.assertEqual(packTID(*min_unpacked_tid), min_tid)
self.assertEqual(packTID(max_unpacked_tid), max_tid) self.assertEqual(packTID(*max_unpacked_tid), max_tid)
self.assertEqual(addTID(min_tid, 1), pack('!LL', 0, 1)) self.assertEqual(addTID(min_tid, 1), pack('!LL', 0, 1))
self.assertEqual(addTID(pack('!LL', 0, 2**32 - 1), 1), self.assertEqual(addTID(pack('!LL', 0, 2**32 - 1), 1),
...@@ -202,7 +201,7 @@ class testTransactionManager(NeoUnitTestBase): ...@@ -202,7 +201,7 @@ class testTransactionManager(NeoUnitTestBase):
pack('!LL', 2, 0)) pack('!LL', 2, 0))
# Check impossible dates are avoided (2010/11/31 doesn't exist) # Check impossible dates are avoided (2010/11/31 doesn't exist)
self.assertEqual( self.assertEqual(
unpackTID(addTID(packTID(((2010, 11, 30, 23, 59), 2**32 - 1)), 1)), unpackTID(addTID(packTID((2010, 11, 30, 23, 59), 2**32 - 1), 1)),
((2010, 12, 1, 0, 0), 0)) ((2010, 12, 1, 0, 0), 0))
def testTransactionLock(self): def testTransactionLock(self):
......
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