Functional
Non-Functional
Assumptions
Write / Read QPS
Storage over 5 years
1. Create short URL (write path)
POST /api/v1/urls Content-Type: application/json{ "longUrl": "https://example.com/some/very/long/path" }
Response 201 Created:
{ "shortUrl": "https://tiny.url/aB3xK9z", "longUrl": "https://example.com/..." }
2. Redirect (read path)
GET /{shortCode}
Response 302 Found with Location: https://example.com/some/very/long/path
Overview A read-heavy URL shortener with two independent paths — a write path that creates links and a hot read path that serves redirects — sitting behind a shared load balancer, backed by a partitioned key-value store with a Redis read cache.
Components
code → longUrl, returns the short URL.302 Found with the Location header. Kept separate from the write path so redirect spikes don't slow link creation.short_code → long_url, LRU eviction, sitting in front of the DB. Serves popular codes from memory and shields the DB from the ~100:1 read load.short_code → long_url store, partitioned by hash(short_code) so every lookup hits exactly one shard (even distribution, no scatter-gather).Table: url_mappings
| Column | Type | Notes |
short_code | string (7) | Partition key — the lookup key |
long_url | string (≤2000) | original URL |
created_at | timestamp | for auditing |
expires_at | timestamp, nullable | null = permanent; drives TTL |
user_id | string, nullable | owner, if accounts exist |
(An internal id is optional — short_code is already unique and is what every read uses.)
Index strategy
short_code → every redirect is a single-key lookup, O(1), no table scan.user_id (if accounts exist) to list all links a user created.long_url — redirects never query by it.Partitioning
hash(short_code) → even traffic distribution (random codes → uniform) and each lookup hits exactly one shard, no scatter-gather. Avoid partitioning by long_url or created_at (scatters reads / creates hot shards).Consistency
Storage technology
1. ID Generation Service
How it works: A global counter encoded in base62 (62⁷ ≈ 3.5 trillion codes). To avoid a single hot counter, use range allocation — a coordinator hands each app server a block of IDs (e.g. 1M at a time); servers mint codes locally with no per-request coordination.
/aaaaaab, /aaaaaac…). Mitigate by padding/obfuscating the counter (e.g. bit-shuffle or XOR with a secret), or switch to a pre-generated random pool (KGS) where codes are random by construction.2. Caching (the hot redirect path)
3. Partitioning
hash(short_code) → every redirect routes to exactly one shard, no scatter-gather, O(1).4. Rate Limiting (brief)
429 with Retry-After so clients back off.