Free preview

Choosing the Database

In one line: the chapter picks MongoDB for a reason that is usually a disadvantage, and understanding why it is an advantage here is the whole lesson.

The requirement

A URL shortening service is read-heavy and requires horizontally scalable storage. We need to store user details and mappings of long URLs to short URLs.

As the URL mappings are independent and don't have complex relationships, a NoSQL database is a good fit. MongoDB is a strong candidate because:

  1. It uses a leader-follower protocol, enabling the use of replicas for heavy read workloads.
  2. MongoDB ensures atomicity in concurrent write operations and returns duplicate-key errors to prevent collisions.

Single-leader writes are usually a bottleneck — here they are the feature

Reason two is the interesting one, and it turns a common criticism upside down.

MongoDB routes all writes through a single leader per shard. In most designs at scale that is the thing you complain about — it caps write throughput at one machine and makes the leader a failure point.

Here it is exactly what you want, for two reasons.

Writes are 76 per second. Lesson 3's arithmetic. A single leader handles that with six orders of magnitude of headroom. The bottleneck that would matter elsewhere is invisible.

Serialization gives you correctness for free. The chapter says so directly in its workflow section: "all the write requests go through the single leader and hence exclude the possibility of race conditions due to the serialization of requests."

That matters because of Lesson 2's custom aliases. Two users requesting /coffee simultaneously must not both succeed. With a single leader and a unique index, the second write gets a duplicate-key error — no distributed locking, no consensus round, no application-level coordination.

Compare what a leaderless store would require: a read to check availability, then a write, with a window between them where another node accepts the same alias. You would need compare-and-set or a quorum protocol to close it.

A single writer is a bottleneck when writes are frequent and a correctness guarantee when they are not. At 76 writes per second, you take the guarantee and ignore the cost — and stating that trade explicitly is what makes this a reasoned choice rather than a preference.

The design's own question: why not Cassandra or Riak?

You're evaluating NoSQL databases for a read-intensive service. Why might Cassandra or Riak not be good choices compared to MongoDB?

The answer follows directly from the previous callout, and it is about write architecture.

MongoDBCassandra / Riak
Write modelSingle leader per shardLeaderless — any replica accepts
Conflicting writesRejected — duplicate key errorBoth accepted, reconciled later
Conflict resolutionNot neededLast-write-wins or vector clocks
Optimized forBalanced, consistent workloadsVery high write throughput

Leaderless stores are built to accept writes anywhere, which is superb when you have millions of writes per second and no uniqueness constraint — that building block's location updates, for instance.

It is exactly wrong for a uniqueness requirement. With last-write-wins, two users claiming /coffee both get success, and one silently loses their alias later. There is no natural point at which the system can say no.

And the throughput those stores buy is irrelevant here — 76 writes per second does not need a leaderless architecture.

Choose a leaderless store when writes are many and uniqueness does not matter; choose a single-leader store when uniqueness matters and writes are few. This design is squarely the second case.

But the read argument is thinner than it looks

Reason one — "leader-follower protocol, enabling the use of replicas for heavy read workloads" — is true and not distinctive. Every replicated database supports read replicas, including relational ones.

And Lesson 3's numbers make the "heavy read workload" claim worth examining: 7,600 reads per second, against a 6 TB dataset with a 66 GB hot subset in cache.

Most of those reads never reach the database at all. A well-tuned cache on a workload this skewed — a small number of links carrying most traffic — should absorb the large majority, leaving the database with a modest residual.

So the honest framing is: the database is not under heavy read load; the cache is. The database's job is durability and the uniqueness guarantee, and both are satisfied comfortably.

Which raises a question the chapter does not ask: would a relational database work? At 6 TB, 76 writes per second, and a simple two-column mapping, PostgreSQL with a unique index would satisfy every requirement — and give you the duplicate-key rejection more naturally than MongoDB does.

The genuine arguments for NoSQL here are the two the evaluation gives later: schema flexibility (not every URL has a UserID, since anonymous users exist) and operational ease of horizontal distribution. Both are real and neither is about scale.

When the data is 6 TB and the workload is 76 writes per second, the database choice is about operations and schema, not capacity — and saying so is more honest than invoking scale.

What the mapping table actually holds

The design stores two things: user details and long-to-short mappings. The mapping row is the interesting one, and Lesson 3 budgets 500 bytes for it.

A reasonable breakdown:

short_key      ~11 bytes    (base-58, up to 11 chars)
long_url      ~100-200 bytes
user_id          8 bytes    (optional - anonymous users have none)
created_at       8 bytes
expires_at       8 bytes
custom_flag      1 byte

That is comfortably under 500 bytes, so the estimate is generous — which is fine.

Two observations. The optional user_id is exactly the schema-flexibility argument, and it is a genuine one: a relational schema would need a nullable column, which is fine but less natural than simply omitting the field.

The primary key must be the short key, not the long URL. Redirection looks up by short key at 7,600 QPS; deduplication looks up by long URL at 76 QPS. So you index the short key as the primary and add a secondary index on the long URL — a hundredfold difference in access frequency that the schema discussion never mentions.

The cache in front of it

A system with a thousand reads per write is a cache-shaped problem, and it is worth being concrete rather than saying "add a cache."

Cache-aside, not write-through. The mapping is written once and never updated, so there is no invalidation problem in the usual sense — the only mutation is deletion at expiry.

LRU eviction, because link popularity is extremely skewed and decays fast: a link shared this morning is hot for hours and then effectively dead. Recency predicts future access far better than total frequency.

TTL must not outlive the URL's own expiry. If a link expires at midnight and the cache entry lives until 2am, the service keeps redirecting to something it has already deleted — the expiry requirement silently is not met.

The payoff is direct: a memory lookup is on the order of a hundred nanoseconds against tens of microseconds for an indexed SSD read, so a high hit rate is what makes a sub-100 ms redirect comfortable rather than tight. For the very hottest codes, push the redirect itself to a CDN or edge worker so it never reaches origin — cost and complexity, so name it as the next step rather than the default.

Key takeaway

MongoDB's single-leader writes are usually a bottleneck and here they are the feature — at 76 writes per second the throughput cost is invisible, and serialization plus a unique index gives duplicate-key rejection for custom aliases with no distributed locking. That is why Cassandra and Riak are wrong: leaderless stores accept conflicting writes and reconcile later, so there is no point at which they can say no to a duplicate alias, and the write throughput they buy is irrelevant. The read argument is thinner — 7,600 QPS against a 66 GB cache means the database is not under heavy read load — so the honest justification is schema flexibility and operational distribution, not scale, and a relational database would also work.

Next: how a short URL is actually generated.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue