POST /urls
{
"longUrl": "https://example.com/very/long/url",
"expiry": "2026-06-01T00:00:00Z"
}
{
"shortUrl": "https://short.ly/abc123",
"shortCode": "abc123",
"expiry": "2026-06-01T00:00:00Z"
}
GET /{shortCode}
Example:
GET /abc123
shortCode
302 Found
Location: https://example.com/very/long/url
For user data, PostgreSQL is usually better because:
MongoDB can be considered if:
1. User submits long URL
2. URL Service generates short URL
3. Store mapping in PostgreSQL
4. Cache mapping in Redis with TTL
1. Client requests short URL
2. Check Redis first
3. If cache hit → return long URL
4. If cache miss:
→ query PostgreSQL
→ populate Redis
→ redirect user
Used as:
Used as:
Yes. Your current points are good, but some components can be clarified and strengthened. It looks like you’re designing a URL Shortener system (like TinyURL/Bitly) with focus on scalability, latency, and concurrency.
Here is an improved component design:
1. API Layer
Client → Load Balancer → Services
2. URL Service (Stateless, EKS Managed)
Responsibilities:
Improvements:
URL Service Pods (EKS)
├── Pod 1
├── Pod 2
└── Pod N
3. User Service (Stateless, EKS Managed)
Responsibilities:
Also horizontally scalable.
4. Redis Cache (Low Latency)
Purpose:
shortUrl -> originalUrl
Flow:
Read Request
↓
Redis Cache
↓ Hit
Return URL
↓ Miss
DB Lookup
↓
Populate Redis
↓
Return URL
Your question:
Concurrent request issue? Multiple reads same row supported?
Answer: Multiple reads are usually not a problem.
Redis supports very high concurrent reads (millions/sec possible depending on setup).
DB also supports concurrent reads through:
Issue happens more with:
Example:
Redis key expires
1000 requests come simultaneously
All hit DB
DB overload
Solution:
5. Persistent Storage (Primary DB)
Your question:
Cache miss? Persist somewhere else?
Yes.
Redis is cache, not source of truth.
Need persistent DB:
Redis (Fast cache)
↓ Miss
Aurora/Postgres/DynamoDB
Store:
ShortURL
LongURL
UserId
CreatedAt
Expiry
Can also use:
6. URL ID Generation (Collision Avoidance)
Your point:
7 bit uuid generation unique
7 bits gives:
2^7 = 128 values
Too small.
Need something like:
Options:
Option 1: Snowflake ID
Timestamp + MachineId + Sequence
Example:
41 bits Timestamp
10 bits Machine ID
12 bits Sequence
No collision.
Option 2: Base62 Encoding + Counter
10001 → "2Bh"
Counter from distributed ID generator.
Option 3: UUIDv7 + Base62
UUIDv7 gives:
Encode to Base62 for short URL.
Example:
UUIDv7
↓
Base62
↓
"abX92P"
7. Analytics Service (Async)
Don’t update click count synchronously.
Redirect
↓
Kafka/SQS
↓
Analytics Consumer
↓
DB
Reduces latency.
8. CDN (Optional)
Popular URLs:
Edge CDN
↓
Redis
↓
DB
Reduces redirect latency globally.
Final Improved Architecture
Client
↓
Load Balancer / API Gateway
↓
--------------------------------
| URL Service (EKS, Stateless) |
| User Service (EKS) |
--------------------------------
↓
Redis Cache Cluster
↓ Miss
Primary DB (Aurora/Postgres/Dynamo)
↑
Read Replicas
URL Generation
↓
UUIDv7/Snowflake/Base62
Analytics
↓
Kafka/SQS
↓
Consumer
↓
Analytics DB
Key improvements:
This becomes closer to a production-grade system design answer for interviews.