UUIDT
Timestamp-orderable UUIDs for Python, written in Rust.
Installation
pip install uuidt
Usage
import uuidt
u = uuidt.new('my-namespace')
print(u.namespace)
print(u.timestamp)
print(u.hostname)
print(u.random_chars)
print(str(u))
print(uuidt.extract_timestamp("cr3su3qh-4ium-00bk-00ip-vqlpgpomk3dv"))
Motivation
UUIDs are great for generating unique identifiers, but they are not
necessarily time-orderable. This is a problem if you want to generate a UUID
for a new record in a database, and then use that UUID to order the records
by creation time.
Many databases avoid this problem using auto-incrementing integer IDs, but
this isn't possible in distributed databases like
CockroachDB, so a UUID is typically used as
the primary key instead.
This library generates UUIDs that are time-orderable. The first 12 alphanumeric
characters of the UUID are a nanosecond-precision timestamp which has been
base-36 encoded, so they can be sorted lexicographically. The remaining 20
characters are a combination of a namespace, the hostname of the machine that
generated the UUID, and a random string.
Technically, UUID1s are also time-orderable, but they are not guaranteed to
be ordered by creation time, and it can be difficult to extract the
timestamp from a UUID1.
Why Rust?
Mostly as a learning opportunity for me, though also for speed. The Rust
implementation is significantly faster than the Python implementation, which
used Numpy to convert to base-36.
What if I don't want the Rust implementation?
UUIDT should be installable as a wheel on most systems, but if you hate Ferris
and want to use the Python implementation instead, here's some equivalent
code:
import random
import socket
import time
import numpy as np
BASE = 36
DIVISOR = BASE - 1
CHARACTERS = list('0123456789abcdefghijklmnopqrstuvwxyz')[:BASE]
class UUIDT:
def __init__(self, namespace: str, timestamp: int, hostname: str, random_chars: str):
self.namespace = namespace
self.timestamp = timestamp
self.hostname = hostname
self.random_chars = random_chars
def __str__(self):
hostname_enc = sum(self.hostname.encode('utf-8'))
namespace_enc = sum(self.namespace.encode('utf-8'))
timestamp_str = np.base_repr(self.timestamp, 36).lower()
hostname_str = np.base_repr(hostname_enc, 36).lower()
namespace_str = np.base_repr(namespace_enc, 36).lower()
return (
f'{timestamp_str[:8]}-{timestamp_str[8:]}-{hostname_str:0>4}-'
f'{namespace_str:0>4}-{self.random_chars}'
)
def new(namespace: str) -> UUIDT:
timestamp = time.time_ns()
hostname = socket.gethostname()
random_chars = ''.join(random.choices(CHARACTERS, k=4))
return UUIDT(namespace, timestamp, hostname, random_chars)
License
MIT
Using UUIDT in your project
While UUIDT is MIT licensed, I'm really curious to seeing the projects that
use it! If you use UUIDT in your project, I'd love to hear about it! Please
let me know by either opening an issue or sending me an email at the address
in the pyproject.toml
file.
Contributing
Contributions are welcome! Just open an issue or a pull request.