Distributed Unique ID Generator
Explore how a distributed unique ID generator packs timestamp, node identity, and sequence bits into one signed 64-bit integer.
Continue with the guided workspace
Use the paired workspace to connect this guide's mental model with live component state, visible signals, and the outcome each step or scenario is meant to prove.
A distributed unique ID generator lets a worker create a compact, sortable identifier without asking a central counter for every request. This lesson follows the bits that form one Snowflake-style value: an accepted millisecond timestamp, a unique node identity, and a sequence number for requests that share that millisecond.
Mental Model
Read the stage from left to right. The timestamp supplies the high-order payload bits, the node identity distinguishes workers, and the sequence distinguishes requests from the same worker in the same accepted millisecond. Those fields are shifted and combined into one nonnegative 64-bit integer. The leading sign bit is reserved as 0, leaving 63 payload bits for values that remain safe in signed integer storage.
Components and Why They Exist
Timestamp
The timestamp field records milliseconds since a custom epoch. It contributes 41 high-order payload bits, which makes IDs broadly time-sortable and gives the format a finite but long time range. Generate a request and inspect the epoch delta and binary timestamp field.
Node identity
The identity field combines a 5-bit datacenter value and a 5-bit worker value. Its 10 bits distinguish generators that accept requests during the same millisecond. Inspect the packed identity bits to see why two workers do not rely on the sequence counter alone.
Sequence
The sequence counter occupies 12 bits. It starts at zero for a newly accepted millisecond and increments for each additional request in that millisecond, allowing values from 0 through 4095. Use Same ms and Fill capacity to see the bounded counter and its overflow guard.
Assembled ID
The assembled value shifts the timestamp above identity and sequence, then combines all three fields beneath the reserved sign bit. Open the decoded fields after generating an ID to compare the decimal value with the field values that produced it.
Identity allocation
Identity allocation happens before a worker serves requests. Whatever allocation mechanism a deployment uses, it must ensure that concurrent workers do not receive the same datacenter-and-worker bit combination. The lesson treats this as a startup responsibility, not a request-time hop.
Clock guard
A worker remembers its last accepted timestamp. If its clock reports an earlier millisecond, it must reject the request or wait until time catches up rather than reuse timestamp and sequence bits. Use Clock rollback to inspect that guard.
Edge Cases and Misconceptions
- Reserved sign bit: The leading bit stays
0, so the packedBigIntremains nonnegative and fits signed 64-bit storage. It is not payload capacity. - Sequence overflow throttle: A 12-bit sequence counter supports 4,096 IDs per millisecond per node. If a node receives more than 4,096 requests in one millisecond, it must block the calling threads until the next millisecond tick, which is a rare bottleneck under extreme burst load.
- Clock rollback policy: A timestamp that moves backwards must not be accepted with a reused sequence. A generator can reject work or wait for time to catch up; either policy protects uniqueness for that worker.
- ULID as a coordination-free alternative: ULIDs (Universally Unique Lexicographically Sortable Identifier) use 128 bits: 48 bits of timestamp and 80 bits of randomness. They avoid machine ID coordination entirely, but double the database storage index size compared to Snowflakeβs 64-bit integers.
Common Questions
Why are distributed Snowflake IDs preferred over random UUIDs for databases?
Snowflake IDs are 64-bit integers that are chronologically sortable (monotonically increasing). In contrast, random 128-bit UUIDv4 values cause severe B-tree index fragmentation (page splits) in databases because inserts occur at random positions rather than at the end of the index. Snowflake IDs also consume half the storage space.
How does the sequence counter prevent collisions within the same millisecond?
Each generator node maintains a local 12-bit sequence counter. For every ID generated within the same millisecond, the counter is incremented (from 0 up to 4095). If the sequence overflows, the generator blocks requests until the system clock advances to the next millisecond, resetting the counter to 0.
Why is the machine ID leased via ZooKeeper during startup instead of hardcoded?
Hardcoding machine IDs in modern ephemeral environments (like Kubernetes or cloud auto-scaling) is highly error-prone and leads to duplicate ID generation. Leasing machine IDs dynamically from ZooKeeper at startup allows instances to claim a unique identifier automatically and release it upon shutdown.
What is the custom epoch offset and why is it used?
The Snowflake format allocates 41 bits for a millisecond timestamp. Using the standard Unix epoch (1970) would exhaust this space in a few years. A custom epoch offset (e.g., set to the company's launch date) shifts the time origin, giving the 41-bit spaces a runway of 69 years.
What happens when the 41-bit timestamp space runs out after 69 years?
Once the timestamp space overflows, the generated IDs will wrap around and conflict with previously generated IDs. Systems must coordinate a migration to a larger ID footprint (e.g., 128-bit ULID or UUIDv7) or shift the epoch base forward (which requires changing how old IDs are sorted).
How does Snowflake ensure IDs are sortable across different nodes?
The timestamp bits make IDs sortable by each worker's accepted timestamp. They cannot guarantee strict global chronology across nodes when clocks are skewed or a worker rejects or waits through its rollback policy, so use the ordering as a useful local/time-based sort rather than a universal event order.
Why not use UUIDv7 instead of Snowflake?
UUIDv7 is a 128-bit sortable identifier that is also B-Tree friendly. Snowflake's advantage is its compact 64-bit size, which fits into standard integer index types, yielding faster lookups and half the index storage footprint.