Skip to content

Instantly share code, notes, and snippets.

@strizhechenko
Last active June 23, 2023 14:08
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 strizhechenko/7462de340756043455da8fb12f4d619d to your computer and use it in GitHub Desktop.
Save strizhechenko/7462de340756043455da8fb12f4d619d to your computer and use it in GitHub Desktop.
Компактные человекочитаемые уникальные идентификаторы для небольших несинхронизирующихся распределённых систем.
from base64 import b64encode
from time import time
import os
pid = os.getpid()
if pid == 1: # для поддержки запуска в контейнерах
import socket
hostname = socket.gethostname()
pid = ord(hostname[0]) + ord(hostname[-1])
def uuid05(machines=10, ttl=2 * 86400, precision=0) -> int:
"""
Компактные человекочитаемые уникальные идентификаторы для временных объектов
в небольших несинхронизирующихся распределённых системах. Если объекты живут вечно - вам к nanoid.
Если нужно разом сгенерировать несколько uid - сгенерируйте один и приклейте порядковый номер объекта к нему.
%timeit uuid05() - 2.1 µs ± 2.08 ns per loop @ 3.2GHz
:arg machines - число машин в системе;
:arg ttl - через сколько секунд объект с uid, который может вернуть фукнция, гарантированно исчезает из системы;
:arg precision (optional) - переопределение точности, если объекты создаются часто. Максимум - 6.
>>> assert uuid05(2, 3600) <= 132400
>>> assert uuid05(10, 3600) <= 932400
>>> assert uuid05(10, 3600, 2) <= 9356400
>>> assert uuid05(10, 2 * 86400) <= 91555200
>>> assert uuid05(16, 3600) <= 15356400
>>> assert uuid05(16, 1800) <= 15178200
"""
precision = min(6, precision or int(machines ** (1 / 4)))
run_id = pid % (machines - 1)
time_id = int((time() % ttl) * 10 ** precision)
return int(f'{run_id}{time_id}')
def _uuid05_maxval(machines=10, ttl=2 * 24 * 60 * 60, precision=None) -> int:
"""
Функция, вычисляющая максимальное значение uuid05 при заданных параметрах.
:arg machines - число машин в системе;
:arg ttl - секунды, спустя которые объект с uid, который может вернуть фукнция, гарантированно исчезнет из системы;
:arg precision (optional) - можно переопределить точность, если объекты создаются очень часто. Не больше 6.
"""
precision = min(precision or int(machines ** (1 / 4)), 6)
run_id = machines - 1
time_id = ttl * ((10 ** precision) - 1)
return int(f'{run_id}{time_id}')
def int2b64(uid: int, *args, **kwargs) -> str:
"""
Если вы используете uuid05 как строку, хочется ещё компактнее, а число не имеет значения - эта функция для вас.
Под капотом используется b64encode, которому можно прозрачно прокинуть аргументы.
Так-как декодирование не подразумевается, невозбранно избавляемся от паддинга.
%timeit b64(uuid05()) - 2.87 µs ± 13.9 ns per loop @ 3.2GHz
>>> int2b64(3136979908, altchars=b'_-')
'uvqDxA'
>>> int2b64(29136919548, altchars=b'_-')
'BsiyG-w'
"""
uid_as_bytes = uid.to_bytes((uid.bit_length() + 7) // 8, byteorder='big')
return b64encode(uid_as_bytes, *args, **kwargs).decode().replace('=', '')
if __name__ == '__main__':
print(_uuid05_maxval(2, 3600))
print(_uuid05_maxval(10, 3600))
print(_uuid05_maxval(10, 3600, 2))
print(_uuid05_maxval(16, 3600))
print(_uuid05_maxval(16, 1800))
u = uuid05(100, 2 * 24 * 60 * 60)
print(u, int2b64(u, altchars=b'_-'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment