V
← Back to System Design Showcase

❤️ Design "Likes" Counter at Scale

500M+ DAU • 100k+ Likes/sec • Sharded Counters • Kafka • Cassandra

High-concurrency distributed like tracking architecture: Client optimistic UI, Redis idempotency sets, Kafka event buffering, Flink tumbling window micro-batching, sharded Redis sub-counters (defeating the hot key thundering herd), Cassandra immutable audit logs, and WebSocket live delta broadcasting.

Post Profile:
Like Ingestion Rate
142,500likes/s
128 Kafka Partitions Active
P99 Write Response
< 12msOptimistic
0ms Client Feedback
Read P99 Latency
1.2msmulti-tier
L1: 40μs • Redis MGET: 1.2ms
Active Counter Shards
16shards/key
16x Write Concurrency
Idempotency Guard
Normal (100% Unique)
Redis Set SADD O(1)

🌐 Distributed Likes Counter Architecture

Ingress → Idempotency Check → Kafka Buffer → Flink Window Rollup → Sharded Redis → Cassandra Event Store → WebSocket Broadcast.

System Ready • Click any node to inspect
COL 1: INGRESSCOL 2: IDEMPOTENCYCOL 3: BUFFER & FLINKCOL 4: SHARDED COUNTERCOL 5: PERSISTENCECOL 6: READ & SYNCWeb & Mobile ClientsOptimistic UI HeartZero-Latency Feedback❤️ Instant Red StateAPI Gateway & AuthToken Bucket Rate Limiter5 likes/s Per User CapBot Flood DefenseLike Ingestion ServiceStateless Go FleetDecoupled Asynchronous< 12ms P99 ACKUser-Like State CacheRedis Set / Bloom FilterSADD post:123:likers1 = New, 0 = DuplicateKafka Stream Bufferlikes-events-v1hash(post_id) Partitions128 Kafka PartitionsWindow AggregatorFlink 1s Tumbling WindowMicro-Batching Delta Rollup50k Taps → 1 INCRBYSharded Redis CounterHot Key Meltdown Shieldpost:123:shard_{0..15}Random Shard INCRBYParallel MGET Sum on Read16x WRITE CONCURRENCYL1 Pod Local CacheIn-Process Caffeine Cache2-Second Viral Post TTL< 40μs Microsecond ReadCassandra Event StoreImmutable post_likes Table((post_id), user_id, time)"Who Liked" ModalsPost Metadata StorePostgreSQL / Auroralike_count Column5s Micro-Batch UPDATEsLike Query ServiceMulti-Tier Cache-AsideL1 → Redis Shards → DB< 1.2ms P99 LatencyDelta BroadcasterWebSocket / SSE GatewayBatched 2s Live DeltasTicking Counter AnimationReconciliation JobAudit & Drift Self-HealingSpark / Cron Batch ScanCassandra Count Audit
Idle
Click any component card in the SVG to inspect architectural trade-offs.
Client & Read Delivery
Write Ingestion
Idempotency & Storage
Kafka & Flink Aggregator
Sharded Redis Counters
👆 Click any component in the architecture diagram to open its floating interview details.

Core Requirements (The Hello Interview Breakdown)

Functional Requirements:
  • 1. Like / Unlike: User can like or unlike a post or video in real time.
  • 2. View Like Count: Viewers see up-to-date total like count with ticking updates.
  • 3. Who Liked List: Paginated chronological list of users who liked the post.
Non-Functional Requirements:
  • 1. Ultra-Low Latency: Instant optimistic client feedback; write ingestion SLA < 50ms.
  • 2. Idempotency: Multiple accidental taps or retries must never double-count a like.
  • 3. Hot-Key Resilience: A viral celebrity post must never overwhelm a single database row or Redis shard.
  • 4. High Availability & Eventual Consistency: Like counts can lag behind live writes by 1-3 seconds under extreme traffic.
Scale: 500M+ DAU, 100k+ Likes/secP99 Write Latency: < 12ms (Optimistic 0ms)

What is Expected at Each Level?

Mid-Level: Design a basic like ingestion path, explain how to use Redis INCR / DECR for counting, demonstrate optimistic UI updates, and describe how to check for duplicate likes.
Senior: Articulate how to solve the Redis Hot Key problem using Sharded Sub-Counters (16 shards per key). Introduce Kafka buffering and Flink tumbling window micro-batching to slash DB IOPS by 99.9%. Design the Cassandra schema for 'Who Liked' pagination.
Staff+: Formulate end-to-end idempotency at 500k QPS using partitioned Redis Sets or Bloom Filters. Architect real-time WebSocket delta broadcasting for millions of concurrent viewers, design automated counter reconciliation against immutable event logs, and analyze memory sizing and failure recovery under catastrophic regional datacenter loss.

Sharded Redis Counter Engine

Role & Overview

Solves the single-key Redis hotspot problem for mega-viral posts. Instead of a single key, each post's counter is split across N sub-shards (e.g., 16 shards).

Architecture Implementation

1. Write path: `shard = rand(0..15)`; executes `INCRBY post:{id}:shard_{shard} {delta}`. 2. Writes distribute uniformly across 16 different Redis master nodes. 3. Read path: `MGET post:{id}:shard_0 ... post:{id}:shard_15`; sums the 16 values in parallel (< 1ms).

Solving the Redis Hot Key Problem
❌ Bad Solution: Single Redis Key (post:123:likes): When Messi wins the World Cup, 100k likes/sec hammer the single Redis CPU thread managing that key, crashing the shard.
⚠️ Good Solution: Dynamic In-Memory Buffering: Buffers in app memory, but server crashes lose uncommitted counts.
⭐ Great Solution: Sharded Sub-Counters (16-32 Shards): Writes pick a random shard (`INCRBY post:123:shard_N delta`). 16x write concurrency with zero CPU hotspotting on any individual Redis core!
Specification & Snippet
// Sharded Counter Read & Write
func IncrementLike(postID string, delta int64) {
    shard := rand.Intn(16)
    redisClient.IncrBy(ctx, fmt.Sprintf("post:%s:shard:%d", postID, shard), delta)
}

func GetTotalLikes(postID string) int64 {
    keys := make([]string, 16)
    for i := 0; i < 16; i++ { keys[i] = fmt.Sprintf("post:%s:shard:%d", postID, i) }
    vals, _ := redisClient.MGet(ctx, keys...).Result()
    var total int64 = 0
    for _, v := range vals { total += parse(v) }
    return total
}