core practice · coding problem
Pack and unpack a Snowflake id
Implement pack(timestampMs, machineId, sequence) and unpack(id) for a 64-bit Snowflake layout: 41 bits timestamp (ms since a custom epoch), 10 bits machine id, 12 bits sequence. Use BigInt. pack must throw when machineId or sequence exceeds its field.
The mechanism
A database sequence is a coordination point, so distributed id schemes buy independence by partitioning the id space: Snowflake spends bits on a timestamp, a machine id, and a per-millisecond counter, so two nodes cannot collide without sharing a machine id. Time-ordered prefixes are the second reason to care — random UUIDv4 keys scatter writes across a B-tree and destroy insert locality, whereas UUIDv7 and ULID sort by creation time and keep inserts at the right edge of the index.
Expected evidence
unpack(pack(1700000000000n, 42n, 7n)) -> { timestampMs: 1700000000000n, machineId: 42n, sequence: 7n }Try this drill in the practice workspace →
Hints when you need them
- Shift the timestamp left by 22, the machine id by 12, then OR in the sequence.
- Field widths are what bound the design: 10 bits is 1024 machines, 12 bits is 4096 ids per machine per millisecond.
Watch for these mistakes
- Using random UUIDv4 as a clustered primary key, causing page splits and index fragmentation
- Ignoring clock skew and rollback — Snowflake ids collide or go backwards if the clock moves
- Leaking information: a sequential id exposes volume, and a timestamped one exposes creation time
- Assuming machine ids assign themselves; duplicated worker ids are the classic Snowflake outage