Functional Requirement :

1) User generate short url for big url

2) User is able to redirect to the orginal link from the short url

3) User can create profile and create url associated with its profile

Non functional requirement

1) Scalable -> add more url, and server more redirect

2) Latency -> 5ms ?

3) Available -> as much as possible

4) Read Heavy


Create Short URL

Endpoint

POST /urls

Request Body

{ "longUrl": "https://example.com/very/long/url", "expiry": "2026-06-01T00:00:00Z" }

Response

{ "shortUrl": "https://short.ly/abc123", "shortCode": "abc123", "expiry": "2026-06-01T00:00:00Z" }

2. Redirect to Original URL

Endpoint

GET /{shortCode}

Example:

GET /abc123

Behavior

  • Looks up shortCode
  • Fetches original URL
  • Returns HTTP redirect

Response

302 Found Location: https://example.com/very/long/url





High-Level Design

client -> gateway -> user Service -> userDb

UserDb { id , userName , name , emailId , phoneNumber }

For user data, PostgreSQL is usually better because:

  • User schema is stable
  • Relationships and constraints matter
  • Strong consistency is important

MongoDB can be considered if:

  • Schema changes frequently
  • Massive horizontal scaling is required
  • Product evolves rapidly

client -> gateway -> url Service -> urlDb

urlDb{ id , shortUrl , longUrl , expiry }


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


Redis vs Postgresql

PostgreSQL

Used as:

  • Source of truth
  • Permanent storage

Redis

Used as:

  • Cache layer
  • Fast URL resolution
  • TTL-based expiration





Detailed Component Design

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

  • API Gateway / Load Balancer
    • Route traffic to services
    • Rate limiting
    • Authentication

Client → Load Balancer → Services


2. URL Service (Stateless, EKS Managed)

Responsibilities:

  • Create short URL
  • Redirect short URL → original URL
  • Read from Redis first
  • Write metadata to DB

Improvements:

  • Deploy on EKS/Kubernetes
  • Horizontal Pod Autoscaler (HPA)
  • Multiple replicas

URL Service Pods (EKS)

  ├── Pod 1

  ├── Pod 2

  └── Pod N


3. User Service (Stateless, EKS Managed)

Responsibilities:

  • User profile
  • Authentication
  • User URL history

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:

  • Read replicas
  • MVCC (e.g., PostgreSQL)
  • Connection pooling

Issue happens more with:

  • Multiple writes on same resource
  • Cache stampede (many requests after cache expiry)

Example:

Redis key expires

1000 requests come simultaneously

All hit DB

DB overload

Solution:

  • Redis locking
  • Cache warming
  • Random TTL
  • Request coalescing


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:

  • Read replicas for scaling reads


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:

  • Time sortable
  • Very low collision probability

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:

  • ✅ Horizontal scalability → EKS + HPA
  • ✅ Low latency → Redis + async analytics
  • ✅ Cache miss handling → Persistent DB
  • ✅ Collision avoidance → UUIDv7/Snowflake
  • ✅ Concurrent reads → Redis handles well
  • ✅ DB protection → Cache stampede prevention
  • ✅ Global scale → CDN + replicas

This becomes closer to a production-grade system design answer for interviews.