Web fundamentals, an interactive architecture walkthrough
Distributed unique ID generator: how the architecture works
How a distributed ID generator packs a timestamp, worker id and sequence into 64 bits so ids are unique and roughly time-ordered.
This is a learning explainer based on public engineering writing and general design practice. It is simplified, it is not the actual architecture of any company, and it may be inaccurate.
Interactive diagram
Overview
A distributed unique ID generator gives every new record an id that no other machine will ever issue, without a central counter on the hot path. The common design packs a millisecond timestamp, a worker number and a per-millisecond sequence into a 64-bit integer. The only coordination is handing each generator a unique worker number at startup, after which every node issues ids independently.
Because the timestamp is in the high bits, ids sort roughly by creation time, which keeps database index inserts sequential and lets an id stand in for a created-at value. The generator can run as a small network service or as a library inside each caller. Older and newer alternatives are shown alongside: a ticket database that hands out ranges, and UUIDv7 or ObjectId-style ids that need no coordination. This is a simplified learning design, not any company's production system.
Trade-offs and what changes at scale
Ordering depends on clocks, so ids from different nodes are only roughly sorted and a clock that moves backwards must stop the node rather than risk duplicates. 64 bits fit native integer columns and are compact, but the bit split fixes the limits: about 69 years from the epoch, 1,024 workers and 4,096 ids per worker per millisecond. Ids reveal creation time and volume, so they should not be the only protection on a public URL. Random 128-bit ids avoid coordination entirely but are larger and, when fully random, scatter index writes.
How it works, step by step
The walkthrough above, as text. Each step lights up the components named under it.
When one of the ID Generator Nodes starts, it leases a worker id from ZooKeeper as an ephemeral node that disappears if the process dies. A unique worker id is the only coordination the scheme needs; after that each node issues ids on its own.
Each node keeps its clock in step with the NTP Servers. Ids are only as ordered as the clocks that produce them, so clocks across nodes need to agree within a few milliseconds.
The Order Service takes ids from its local ID Buffer, which refills a batch through the Internal Load Balancer, and one of the ID Generator Nodes packs the current millisecond, its worker id and a sequence number into each id. Batching spreads one network round trip over many ids.
Within one millisecond each of the ID Generator Nodes can issue 4,096 ids (the 64-bit layout's 12-bit sequence); after that it waits for the next millisecond. With 1,024 workers that is about 4 million ids per millisecond across the fleet.
If the system clock jumps backwards, the Clock Guard makes the node refuse to issue ids until time catches up. Issuing ids with an old timestamp could repeat an id already handed out.
Because ids rise with time, new rows in the Orders DB land at the right edge of the primary key B-tree instead of at random pages. Random UUIDv4 keys scatter inserts across the index and cause more page splits.
The Message Service uses ids as the clustering key in the Messages DB, so "messages before id X" is a range scan and no separate timestamp column is needed for ordering. Ids from different nodes are only roughly ordered, within the clock skew between them.
The Upload Service instead runs the Embedded Generator in its own process, with a worker id leased the same way. This removes the network hop, at the cost of every caller needing a unique worker id.
An older alternative is the Range Allocator: it bumps a counter in the Ticket DB by 1,000 and the caller hands out that block locally. Ids are dense and short, but they reveal volume and are only ordered per block.
Ids also key events on the Event Stream and rows in the Analytics Store, where the creation time can be read from the id itself. UUIDv7 and ObjectId-style ids give similar ordering without a worker id, using more bits.
The ID Inspector decodes any id back into its timestamp and worker, which helps when tracing a record to the node that created it. Anyone who sees the ids can do the same, so they leak creation time and rough volume.
Observability tracks ids issued per second, sequence exhaustion and clock offset per node. A node that keeps waiting for the next millisecond is a sign it needs more workers beside it.
Sequence diagram
The same walkthrough as a UML sequence diagram, one lane per layer or group (18 components in all), left to right in the order a request reaches them, numbered by step. A loop on a lane is a step that stays inside that group. Solid arrows are synchronous calls, dashed are asynchronous.
Key components
Coordinationdomain
- NTP Servers: keep node clocks in sync
- ZooKeeper: leases unique worker ids (zookeeper)
ID Servicedomain
- Clock Guard: refuses ids if the clock goes back
- Embedded Generator: same layout, runs in the caller
- ID Generator Nodes: timestamp + worker id + sequence (grpc)
Callerslayer
- ID Buffer: prefetched batch of ids per caller
- Message Service: needs sortable message ids
- Order Service: needs an id per order
- Upload Service: embeds the generator as a library
Internal Load Balancer
- Internal Load Balancer: spreads calls across ID nodes
Observability
- Observability: ids/sec, clock skew, sequence exhaustion
Where ids end uplayer
- Analytics Store: time derived from the id (clickhouse)
- Event Stream: events keyed by id (kafka)
- Messages DB: clustering key on id (cassandra)
- Orders DB: B-tree primary key on id (postgresql)
Alternativesdomain
- Range Allocator: hands out blocks of 1,000 ids
- Ticket DB: auto-increment counter row (mysql)
ID Inspector
- ID Inspector: decodes time and worker from an id
Sources
Public writing this explainer draws on. Read these for what the real systems actually do.
Make it yours
Copy this diagram and its walkthrough into your own workspace, then change anything: swap Postgres for DynamoDB, add a cache, rewrite the narration. Free, and it uses no AI credits.
Related walkthroughs
- URL shortenerHow a link shortener creates unique short keys, serves redirects from a cache in a few milliseconds, and counts clicks off the redirect path.
- Distributed rate limiterHow an API rate limiter shares token buckets across gateways, answers 429 with a retry hint, fails safely and sheds load when the system is stressed.
- Distributed cacheHow a distributed cache places keys with consistent hashing, replicates shards, evicts entries, invalidates on writes and resists stampedes.
This is a learning explainer based on public engineering writing and general design practice. It is simplified, it is not the actual architecture of any company, and it may be inaccurate. Real systems differ in the details. Browse the library.