Admin-friendly, fixed-length, unique-enough, fast-enough, monotonic, identifiers.
Project description
rvid.seq a.k.a RID
Admin-friendly, fixed-length, unique-enough, fast-enough, monotonic, identifiers.
Optimized for small contexts where you only very rarely need more than a few million unique ID:s per day and prefer those ID:s to carry a bit of meaning and be easy to read, remember and speak out loud.
It is essentially a compact, semi-high-resolution timestamp, with a bit of logic for avoiding duplicates in a cross-platform manner.
from rvid.seq import RID
my_id = RID.next()
>>> '0Q1-USW-YU8'
Definition
A RID is an approximation of millisecond Unix epoch time, encoded in base-35 with a readability-optimized character set and presented in exactly three groups of exactly three alphanumerics.
The characters used for encoding is: 0123456789ABCDEFGH*JKLMN#PQRSTUVWXY.
Note that I and O are replaced with * and #.
Compatibility
The code is tested OK with CPython 3.6 and 3.11, and PyPy 7.3.1.
The wheel building may however have some issues on Python 3.6, since current versions of pip no longer support interpreters that old.
Building and installing
Via PyPI: pip install rvid.seq
For local build it is not much harder:
hg clone https://hg.sr.ht/~rvid/rvid.seq
cd rvid.seq
pip wheel . -w dist
The build produces a pure python wheel with no dependencies.
Troubleshooting
⚠️ pip can't find 'hg' command
There seems to be a bug in setuptools_scm or mercurial, which on at least some systems mean that you must run pip via a virtualenv where mercurial is pre-installed:
python3 -m venv venv
venv/bin/pip install mercurial
venv/bin/pip wheel . -w dist
Usage
Command-line
The command-line tool rid lets you get the RID for current time or do
translations:
(venv) [user@host ~]$ rid
0Q1-X1G-HQ6
(venv) [user@host ~]$ rid 0Q1-X1G-HQ6
0Q1-X1G-HQ6 corresponds to 2023-02-14 18:23:34
(venv) [user@host ~]$ rid $(date +%s)
1676397310 corresponds to 0Q1-X2P-P00
In Python code
For programmatic usage, you would import the RID singleton in your software,
and use its .next() method to get a value:
from rvid.seq import RID
id = RID.next()
If you application is multi-threaded, you can use the thread-safe version. It works exactly the same but is slower due to locking-overhead.
from rvid.seq import RID_ThreadSafe as RID
id = RID.next()
Safe resumption
from rvid.seq import epoch2rid, RID
[RID.next() for _ in range(2)]
>>> ['0Q1-UX2-XYS', '0Q1-UX2-XYT']
RID.adjust_top_from_rid( epoch2rid( time.time() + 1000 * 1000 ) )
RID.next()
>>> '0Q2-EYH-G0Q'
If picking up work from e.g. a saved file in a new session, you will need to seed the RID-generator with the max-RID from that saved file. (Or else stuff gets weird if your clock has moved backwards).
Scalability
By the year 2525, if man is still alive, you still have 18 billion id:s left in the number space before wrapping over.
RID is unlikely to be too slow. A Ryzen 3900X generates about 430k RID:s/second with CPython 3.9 and 9.5M RID:s/second with Pypy 7.3.1.
Background and motivations
I have found UUID:s to be annoying at times. They take up lots of screen space and are difficult to read. And I always end up having to read them when debugging one thing or another. They are not optimized for direct human consumption.
On the topic of anchoring to system time
I've chosen to anchor the number sequence on system time, because it adds some extra context information to each ID. I'm not certain that I will stick with that, because it also adds a bit of complication to the implementation and some mental overhead when parsing the ID:s.
Skipping the system-time anchor would allow dropping one of the triplets and still get a reasonable 1.8 billion unique values.
from rvid.seq.common import rid2epoch_ms
rid2epoch_ms("000-YYY-YYY")
>>> 1838265624
On the other hand, decoupling time from ID would mean that you must have and must read a timestamp to have the slightest idea about when an object was created. I'm not 100% sure that it would be net gain in mental overhead.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rvid_seq-1.0.0-py3-none-any.whl.
File metadata
- Download URL: rvid_seq-1.0.0-py3-none-any.whl
- Upload date:
- Size: 15.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0ffc3cd5ff7002885a1c0dbc12a84d30752461d338f028b00862443c24d5e0f
|
|
| MD5 |
8c8b9f2052f423bcd10f9bd1a4e571a6
|
|
| BLAKE2b-256 |
166a08ae62fb9090fa3a2826b163104b2b9fc2c241b2e806a7eeebc4beb4b776
|