URL Shortener Under Load
This service turns long URLs into short four-character codes.
- POST
/shortencreates 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
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/shortenand 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_URLif 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
/shortenthat 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. ASELECTthenINSERTlets 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.