Skip to content

Instantly share code, notes, and snippets.

@gigamonkey
Last active September 5, 2017 23:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gigamonkey/50bf1d44020d167df77a4c209f14892b to your computer and use it in GitHub Desktop.
Save gigamonkey/50bf1d44020d167df77a4c209f14892b to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
from time import sleep
from datetime import datetime, timezone
# Snowflake like ID generator that generates a 53-bit id suitable for sending as a number in JSON.
TIME_BITS = 40
COUNTER_BITS = 10
MACHINE_BITS = 3
assert TIME_BITS + COUNTER_BITS + MACHINE_BITS <= 53
TIME_MASK = (2 ** TIME_BITS) - 1
COUNTER_MASK = (2 ** COUNTER_BITS) - 1
MACHINE_MASK = (2 ** MACHINE_BITS) - 1
EPOCH = int(datetime(2016, 11, 8, tzinfo=timezone.utc).timestamp() * 1000)
def since_epoch():
dt = datetime.utcnow()
return int(dt.replace(tzinfo=timezone.utc).timestamp() * 1000) - EPOCH
def make_id(time, counter, machine_id):
assert 0 <= machine_id <= 7, "Invalid machine id: {}".format(machine_id)
t = (time & TIME_MASK) << (COUNTER_BITS + MACHINE_BITS)
c = (counter & COUNTER_MASK) << MACHINE_BITS
return t + c + machine_id
def ids(machine_id):
counter = 0
last_ts = 0
while True:
ts = since_epoch()
# Whenever the counter rolls over wait a bit to make sure the
# timestamp has advanced at least a millisecond.
if counter == 0:
while ts == last_ts:
sleep(0.0001)
ts = since_epoch()
last_ts = ts
yield make_id(ts, counter, machine_id)
counter += 1
counter %= (2 ** COUNTER_BITS)
def age(id):
return id >> (COUNTER_BITS + MACHINE_BITS)
def counter(id):
return (id >> MACHINE_BITS) & COUNTER_MASK
def machine(id):
return id & MACHINE_MASK
if __name__ == '__main__':
g1 = ids(0)
for _ in range(1030):
id = next(g1)
print("{}: age: {}; counter: {}; machine: {}".format(id, age(id), counter(id), machine(id)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment