Skip to content

Timestamp-orderable UUIDs for Python, written in Rust.

License

Notifications You must be signed in to change notification settings

isaacharrisholt/uuidt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UUIDT

Timestamp-orderable UUIDs for Python, written in Rust.

Installation

pip install uuidt

Usage

import uuidt

# Create a new UUIDT
u = uuidt.new('my-namespace')

# Print the UUIDT properties
print(u.namespace)
print(u.timestamp)
print(u.hostname)
print(u.random_chars)

# Convert to a string
print(str(u))

# Extract the timestamp from a UUIDT string
print(uuidt.extract_timestamp("cr3su3qh-4ium-00bk-00ip-vqlpgpomk3dv"))
# 1678562753992474990

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.

About

Timestamp-orderable UUIDs for Python, written in Rust.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages