Case study
Design a Unique ID Generator
Generate globally unique, roughly sortable 64-bit IDs at 10K+/sec without coordination per ID.
Requirements
- Functional: generate unique ID; optionally expose batch generate
- Properties: unique across all datacenters; roughly time-ordered (sortable)
- Non-functional: 10K IDs/sec per machine; latency < 1ms local
- No central DB round-trip per ID (bottleneck)
- IDs numeric or fixed-length string (base62)
Compare to UUID v4
UUID v4 is random — not sortable and poor for B-tree indexes. Snowflake/truncated timestamp IDs give locality that improves DB insert performance.
Back-of-envelope Estimation
| Metric | Value | Notes |
|---|---|---|
| Global ID rate | 1M/sec peak | Twitter-scale order of magnitude |
| Per machine | 10K/sec | 100 machines suffice with headroom |
| ID size | 64 bits (8 bytes) | Fits in BIGINT; 18 decimal digits max |
| Clock drift budget | < 5 ms | NTP sync required per host |
API Design
| Endpoint | Description |
|---|---|
| GET /v1/id | Returns { id: 1234567890123456789 } |
| GET /v1/id/batch?count=100 | Returns array; max 1000 per request |
| Internal: allocate_worker_id | ZooKeeper/etcd lease assigns machine ID 0–1023 |
Snowflake Bit Layout
| Field | Bits | Range |
|---|---|---|
| Unused/sign | 1 | 0 |
| Timestamp (ms since epoch) | 41 | ~69 years |
| Datacenter + machine ID | 10 | 1024 machines |
| Sequence | 12 | 4096 IDs/ms per machine |
Per millisecond: increment sequence. If sequence overflows, spin until next ms. On clock backward jump, wait or error — never reuse timestamp+sequence pairs.
High-level Design
App servers ──► ID Generator lib (embedded) ──► local clock + worker_id
│
└── optional central service for worker_id lease only
ZooKeeper/etcd: /workers/{dc}/{host} → worker_id (ephemeral node)Prefer embedded library (Twitter Snowflake style) over RPC per ID — eliminates network hop. Central service only coordinates worker_id assignment on startup.
Deep Dive: Alternatives
| Approach | Pros | Cons |
|---|---|---|
| DB auto-increment | Simple, strict order | Single-writer bottleneck; hard to shard |
| UUID v4 | No coordination | Random; 128 bits; index fragmentation |
| Snowflake | Sortable; high throughput | Clock dependency; machine ID management |
| Redis INCR | Easy | Single point of failure; network per ID |
Bottlenecks & Edge Cases
- Clock skew backward — refuse to generate until caught up; metric clock_rollback_events
- Worker ID exhaustion (1024 machines) — extend bits or hierarchical IDs per service
- Leap seconds / VM pause — sequence buffer absorbs brief stalls; alert on NTP drift > 1s
- Hot partition in DB if IDs used as sole shard key — combine with secondary sharding key
Failure Modes & Monitoring
| Metric | Alert threshold |
|---|---|
| ids_generated/sec per host | Approaching 4096/ms sustained |
| clock_rollback_count | > 0 per minute |
| worker_id lease renewals failed | Any failure |
| duplicate_id_detected | Critical — should never happen |
Testing uniqueness
Chaos-test clock jumps and worker restarts. Run collision detector in staging comparing ID sets across nodes.