Free preview

Approach 3: The Range Handler

In one line: this is the first design that satisfies every requirement, and the idea behind it — amortize coordination by batching it — appears everywhere once you recognize it.

How it works

The flow:

  1. Application servers request a range from the central handler — for example 1–1,000,000 or 1,000,001–2,000,000.
  2. The server stores the range start and end points locally.
  3. When an ID is needed, the server assigns the current value and increments it locally.
  4. When the range is exhausted, the server requests a new range.

Example: Server 1 claims 300,001–400,000. It assigns 300,001 to the first request, then 300,002, and so on until 400,000 is assigned. Then it queries the central handler for the next available range.

This prevents duplicates and allows concurrent responses. A load balancer distributes requests across servers to manage load.

The idea worth extracting: amortize the coordination

The move generalises well beyond IDs: when coordination is unavoidable, make it rare rather than making it fast.

Approach 2 needed the database once per ID. This needs the handler once per 100,000 IDs.

The coordination did not disappear — it got amortized. A server holding a range can serve at memory speed with zero network calls, and the central handler sees a request rate five orders of magnitude below the ID rate.

That pattern — batch the expensive coordinated operation and operate locally in between — recurs constantly: connection pools, token buckets, lease-based locks, and the leases from CDNs. Naming it as a general technique is stronger than describing this one instance.

Surviving failure

The range handler tracks range status — available versus taken — in replicated storage. A failover server ensures availability if the main handler fails, recovering state from the storage checkpoint.

Note that the handler being briefly down is not an outage: every application server is still serving IDs from the range it already holds. The system only stalls if a server exhausts its range while the handler is unavailable — which is why range size is a real tuning decision.

Pros and cons

Detail
ProsScalable, available, and guarantees unique IDs. It also satisfies the requirement for 64-bit numeric IDs
ConsIf an application server fails, its unassigned range is lost, creating gaps in the ID sequence. Mitigate by allocating smaller ranges — though they must be large enough to minimize network requests to the central handler

Adding a data center

A common follow-up: how would you adapt range allocation when bringing a new data center online?

The clean answer is to partition the range space by data center rather than handing out globally interleaved ranges — give each DC its own high-order block, so allocations never need cross-DC coordination and a partition between DCs cannot cause a collision. Each DC then runs its own handler over its own block.

This is the same reasoning as home regions in the Foundations walkthrough: make the partition boundary match the failure boundary.

Scorecard

UniqueScalableAvailable64-bit numeric ID
Using UUID
Using database servers
Using a range handler

All four requirements satisfied. The solution generates unique 64-bit numeric IDs suitable for primary keys.

However, these IDs do not guarantee time-based ordering — which is where the second half of this chapter begins.

Key takeaway

Batching coordination turns a per-ID dependency into a per-range one, which is what makes the range handler both available and genuinely unique. The remaining gap is ordering: nothing about a range tells you when an ID was issued.

Interview signal by level

LevelWhat a strong answer sounds like
L4"A service hands out ID ranges to each server."
L5Explains why it works: "each server owns a block, so it assigns locally with no coordination until the block runs out."
Staff+Generalizes and tunes it: "it amortizes coordination — once per 100k IDs instead of per ID, the same pattern as leases or connection pools. Range size trades handler load against IDs lost on server failure, and gaps are fine unless something downstream assumes contiguity. For multi-DC I'd partition the range space per DC so a partition can't cause a collision."

Next: why unique isn't enough.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue