System Design · System Design
CampusClip walk
The two CampusClip paths — paste and redirect — with the store, key placement, and a sketch that uses only numbers the course already stated.
The last topic is CampusClip as one design: a paste path that inserts a code, an open path that redirects, a store that grows by replicas or by shards depending on load shape, and a sketch that multiplies only the numbers this course already gave.
- System Design
- Medium level
- 6 concepts
- 2 practice questions
1The problem: long URL to short code
CampusClip has two jobs and only two. Write: a teacher pastes a long URL and gets back a short code. Read: a student opens that code and is redirected. Everything we have already named — one box versus many, load versus capacity, latency versus throughput, a cache, a queue — is in service of those two jobs.
We will not invent a worldwide QPS. We keep the classroom givens: 800 students, a 9:00 burst we already measured, host capacity 20 redirects/s, reply 250 bytes, mean latency 0.2 s when the host is keeping up. The walk is how those pieces sit on the two paths.
Figure. Two paths, one store. Write inserts a code. Read looks it up and sends the student to the long URL.
The two jobs
- WritePaste long URL → store a code → return clip.campus/CODE.
- ReadOpen CODE → look up the long URL → 302 redirect.
- ReuseLoad, cache, queue, and scale choices attach to one of those two paths.
CampusClip's read job is
- Look up the code and redirect
- Mint a new code for every student who opens the link
- Email the teacher after every open
Read is lookup-then-redirect. Minting is write. Email is optional async work, not the read job.
2Write path: mint a code
On paste, CampusClip creates a short code, writes code → long URL to the store, and returns the short link. That insert is synchronous: the teacher is waiting on the code. After the response, CampusClip may enqueue "email the link" — that is the queue we already allowed.
The code must be unique. A simple classroom scheme: draw six characters from a 62-symbol alphabet (a–z, A–Z, 0–9). That is 62^6 possible codes — we will compute that on the capacity sketch, not here. If the insert hits a duplicate, try another code. Do not return a code the store has not accepted.
Figure. Paste is synchronous into the store. The email queue is dashed — it starts after the teacher already has the code.
How a paste becomes a link
- MintPick a unused short code.
- InsertWrite code → long URL in the store. Wait for success.
- ReplyReturn clip.campus/CODE. Then, optionally, enqueue the email.
The teacher's paste response must include the new short code. CampusClip should
- Insert the mapping, then return the code
- Return a code first and insert it on a queue
- Skip the store if the cache is empty
The code is not real until the store has it. A queue can lose the insert after you already showed the link. An empty cache is not a store.
3Read path: look up and redirect
On open, the student hits the balancer, a CampusClip host asks the shared cache for the code, and on a miss loads the store, fills the cache, and replies 302. That whole path is synchronous. A click increment may be enqueued after the 302 — the student is not waiting on the counter.
This is why the cache sits beside the app, not only in one process: the balancer may send the next open of a3k9 to a different host. It is also why the mapping store leans CP: two different long URLs for one code is a wrong redirect, not a delayed counter.
Figure. Open path: student → balancer → CampusClip → cache, with a dashed miss to the store. The 302 goes back along that path. A click queue is not on it.
How an open becomes a 302
- Hit the balancerStudent does not pick a host.
- Cache, then storeHit answers from the copy; miss loads the store and fills the cache.
- Reply, then maybe queueSend the 302. Enqueue the click if you want a counter.
On a cache miss for a3k9, CampusClip
- Loads the store, fills the cache, then sends the 302
- Sends an empty 302 and looks up later on a queue
- Mints a new code so the miss becomes a write
A miss still has to answer this open. Queueing the lookup leaves the student without a redirect. Minting is the write path.
4The store: one table, then replicas or shards
CampusClip's store is one table: short code → long URL. For this campus that table is small. The first scaling question is not "how many shards does industry use". It is the shape of the load. The 9:00 minute is read-heavy: many opens of a few codes. A cache plus a read replica (a copy of the table that serves lookups, while one primary takes inserts) absorbs that shape. The replica can lag the primary — a brand-new code might miss on the replica for a moment; read-your-write from the primary if the teacher immediately clicks their own link.
Write-heavy is the other shape: so many inserts that one primary cannot accept them. Then you shard — split rows across nodes by key so writes fan out. Replicas do not fix that: they multiply read capacity and often make write fan-out worse. CampusClip's classroom paste rate is not that shape; we name the lever so you can recognise it, not so we can invent a shard count.
Figure. Writes hit the primary; dashed edges show reads served from replicas. That is the read-heavy path. Sharding (not drawn) splits the write set when replicas are not enough.
How each path absorbs load
- ReplicatePrimary ships changes to replicas; opens can move off the primary.
- Accept lagAsync replicas may be briefly stale — or read-your-writes from the primary.
- ShardPartition rows by key so each node owns a slice of writes and storage.
A social graph service is write-heavy: every follow inserts a row and read replicas are already saturated by write fan-out. The next scaling lever is
- Shard the database so writes spread across nodes
- Add more read replicas only
- Disable the primary and serve all writes from replicas
Write load needs partition of the write set — sharding. More replicas multiply read capacity and often worsen write amplification. Replicas are not writable primaries in the usual setup.
5Placing codes when nodes join
If CampusClip ever shards the table, each short code needs an owner node. Ordinary hash-modulo placement (\mathrm{node} = \mathrm{hash}(\mathrm{code}) \bmod N) reshuffles almost every code when N changes — adding a fifth store node would move nearly all rows. Consistent hashing places codes and nodes in the same ordered space so that when a node joins or leaves, only about 1/N of the codes move on average; the rest keep their previous owner.
That 1/N is an average over the keyspace, not a production measurement. For N = 5 after a join, about 1/5 = 0.2 of codes rematerialize. Cache rings use the same idea: adding a cache node should not empty every other node.
Picture a circle. Nodes sit at hash positions; each short code walks clockwise to the first node it meets. Add a node: only codes that now hit the newcomer move — roughly one N-th of the keyspace — not a full reshuffle.
How remapping stays small
- PlaceHash codes and node ids into the same ordered space.
- OwnEach code belongs to the next node clockwise (or the nearest successor).
- ChurnAdd or remove one node: only codes in its arc move — about 1/N of them.
Codes remapped when a node joins
A CampusClip store ring has N = 4 nodes. A fifth node joins. About what fraction of codes must move?
- Nodes after joinN' = 5
- Fraction remapped \approx 1/N'1/5 = 0.2
- Contrast: hash mod N on every N changenearly all codes move
Pro tip. Quote ≈ 1/N as the peg; virtual nodes change the exact constant, but the point is linear in 1/N, not a full reshuffle.
Compared with \mathrm{hash}(\mathrm{code}) \bmod N, consistent hashing when N grows mainly helps because
- only about 1/N of codes rematerialize
- every code moves, which rebalances perfectly
- it removes the need for a load balancer
The ring keeps most code→node assignments stable. Full reshuffle is what modulo does. Load balancers are a separate layer.
6A capacity sketch from numbers we stated
We can now multiply only what we already gave CampusClip. Host H finishes 20 redirects/s at W = 0.2 s, so L = 4 in-flight. A 250-byte reply at 20/s is 5000 bytes/s out. At 9:00, λ = 10/s so ρ = 0.5 and L = 2. The 40/s burst on one host is 20/s excess — add a second host, or the wait pile grows.
The code space: six characters from 62 symbols is 62^6 = 56\,800\,235\,584 possible codes. We do not need that many for 800 students. The number is here so you see the minting scheme has room, not so we can claim a worldwide shortener. Writes stay synchronous; clicks and mail may queue at the 20/s worker we already sized.
Figure. Only stated CampusClip numbers: L = 20 × 0.2 = 4 in-flight, outbound 20 × 250 = 5000 bytes/s, and 62^6 = 56,800,235,584 codes. No production QPS is invented.
CampusClip sketch from stated numbers
Using only the classroom givens, what are in-flight opens at capacity, outbound bytes/s at capacity, and 62^6?
- L = 20 \times 0.24 in-flight
- Bytes/s = 20 \times 2505000 bytes/s
- 62^2 = 3844; 62^4 = 3844^214,776,336
- 62^6 = 62^4 \times 62^2 = 14776336 \times 384456,800,235,584 codes
Pro tip. Every factor was stated earlier in the course. If a factor is missing, do not invent a production QPS to fill it.
A second host is added, each still 20 redirects/s, and the balancer splits a 40/s burst evenly. Per-host utilisation is about
- 1.0
- 2.0
- 0.5
40/s across two 20/s hosts is 20/s each, so ρ = 1. 2.0 would be the one-host burst. 0.5 is the 9:00 minute on one host.
Notes
- CampusClip has two jobs: insert a short code (write) and redirect on open (read).
- The write insert is synchronous; email after the reply may queue.
- The read path is balancer → host → cache → store → 302; clicks may queue after the 302.
- Read-heavy growth: cache plus read replicas. Write-heavy growth: shard. Replicas do not absorb write fan-out.
- Consistent hashing moves about 1/N keys when a node joins, not a full reshuffle.
Formulas
- Little's Law on the read path: L = \lambda W.
- Bytes/s = QPS \times payload.
- Code space for 6 symbols from 62 = 62^6 = 56\,800\,235\,584.
- Consistent hashing remap \approx 1/N on a join.
Exam traps & shortcuts
- Read-heavy → cache + replicas; write-heavy → shard.
- Never return a short code the store has not accepted.
- Do not invent a worldwide QPS to finish a sketch. Multiply what you stated.
Reference tables
Every number was stated earlier. Do not add a production QPS.
| Piece | Where it sits | Stated number |
|---|---|---|
| Write | Synchronous insert; email may queue | — |
| Read | Balancer → host → cache → store → 302 | W = 0.2 s; μ = 20/s |
| Cache | Shared, beside the app; cache-aside writes | — |
| Store | One table; replica if reads dominate | — |
| Sketch | L, bytes/s, code space | L = 4; 5000 bytes/s; 62^6 codes |
Recap
Two paths, a store, and a sketch with no invented scale.
- Jobs
- Write = insert a code. Read = look up and 302.
- Sync
- Insert and redirect stay on the request. Mail and clicks may queue.
- Store
- Reads → cache + replicas. Writes that swamp the primary → shard.
- Hash
- Consistent hashing moves ≈ 1/N codes on a join, not almost all.
- Sketch
- 20 × 0.2 = 4 in flight; 20 × 250 = 5000 bytes/s; 62^6 codes.
Practise CampusClip walk
Reading is free and needs no account. Practice, mocks and progress live in the app.
- 2 exam-style questions on this topic, with explanations
- A 3-question practice set that ends the chapter
- Timed mocks scored with the real marking scheme
- Readiness tracked per topic, kept on your device