SYSTEM DESIGN:Lesson 14: Snowflake IDs at Scale

Mastering lesson 14: snowflake ids at scale concepts and implementation.

4096 new IDs per millisecond — per machine

"Our order service was doing 8,000 creates per second during Big Billion Days. Postgres nextval() became the bottleneck — every insert waited on the same sequence lock. We moved to Snowflake-style IDs and the queue vanished."

When a single database sequence becomes the choke point, you need IDs that any machine can generate independently without calling a central coordinator — while staying unique, sortable, and compact.

Twitter's Snowflake is the canonical answer. This chapter covers the bit layout, clock skew, datacenter bits, throughput math, and alternatives (Sonyflake, ULID). We tie it back to the URL shortener — because every short code you generate is ultimately a Snowflake or counter in disguise.

Prerequisites: Counting Without Collisions for UUID vs auto-increment basics.

Snowflake ID meets two clocks that disagree

Snowflake ID meets two clocks that disagree

4096 IDs per millisecond per machine — unless NTP has other plans.

Snowflake bit layout — 64 bits, zero coordination

A Snowflake ID is a 64-bit integer. Each bit region encodes metadata so two machines never produce the same ID — without talking to each other.

Twitter Snowflake 64-bit ID structure

Twitter Snowflake 64-bit ID structure

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
├─┬───────────────────────┬───────────┬───────────┬───────────┤
│0│   timestamp (41 bits) │ datacenter│  machine  │ sequence  │
│ │   ms since epoch      │  (5 bits) │  (5 bits) │ (12 bits) │
└─┴───────────────────────┴───────────┴───────────┴───────────┘
FieldBitsRangePurpose
Sign1Always 0Keeps ID positive in languages with signed ints
Timestamp41~69 years from custom epochSortable by creation time
Datacenter ID50–31 (32 DCs)Prevents cross-DC collision
Machine ID50–31 (32 machines per DC)Prevents cross-machine collision
Sequence120–4095 per msHandles burst within same millisecond
# Simplified Snowflake generation (conceptual)
EPOCH = 1288834974657  # Twitter custom epoch (Nov 2010)

def generate_id(dc_id: int, machine_id: int, seq: int, now_ms: int) -> int:
    timestamp = now_ms - EPOCH
    return (timestamp << 22) | (dc_id << 17) | (machine_id << 12) | seq

# dc_id=1, machine_id=5, seq=42, now → single 64-bit integer

Twitter's original epoch gives 69 years of timestamps. Sonyflake uses a more recent epoch with 39 timestamp bits — shorter lifespan, more sequence bits. The trade-off is configurable.

Throughput math — napkin numbers

Snowflake throughput is bounded by the sequence field:

Per machine:  4096 IDs / millisecond = 4,096,000 IDs / second
32 machines per DC:  32 × 4096 = 131,072 IDs / ms per datacenter
32 datacenters:  32 × 131,072 = 4,194,304 IDs / ms globally
ScaleIDs/sec neededSnowflake enough?
Indian startup MVP100Yes — by orders of magnitude
E-commerce sale peak10,000Yes — one machine handles it
Twitter 2012~5,000 tweets/secYes — with headroom
UPI peak (system-wide)millions/secNo — needs multiple ID spaces or wider sequence

In an interview, do the math out loud. "12 sequence bits gives 4096 per ms per machine. At 10k IDs/sec I need 10 IDs per ms — well within limits." That beats reciting the bit diagram from memory.

If you exceed 4096 IDs in the same millisecond on one machine, the generator waits for the next millisecond (busy-spin or sleep). Throughput drops but uniqueness holds.

Clock skew — the problem nobody mocks in tests

Snowflake IDs depend on wall-clock time. If Machine A's clock is 500ms ahead of Machine B, Machine A generates IDs with future timestamps. When Machine B catches up, it might generate IDs that sort before IDs already issued — breaking monotonic ordering.

Defences:

1. NTP sync on every machine (mandatory, not optional)
2. Reject IDs if local clock < last generated timestamp
3. Wait until clock catches up before generating next ID
4. Alert if clock drift exceeds threshold (e.g., 100ms)
ScenarioWhat happensFix
Clock jumps forward (NTP correction)Sequence resets, possible duplicate if not handledTrack last_timestamp; reject backward jumps
Clock jumps backwardNew IDs sort before old IDsBlock generation until clock ≥ last_timestamp
VM migration between hostsClock may shiftRe-sync NTP before resuming ID generation
Container without NTPDrift accumulatesRun NTP sidecar or use coordinated service

This is why some teams run a lightweight ID allocation service instead of pure local Snowflake — one source of truth for timestamps. You lose zero-coordination purity but gain clock sanity.

Datacenter and machine ID assignment

Each machine needs a unique (datacenter_id, machine_id) pair. Duplicate assignment = guaranteed collision, no matter how good your timestamp logic is.

Common assignment strategies:

Option 1 — Config file / env var
  SNOWFLAKE_DC=1 SNOWFLAKE_MACHINE=5
  Simple. Human error assigns same pair twice.

Option 2 — ZooKeeper / etcd lease
  Machine boots → claims next free (dc, machine) slot → heartbeat
  Slot released on graceful shutdown. Crash = slot locked until lease expires.

Option 3 — Kubernetes StatefulSet ordinal
  Pod name "order-service-7" → machine_id = 7
  Works if pod count ≤ 32 per DC.

For a two-datacenter setup (ap-south-1 + ap-south-2), assign DC 0 to Mumbai and DC 1 to Hyderabad. 32 machines per DC is 64 total generators — enough for most Indian product companies until you are operating at Flipkart tier.

Alternatives: Sonyflake, ULID, UUID v7

Snowflake is not the only option. Know these for interview bonus points:

SystemFormatSortableCoordinationNotes
Twitter Snowflake64-bit intYesDC + machine IDsIndustry default reference
Sonyflake64-bit intYes16-bit machine IDMore machine bits, fewer DC bits
ULID128-bit string (26 chars)YesNone (random component)Crockford base32, lexicographically sortable
UUID v7 (RFC draft)128-bit UUIDYesNoneTimestamp in high bits, random in low
Instagram ID sharding64-bit intYesDB shard embedded in IDCustom layout per company

ULID example:

01ARZ3NDEKTSV4RRFFQ69G5FAV
├──────── timestamp ────────┤├──── random ────┤
26 characters, case-insensitive, URL-safe

ULID needs no machine ID assignment — the random suffix handles uniqueness. Trade-off: 128 bits instead of 64, and no embedded datacenter metadata. Good for logs and event IDs. Less ideal as a narrow DB primary key.

UUID v7 is gaining adoption because it is a standard, works in any language, and sorts by time. If your team already uses UUIDs everywhere, v7 is an easier migration than a custom Snowflake implementation.

Tie-back: URL shortener short codes

Remember the URL shortener from URL Shortener Core? The recommended approach was unique numeric ID + base62 encode.

Snowflake ID:  139281847291263 → base62 → "kF3xP2q"
Short URL:     schoolabe.com/x/kF3xP2q

Why Snowflake fits the shortener perfectly:

  • Unique across all app servers and datacenters — no collision on insert
  • Sortable — "latest links" query works on the numeric ID before encoding
  • Compact after encoding — 7 base62 chars holds up to 62^7 ≈ 3.5 trillion IDs
  • No central bottleneck — each app server generates IDs locally at redirect-create time

At scale (URL Shortener at Scale), the read path hits Redis and CDN. The write path still needs a fast unique ID — Snowflake removes the Postgres sequence from the critical path.

Contrast with hash-based codes: Snowflake guarantees uniqueness without retry loops. Hash needs collision detection and fallback — fine at small scale, messy at 10k creates/sec.

Operating a Snowflake service in production

Deployment checklist:

  • NTP synced on every host (chrony or systemd-timesyncd)
  • Unique (datacenter_id, machine_id) per generator instance
  • Monitor: IDs generated/sec, clock drift alerts, sequence overflow waits
  • Graceful shutdown: finish in-flight IDs before releasing machine slot
  • Idempotent API: client retries must not create duplicate records (use client-supplied idempotency key separately from Snowflake ID)

When NOT to build Snowflake:

  • Below 1,000 IDs/sec — Postgres sequence is simpler and battle-tested
  • IDs must be opaque strings — use ULID or UUID v7 instead
  • Strict global monotonic order required — Snowflake is per-machine roughly sorted, not globally strict

Libraries exist in every language: github.com/bwmarrin/snowflake (Go), flake-id (Node), pysnowflake (Python). Do not rewrite from scratch unless you enjoy debugging clock skew at 3 AM.

Common mistakes in Snowflake interviews

Mistake 1 — forgetting clock synchronization.

Drawing the bit layout without mentioning NTP is incomplete. Clock skew is the #1 production issue with timestamp-based IDs.

Mistake 2 — duplicate machine IDs.

Two Docker containers with machine_id=0 will collide. Always explain assignment strategy — env var, etcd, or StatefulSet ordinal.

Mistake 3 — assuming global strict ordering.

Snowflake IDs from different machines are roughly time-ordered, not perfectly ordered. Machine A at T+1ms can produce a lower ID than Machine B at T if B's clock is ahead. Use a DB timestamp for strict ordering if needed.

Mistake 4 — using Snowflake as a UUID string.

A 64-bit integer is 8 bytes. Stringifying it ("139281847291263") wastes space in JSON APIs. Base62-encode for URLs, keep integer in the database.

Mistake 5 — no plan for epoch exhaustion.

41 bits ≈ 69 years. Twitter's epoch expires around 2080. You will not be in that interview, but mentioning it shows you think long-term.

Interview talking points

When asked "design a distributed unique ID generator":

  1. Requirements — sortable? 64-bit int or string? QPS per machine? Multi-DC?
  2. Draw Snowflake — label each bit field with size and purpose
  3. Throughput — 4096/ms/machine, do the math for their stated QPS
  4. Clock skew — NTP + last_timestamp guard + wait on backward jump
  5. Machine ID assignment — etcd/ZooKeeper or config with validation
  6. Alternatives — UUID v7 if standard compliance matters, ULID for string IDs
  7. Tie to use case — "For a URL shortener I base62-encode this 64-bit ID"

End with: "I would use an off-the-shelf Snowflake library, assign machine IDs via etcd, and monitor clock drift." Practical beats theoretical.

Phase 3: full interview designs

You have the primitives. Phase 3 applies them to complete product designs — web crawler, notifications, news feed, and chat.

Continue here: How Web Crawlers Actually Work.