URL Shortener Under Load

All challenges

URL Shortener Under Load

This service turns long URLs into short four-character codes.

  • POST/shorten creates a short code for a URL.
  • GET/<code> sends the visitor to the original URL.

It already stores 1,343,693 links. Each code is 4 characters, and each character is one of 36 options (0–9 or a–z).

Everything looks fine. Every request succeeds and there are no errors.

But there is a problem. Some old links now open the wrong page. Nobody gets an error, so nobody notices, and it keeps happening every second.

System Architecture

POST /shortenGET /<code>linksClient SimulatorShortener APIPostgresRedis (optional)

Your task

Find out why existing short links are ending up at URLs their owners never submitted, and make it stop.

You edit links.py and codes.py in the api repo, already cloned in the editor:

  • links.py: the two routes, POST/shorten and GET/<code>.
  • codes.py: short code generation.
  • db.py: the connection pool and the SQL.
  • Short codes must stay exactly 4 characters. The client rejects any other length.
  • Redis is available at REDIS_URL if you want it. Nothing requires it.
  • Commit + push → the platform rebuilds the image and redeploys the API.

Success Criteria

When you submit, the Judge watches the next 60 seconds of live traffic. All three must pass:

  • No rebound links: no code is ever handed out twice, and every existing link still points where it did.
  • Write throughput ≥ 9/s: of the 10 links/s offered.
  • No response errors: no 4xx/5xx responses or timeouts.

Where to look

  • API Stats: a POST/shorten that returns a code which already belonged to someone is counted as a failed request.
  • Metrics: Rebound links/s is the gate. The client knows every taken code, including the seeded ones, and re-checks where links point.
  • Tests: tests/ passes on the starter. Nothing inside the service can tell that links are being destroyed.

Submit once Rebound links/s has been at zero for a minute and writes hold near 10/s.

  • One atomic claim beats check-then-write: insert_link_if_free() (INSERT … ON CONFLICT DO NOTHING) tells you in one statement whether the code was yours. A SELECT then INSERT lets two writers both see a code as free.
  • On a collision, generate again, but cap the attempts and decide what a give-up returns. Work out how full the table is before you pick the cap.
  • Or stop guessing one code at a time: keep a pool of codes you already know are free (a key generation service), and refill it in batches. Where the pool lives (in the process, or shared) is a design choice.