Concept Drills: 16 Sequencer Probes
ID generation appears as a standalone question and as a follow-up inside URL shorteners, tweet stores, and tracing systems. These are the probes that get asked.
Requirements and UUIDs
1. Why can't you use a database auto-increment column? · L4 · Testing: the core problem
Because auto-increment needs a single authority deciding the next value, and a distributed system doesn't have one. Across sharded tables or multiple nodes generating independently, there's nothing to serialize the counter — so you either coordinate explicitly or design so coordination isn't needed.
2. Is 64 bits enough? · L5 · Testing: whether they'll do the arithmetic
Yes, comfortably. 2^64 is about 1.84 × 10^19. At a billion IDs a day that's 365 billion a year, so 2^64 divided by 365 × 10^9 is roughly 50.5 million years. The constraint isn't exhaustion, it's how you spend the bits.
3. Why not just use UUIDs? · L5 · Testing: knowing the specific failures
Five reasons. They're 128 bits, so they fail a 64-bit requirement outright. They're non-numeric. They're only probabilistically unique, not deterministically. They're not monotonically increasing, so no time-ordering. And random keys are the worst case for B-tree locality — consecutive inserts scatter across the index instead of appending to a hot page.
4. What do UUIDs get right that nothing else does? · Staff · Testing: fair evaluation
Zero coordination. Every other approach depends on something — a database, a range handler, a clock, a worker-ID assignment. A server with no network connectivity can still generate valid UUIDs indefinitely. That's a genuinely strong availability property, and it's why UUIDs remain correct whenever the size constraint doesn't apply.
Database and range handler
5. Increment-by-m across m servers. What breaks? · Staff · Testing: the specific failure
Changing
m. With m=3, A emits 1,4,7 and C emits 3,6,9. If B dies and m becomes 2, A's next ID is 7+2=9 — which C already issued. Duplicate. It's structurally the same trap ashash(key) % mpartitioning: any scheme whose correctness depends on the current node count breaks when membership changes, and membership always changes.
6. How does a range handler work, and why is it better? · L5 · Testing: the mechanism
A central service hands out blocks — say 300,001 to 400,000. A server claims a block, assigns IDs from it locally, and only returns to the handler when the block is exhausted. Uniqueness is guaranteed because no two servers hold the same block, and the handler isn't on the hot path because it's consulted once per 100,000 IDs rather than per ID.
7. What's the general principle behind the range handler? · Staff · Testing: transfer
Amortize coordination by batching it. The coordination doesn't disappear, it just happens far less often, so the shared component sees a request rate orders of magnitude below the operation rate. The same pattern shows up in connection pools, token buckets, lease-based locking, and the CDN leases — recognizing it as a technique matters more than this one instance.
8. What does the range handler cost you? · L5 · Testing: the downside
Gaps. If a server dies holding an unassigned range, those IDs are never issued. That's usually fine — uniqueness still holds, you just skip numbers — unless something downstream assumes contiguity. Smaller ranges reduce the loss but increase handler traffic and exposure to handler downtime.
Time and Snowflake
9. Why can't you use a UNIX timestamp as the ID? · L5 · Testing: the arithmetic
Millisecond granularity gives 24 × 60 × 60 × 1000 = 86.4 million per day, an order of magnitude below a billion. And two requests arriving in the same millisecond on the same server get the same value — which happens constantly above 1,000 requests per second. The ceiling is clock resolution, not capacity.
10. Give me the Snowflake bit layout. · L5 · Testing: recall with justification
1 sign bit always zero so it's a positive integer, 41 bits of timestamp giving about 69 years, 10 bits of worker ID for 1,024 workers, and 12 bits of sequence for 4,096 IDs per millisecond per worker, resetting each millisecond. Total 64. Each field answers a specific failure: sequence for same-millisecond collisions, worker for cross-server ones, timestamp for ordering.
11. Why set a custom epoch? · Staff · Testing: a practical detail
The 41 bits count milliseconds since the epoch, so the budget starts when you say it does. Using Twitter's 2010 default in a system launched in 2024 silently burns fourteen years before you issue an ID. Setting the epoch to your launch date recovers them, and it costs nothing.
12. A Snowflake worker's clock jumps backwards. What happens? · Staff · Testing: the real weakness
It can reissue a millisecond it already used, producing duplicates or violating causality. The standard defence is to refuse to generate while current time is behind the last timestamp used — stall rather than risk a duplicate. I'd also configure NTP to slew rather than step on ID-generating hosts, correcting gradually instead of jumping. This is why Snowflake is only weakly causal.
Logical clocks and TrueTime
13. Lamport clocks — what do they give you, and what don't they? · Staff · Testing: the asymmetry
Each process keeps a counter, increments before local events, sends it with messages, and on receipt sets it to max(local, received) + 1. That guarantees if
acausedbthen L(a) < L(b). But the converse doesn't hold — L(a) < L(b) doesn't prove causality, since the events might be concurrent. So it gives a partial order consistent with happened-before, and can't detect concurrency.
14. Why not use vector clocks in the ID? · Staff · Testing: the size wall
They do capture true causality — each node tracks a counter per node, so you can compare two vectors and tell whether one dominates or they're concurrent. But they're O(n) in cluster size. With 53 bits for the whole vector you get a handful of bits per node across a small cluster, and in a large system — or one where every browser is a node — it's hopeless. That's why the scorecard says "can exceed" rather than yes or no.
15. What does TrueTime actually return? · L5 · Testing: the key idea
An interval
[earliest, latest], not a point, with a guarantee that the real time lies inside it. Google keeps the uncertainty — epsilon — under about 7 ms using GPS receivers and atomic clocks, with a daemon running Marzullo's algorithm to intersect readings from multiple masters. Two events are ordered only if their intervals don't overlap; if they do, the API reports the order as undetermined rather than guessing.
16. Why not use a globally consensus-ordered counter? · Staff · Testing: the throughput reality
Because consensus is slow. Spanner reports roughly 100 operations per second for single-row read-modify-write transactions, against a requirement of about 11,600 per second — two orders of magnitude short. Strict global ordering requires round trips you can't amortize away, which is exactly why the practical answer relaxes ordering and takes Snowflake's weak guarantee instead.
Self-check
| You should be able to | Covered in |
|---|---|
| Explain why auto-increment doesn't survive distribution | Lesson 1 |
| Derive that 64 bits lasts ~50 million years | Lesson 2 |
| Name all five UUID drawbacks and their one strength | Lesson 3 |
| Show how increment-by-m breaks on membership change | Lesson 4 |
| Explain amortized coordination as a general technique | Lesson 5 |
| Separate uniqueness from causality | Lesson 6 |
| Compute the 86.4M/day timestamp ceiling | Lesson 7 |
| Reproduce and justify the Snowflake bit layout | Lesson 8 |
| State what Lamport clocks can and cannot prove | Lesson 9 |
| Explain interval-based time and non-overlap ordering | Lesson 10 |
| Reproduce the seven-approach scorecard | Lesson 11 |
The cheat sheet next compresses the chapter onto one page.