Grokking the System Design Interview, Volume II
Vote

0% completed

Unique ID Generator: Requirements

Step 2: Clarifying and Defining Requirements

Step 2: Clarifying and Defining Requirements

Step 1 said what a GUID service is. Now we agree on what ours must do, before anything is drawn.

Functional requirements

The service has to do four things. The diagram lists them, and each one is explained below it.

Functional requirements
Functional requirements
  • Generate unique IDs on demand. The system provides an API that clients call to get a new ID at any time. Every ID returned is globally unique. No two calls ever produce the same ID, even from different services or different regions.
  • Support time-sortable and opaque IDs. Clients can ask for either format. A time-sortable ID reflects creation order when compared with others. An opaque ID shows no relation to time or sequence. This can be two endpoints, or one parameter like the type field in Step 4. Both formats are globally unique across the whole system.
  • Simple client API. A REST or gRPC endpoint that returns a new ID with little overhead. For example, POST /v1/ids returns a JSON body holding a new ID. Batch requests, like 100 IDs in one call, cut call overhead for clients that need many IDs.
  • No offline client ID generation. All IDs come from the central service. Clients never make their own IDs offline. So the design never has to merge offline IDs or resolve conflicts from offline nodes. A client must be online and able to reach the service.

The last item is a restriction rather than a feature, and it is the one worth agreeing out loud. Ruling out offline generation removes a whole class of problem from the rest of the design.

Non-functional requirements

Those four say what the service does. These say how well it has to do them, and they are what shape the design. The diagram lists them, and each one follows.

Non-functional requirements
Non-functional requirements

High throughput. The system handles hundreds of thousands of ID requests per second. We target at least 100,000 IDs per second sustained. Peak bursts of 200,000 to 500,000 per second are handled by adding nodes. Each generator node takes a large share, tens of thousands per second, so we run enough nodes to meet demand.

Low latency. Making an ID is a few processor instructions. Almost all of the time a caller waits is network. In region, the service answers in under 5ms at p99. The p99 is the time within which 99 out of every 100 requests complete.

So latency here is a question about distance, not about work. A client using the library from Step 5 makes no network call and sees microseconds. For everyone else, the only way to answer faster is to answer from a closer node.

Treat any p99 above 10ms as a fault, not a busy period. Nothing in this service does enough work to be slow. A slow response means a garbage collection pause, a saturated network card, or a node that should be replaced.

High availability and fault tolerance. Target 99.99% uptime or better. There is no single point of failure. If a server or a whole datacenter goes down, ID generation continues, perhaps with clients failing over to another region. Redundancy and failover are required.

Availability matters more here than in most services. Other services call this one before they can insert a row, so an outage here stops writes everywhere.

Global consistency. IDs are globally unique across all datacenters and regions. The design coordinates or partitions the ID space so two regions never produce the same ID. The format is consistent too. An ID made in Europe has the same structure and length as one made in the US.

Scalability. The system scales horizontally, meaning by adding more machines. We can add generator instances, in existing or new regions, without major reconfiguration. If traffic doubles, we deploy more nodes, or add new bit partitions if needed. Growth in request rate and in the number of clients should be smooth.

Precision and ordering guarantees. Time-sortable IDs preserve insertion order within reasonable bounds. If one ID is created after another, it usually has the higher value. Minor anomalies from distributed clocks are acceptable, but the order should generally hold.

That word "usually" is deliberate. The design should limit problems from clock skew, the difference between two machines' clocks, and handle them safely. If a clock drifts or jumps, the system must not make duplicates or badly out-of-order IDs.

ID size. Many use cases prefer IDs that are not too large. Time-sortable IDs are numeric, and we aim for 64 bits, which databases handle efficiently. Opaque IDs may be larger, like the 128-bit UUID format. We avoid anything bigger, to keep storage and indexing overhead down.

Next: Step 3, where those requirements become numbers.

Reading Progress

0%


Vote for new content

On This Page

Step 2: Clarifying and Defining Requirements