System requirements
Functional:
- Create short URL from long URL
- Redirect short URL to original URL
- Support optional expiration (default: 1 year)
- Optional custom alias (nice to have)
Non-Functional:
- High read throughput (read-heavy system)
- Low latency redirects (<50ms)
- High availability (no single point of failure)
- Horizontally scalable
- Globally available (multi-region)
Estimations:
- 100K users × 10 URLs/day = 1M writes/day (~12 writes/sec)
- Reads ≈ 1000/sec
- Keyspace: Base62 → 62⁶ ≈ 56.8 billion combinations (no need for recycling)
API Design
POST /create Flow:
- Receive long_url
- Generate unique ID (Snowflake)
- Convert ID → Base62 → url_key
- Store mapping in DB
- Cache in Redis
- Return short URL
GET /url_key Flow:
- Check cache
- If hit → redirect to long_url
- If miss → query DB
- Cache result
- Check expiration
- Redirect to long_url or return error if expired
High-Level Architecture
Client → GeoDNS → Nearest Region
Each region contains:
- Load Balancer
- Stateless App Servers
- Redis (cache)
- Sharded Database (primary + replicas)
- Distributed ID Generator (Snowflake)
Detailed component design
Key Generation (Core Decision)
- Use Snowflake ID generator for globally unique IDs
- Convert ID to short key using Base62 encoding
Why:
- No collisions
- No race conditions
- No need for KGS or coordinator
- Works across multiple regions
Database Design
Table: url_mapping
- url_key (Primary Key)
- long_url
- created_at
- expiration_date
- user_id (optional)
Sharding:
- Partition by hash(url_key)
Replication:
- Primary + read replicas per region
- Async replication across regions
Caching Strategy
- Cache hot URLs in Redis
- Key → url_key, Value → long_url
- Use LRU eviction
- Store millions of entries to reduce DB load
Multi-Region Design
- GeoDNS routes user to nearest region
- Each region handles reads + writes independently
- IDs generated locally using Snowflake
Replication:
- Asynchronous between regions
Consistency:
- Eventual consistency (acceptable for URL shortener)
Expiration & Cleanup
- Store expiration_date in DB
- On read: check expiry → reject if expired
- Background job deletes expired entries
- No need to recycle keys (huge keyspace)
Scaling & Optimizations
- Add DB shards for write scaling
- Use read replicas for read-heavy traffic
- Use CDN like Cloudflare to cache redirects at edge
- Increase cache size to reduce DB hits
Tradeoffs
- Sequential Base62 keys → predictable URLs
- → Can add randomness if needed
- Eventual consistency across regions
- → Slight delay in propagation but acceptable
Final Summary
- Use Snowflake + Base62 for key generation (no KGS)
- Use Redis cache for fast reads
- Use sharded DB + replicas for scalability
- Use GeoDNS + multi-region deployment
- Use async replication for global availability