ksuid.net
Back

timeflake generator

Recommended

Click on an ID to copy.

PropertyValue
Bit Length128
Output Length22 chars
Encodingbase62
SortableYes
TimestampedYes
MonotonicNo
Crypto RandomYes

Timeflake is a 128-bit, roughly time-ordered, globally unique identifier format created by Anthony Sottile and released as an open-source Python library. The design draws direct inspiration from several established ID schemes -- Twitter's Snowflake for time-sortability, Instagram's sharded ID system for simplicity, and Firebase's PushID for coordination-free generation using randomness. Timeflake's goal is to combine the best aspects of these approaches into a single format that is sortable, compact, and directly compatible with standard UUID storage in relational databases. By using 48 bits of millisecond-precision timestamp and 80 bits of cryptographically secure randomness, Timeflake achieves global uniqueness without requiring worker-ID assignment, centralized coordination, or any shared state between generators.

The motivation behind Timeflake is pragmatic. Many applications need identifiers that sort chronologically for efficient database indexing, but they also need the operational simplicity of random generation -- no machine registries, no node configuration, no sequence counters to persist. Timeflake delivers both by leaning on the statistical improbability of collision across 80 random bits (the same randomness budget as a ULID) while embedding enough timestamp precision to keep IDs roughly ordered. The "roughly" qualifier is important: two Timeflakes generated within the same millisecond on different machines will share identical timestamp prefixes but differ in their random suffixes, so their relative order within that millisecond is arbitrary. Across millisecond boundaries, however, ordering is strict and reliable.

How Timeflake Works

A Timeflake is a 128-bit value divided into two contiguous fields with no version bits, variant markers, or reserved regions.

The high 48 bits store a Unix timestamp in milliseconds. Forty-eight bits of millisecond precision provide a range of approximately 8,919 years from the Unix epoch (January 1, 1970), meaning the format will not exhaust its timestamp space until roughly the year 10889. This is substantially more headroom than Snowflake's 41-bit timestamp (approximately 69 years) or TSID's 42-bit timestamp (approximately 139 years). The timestamp is always derived from the system clock at generation time.

The low 80 bits are filled with data from a cryptographically secure pseudorandom number generator (CSPRNG). This randomness serves as the sole uniqueness guarantee: there is no worker ID, no sequence counter, and no node configuration. With 80 bits of randomness per millisecond window, the probability of collision between two independently generated Timeflakes within the same millisecond is approximately 1 in 2^80 (about 1.2 x 10^24), which is negligible for virtually all practical workloads.

The generation algorithm is stateless and straightforward. The generator reads the current system time in milliseconds, converts it to a 48-bit big-endian integer, generates 80 bits of random data, concatenates the two, and returns the resulting 128-bit value. There is no mutex, no persistent counter, and no inter-process communication. This simplicity makes Timeflake easy to implement correctly in any language.

One of Timeflake's distinguishing features is its support for multiple output representations from the same underlying 128-bit value. The base62 encoding produces a 22-character alphanumeric string using digits 0-9, lowercase a-z, and uppercase A-Z. This representation is compact and URL-safe, making it ideal for API responses and user-facing contexts. The hexadecimal encoding produces a 32-character lowercase hex string, which can be stored directly in UUID columns by inserting the standard 8-4-4-4-12 hyphen pattern. The UUID representation formats the same 128 bits as a standard UUID string, enabling seamless storage in PostgreSQL's uuid type, MySQL's BINARY(16), or any other system that expects UUID values. Finally, the integer representation exposes the raw 128-bit unsigned integer, useful for languages and databases that support large integer types natively.

This multi-format flexibility means developers can adopt Timeflake without changing their database schema. If a system already uses UUID primary keys, Timeflakes can be stored directly in those columns with no migration required, and existing UUID indexes will continue to work correctly.

Use Cases

Primary keys in relational databases with UUID columns. Many applications begin with UUIDv4 primary keys for global uniqueness but later discover that random UUIDs cause B-tree index fragmentation and poor write performance. Switching to Timeflake provides time-ordered inserts that append to the end of the index, dramatically improving write throughput and reducing page splits, while the UUID-compatible format means no schema migration is needed.

Distributed microservice architectures. In systems where dozens or hundreds of microservices independently create records, coordinating worker IDs for a Snowflake-style generator adds operational complexity. Timeflake removes this burden entirely: each service generates IDs using only its local clock and random source. The 80-bit random field provides ample collision resistance even at high concurrency, and the timestamp prefix ensures that records from different services remain globally sortable.

Event sourcing and append-only logs. Event-sourced systems append immutable events to a log and reconstruct state by replaying them in order. Timeflake IDs serve as natural event identifiers that encode their creation time and sort correctly without a separate sequence column. The base62 representation keeps event IDs short in log output and debugging tools, while the integer representation enables efficient binary storage in high-performance event stores.

Mobile and offline-first applications. Applications that generate records on a client device before syncing to a server need IDs that are globally unique without server coordination. Timeflake's stateless, random-based generation is well suited to this scenario. When records eventually reach the server, their Timeflake IDs preserve approximate creation order, simplifying conflict resolution and timeline reconstruction.

Comparison with Alternatives

Timeflake and ULID share an identical bit layout: 48 bits of millisecond timestamp followed by 80 bits of randomness, for a total of 128 bits. The primary differences lie in encoding and specification. ULID uses Crockford base32 encoding to produce a 26-character case-insensitive string, while Timeflake uses base62 to produce a shorter 22-character case-sensitive string. ULID also defines a monotonic extension that increments the random component for IDs generated within the same millisecond, guaranteeing strict ordering even within a single millisecond. Timeflake has no monotonic mode, so same-millisecond IDs are ordered randomly. If sub-millisecond ordering matters, ULID's monotonic variant is preferable; if shorter strings and UUID compatibility are priorities, Timeflake has the edge.

Compared to UUID v7, Timeflake offers a similar value proposition -- timestamp-prefixed 128-bit identifiers that fit in UUID columns -- but without the overhead of the UUID specification's version and variant bits. UUID v7 reserves 6 bits for version (0111) and variant (10) markers, slightly reducing the available randomness. The advantage of UUID v7 is standards compliance: any system that validates UUID format will accept a v7 value, whereas a Timeflake stored in a UUID column technically violates the version and variant conventions. For systems where strict UUID compliance is required, UUID v7 is the safer choice. For systems where only the 128-bit storage format matters, Timeflake's simplicity and shorter base62 representation may be more appealing.

When compared to KSUID, Timeflake differs in timestamp precision and total size. KSUID uses a 32-bit second-precision timestamp with 128 bits of randomness, totaling 160 bits encoded as a 27-character base62 string. KSUID's larger random component provides stronger collision resistance, but its second-level granularity means IDs generated within the same second are not time-ordered. Timeflake's millisecond precision provides tighter ordering, and its 128-bit size makes it compatible with UUID columns, whereas KSUID requires a dedicated text or binary column.

Code Examples

import { Timeflake } from 'timeflake';
const tf = Timeflake.random();
console.log(tf.base62);

Frequently Asked Questions

What is a Timeflake?

A Timeflake is a 128-bit, time-sortable unique identifier that combines a 48-bit Unix timestamp in milliseconds with 80 bits of cryptographically random data. It is designed as a modern alternative to UUIDs, offering chronological sortability while maintaining strong collision resistance.

Is Timeflake compatible with UUID?

Yes, Timeflakes are 128 bits in length, making them storage-compatible with UUID columns in most databases. They can be stored in UUID-type fields, binary(16) columns, or as their base62-encoded string representation, providing a smooth migration path for systems already using UUIDs.

How does Timeflake compare to ULID?

Timeflake and ULID share the same core concept of combining a timestamp with randomness in a 128-bit value. The main differences lie in their encoding and ecosystem support. Timeflake uses base62 encoding by default and originated in the Python ecosystem, while ULID uses Crockford Base32 and has broader cross-language support.

How long is a Timeflake string?

A Timeflake is 22 characters long when encoded in base62, making it shorter than a standard UUID string (36 characters with hyphens). It can also be represented as a raw 128-bit integer or a hexadecimal string, depending on the use case and storage requirements.

Is Timeflake suitable for database primary keys?

Yes, Timeflakes are well-suited for database primary keys. Their time-sorted nature means new records are always appended to the end of B-tree indexes, reducing page splits and improving write performance. They also provide enough randomness (80 bits) to safely generate IDs across multiple application servers without coordination.

Related Generators

© 2024 Carova Labs. All rights reserved