SD
System Design Cheatsheet
ByteByteGo Vol. 1 & 2 · 26 Systems · v10 · Staff+ prep (v15) →
26 systems
📈
Vol 1 · Ch. 1
Scale From Zero to Millions of Users
The canonical scaling progression — introduce complexity only when the current tier is the bottleneck.
Core Thesis
Scale each tier independently. Every new layer solves a specific bottleneck. Don't over-engineer before you need it.
Scaling Stages
StageKey AdditionBottleneck Solved
Single ServerEverything on one boxStarting point — no redundancy
Separate DBWeb + DB splitIndependent tier scaling
Load BalancerLB → web server poolWeb tier SPOF; horizontal scaling
DB ReplicationMaster (write) + Slaves (read)Read throughput; DB high availability
Cache (Redis)Cache tier in front of DBRepeated expensive DB reads
CDNEdge servers for static assetsGlobal latency for JS/CSS/images
Stateless WebSession → shared store (Redis)Sticky sessions; enables autoscaling
Multi-DCGeoDNS routingRegional failover; global latency
Message QueueAsync producer/consumerTight coupling; burst absorption
DB ShardingHorizontal DB partitioningWrite throughput ceiling
MicroservicesDecompose monolithMonolith scalability limits
Vertical vs Horizontal Scaling
DimensionVertical (Scale Up)Horizontal (Scale Out)
MechanismAdd CPU/RAM to serverAdd more servers to pool
CeilingHard limit — can't add unlimited resourcesVirtually unlimited
FailoverSingle point of failureRedundant — others absorb traffic
CostExponential at high specsCommodity hardware, linear cost
Best forLow traffic, simple opsProduction-grade, HA systems
8 Laws of Scale — Memorise These
1) Stateless web tier  ·  2) Redundancy at every tier  ·  3) Cache aggressively  ·  4) Multi-DC  ·  5) CDN for statics  ·  6) Shard the DB  ·  7) Split tiers into services  ·  8) Monitor & automate
Cache Considerations
TopicDetail
When to cacheRead frequently, modified infrequently. Never use as primary data store — volatile memory.
TTL / ExpiryToo short → DB hammering (cache stampede). Too long → stale data. Balance based on freshness needs.
SPOF riskSingle cache server = SPOF. Multiple cache nodes across AZs. Overprovision memory by ~20%.
Eviction policyLRU (most common), LFU (frequency), FIFO. Choose based on access pattern.
Cache stampedeMany requests hit DB simultaneously on miss. Mitigate with mutex lock, probabilistic early expiration, or cache warming.
🔢
Vol 1 · Ch. 2
Back-of-the-Envelope Estimation
Numbers every Staff engineer must know cold. Do these estimates before proposing architecture.
Latency Numbers
OperationLatencyRelative
L1 cache reference~0.5 ns
L2 cache reference~7 ns14×
RAM access~100 ns200×
Redis GET~1 ms2M×
SSD random read~100 µs200K×
DB query (indexed)~1–10 ms2–20M×
Network same DC~0.5 ms1M×
Network cross-continent~150 ms300M×
HDD disk seek~10 ms20M×
Estimation Rules
Time Constants
Seconds per day: 86,400 ≈ 100K
Seconds per month: ~2.5M
Seconds per year: ~31.5M
QPS Estimation
1M DAU → ~12 QPS avg
Peak = 2–3× avg
1B DAU → ~12K QPS avg
Storage Sizes
1 char = 1 B · 1 int = 4 B
1 photo ≈ 300 KB
1 min HD video ≈ 150 MB
1 TB = 10¹² B · 1 PB = 10¹⁵ B
Twitter Example
300M MAU, 100M DAU
50% mobile · 10% post daily
Write QPS ≈ 1,150 · Peak ≈ 3.5K
Storage: 10% × 100M × 250B ≈ 2.5 GB/day
🗂
Vol 1 · Ch. 3
System Design Interview Framework
A 4-step process to structure any system design answer. Follow this every single time.
Golden Rule
Never jump to solution. Always clarify scale first. "Design Twitter for 10 users" vs "1B users" are completely different systems.
4-Step Process
StepTimeWhat to Do
1. Understand the problem3–10 minClarify features, scale, constraints. Ask: DAU? Read/write ratio? Latency SLA? Consistency requirements?
2. High-level design10–15 minDraw rough architecture. Get buy-in before diving deep. Cover client → LB → API → DB → cache → queue.
3. Design deep dive10–25 minFocus on hardest parts: data model, critical APIs, scale bottlenecks, failure modes. Follow interviewer's lead.
4. Wrap up3–5 minSummarize, discuss bottlenecks, operational concerns (monitoring, deployment, failure scenarios), future improvements.
Clarifying Questions to Always Ask
Scale
How many users? DAU? Reads vs writes? Peak QPS? SLA latency?
Features
What features are in scope? Mobile vs web? APIs only or full system?
Data
How much data? Retention period? Consistency requirements? ACID needed?
Constraints
Existing tech stack? Budget? Latency SLA? Multi-region requirements?
Staff-Level Signal
Frame answers as an evolution: "At 1K users, X is fine. At 100K I'd add Y. At 1M I'd add Z." This shows you think incrementally, not in one giant upfront design.
🚦
Vol 1 · Ch. 4
Design a Rate Limiter
Control traffic flow. Prevent abuse. Protect downstream services. Know all 5 algorithms cold.
The 5 Algorithms
Token Bucket
Bucket holds N tokens; refills at rate R/s. Each request consumes 1 token. Reject if empty. Used by Amazon, Stripe.
PROS

Burst-tolerant. Memory efficient.

CONS

Two params hard to tune.

Leaky Bucket
FIFO queue. Requests processed at fixed output rate. Drop if queue full. Used by Shopify.
PROS

Stable outflow rate.

CONS

Bursts fill queue; recent requests dropped.

Fixed Window Counter
Divide time into fixed windows. Count requests per window. Reject if over limit.
PROS

Simple. Memory efficient.

CONS

Burst at window boundary doubles effective rate.

Sliding Window Log
Store timestamp of each request. Count timestamps in last N seconds. Reject if over limit.
PROS

Accurate. No boundary problem.

CONS

High memory — stores every timestamp.

Sliding Window Counter
Hybrid: fixed window + weighted previous window count. Approximates sliding log with much less memory.
PROS

Accurate and memory efficient. Best of both worlds.

CONS

Slight approximation vs true sliding log.

Design Considerations
TopicDetail
StorageUse Redis for shared counter across distributed nodes. Key = user_id or IP; value = count + TTL expiry.
PlacementAPI gateway (edge) or middleware per service. Gateway preferred for centralized policy management.
Response headersReturn X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Retry-After in 429 responses.
Multi-DCDeploy at edge. Use eventual consistency for cross-DC counter sync — slight over-allowing is acceptable.
Soft vs hard limitsSoft: allow small burst over limit. Hard: strictly enforce. Most production systems use soft limits.
🔵
Vol 1 · Ch. 5
Consistent Hashing
Minimize key redistribution when nodes are added or removed. Foundation of distributed data stores.
The Problem
Naive modulo hashing: hash(key) % N. Adding/removing a server reshuffles nearly ALL keys → massive cache misses, full data redistribution.
Core Concepts
ConceptDetail
Hash ringMap both server IPs and keys onto a circular ring (SHA-1 space 0 to 2¹⁶⁰). Key stored on first server clockwise from its position.
Adding a serverOnly keys between predecessor and new server need remapping — roughly k/n keys total.
Removing a serverKeys from removed server reassigned to next clockwise server. Only ~k/n keys affected.
Virtual nodesEach server gets v positions on the ring. More positions = more even distribution. Tune v per server capacity. Trade-off: more v = more even, more memory.
Data replicationWalk clockwise from key position; replicate to first N unique physical servers (skip same-DC nodes for disaster tolerance).
Hotspot limitationConsistent hashing distributes keys evenly — NOT traffic. Celebrity keys still create hot nodes. Mitigate with read replication or key salting.
Real-World Use
Amazon DynamoDB, Apache Cassandra, Discord (scaled Elixir to 5M concurrent users), Akamai CDN all use consistent hashing for data partitioning.
🗄
Vol 1 · Ch. 6
Design a Key-Value Store
Distributed hash table design covering CAP theorem, quorum, gossip protocol, and Bloom filters.
CAP Theorem
Definition
A distributed system can only guarantee 2 of 3: Consistency (all nodes see same data), Availability (always responds), Partition Tolerance (works during network splits). During partition, choose CP or AP.
CP Systems
Block reads/writes during partition. Prioritize consistency.

Examples: HBase, ZooKeeper
AP Systems
Serve potentially stale data during partition. Prioritize availability.

Examples: Cassandra, DynamoDB, Couchbase
Key Design Components
ComponentDesign Decision
PartitioningConsistent hashing distributes keys across nodes. Virtual nodes for even distribution.
ReplicationAsync replication to N nodes walking clockwise. Replicas in distinct DCs for disaster tolerance.
Quorum (W+R > N)N=3, W=2, R=2 guarantees strong consistency. Lower W/R = higher availability, lower consistency.
Conflict resolutionVector clocks: each write increments [server, version] pair. Client resolves conflicts on read.
Failure detectionGossip protocol: each node maintains heartbeat table, propagates to random neighbors. Node marked down if heartbeat stale.
Sloppy quorumWrite to W healthy nodes even if some replicas are offline. Hinted handoff: temp node stores data, pushes back when original recovers.
Write pathRequest → commit log (durability) → memory table → SSTable (disk) when memory threshold hit.
Read pathCheck memory table → Bloom filter determines SSTable → read SSTable. Bloom filter reduces unnecessary disk reads.
🔑
Vol 1 · Ch. 7
Unique ID Generator (Snowflake)
Generate IDs that are globally unique, sortable by time, and work across distributed nodes without coordination.
Approaches Compared
ApproachHow It WorksProsCons
UUID (128-bit)Generate locally using random bits. No coordination.Simple, no SPOF, no network callNot sortable, 128-bit, rare collisions possible
DB Auto-IncrementDB identity column. Multi-DB with step (server 1: 1,3,5… server 2: 2,4,6…)Simple, sortableDB bottleneck, exposes business data
Snowflake ★64-bit: sign + timestamp + datacenter + machine + sequenceSortable by time, 64-bit, ~4096 IDs/ms/machineClock skew risk, machine IDs must be managed
Ticket ServerCentralized auto-increment serverSimple, numeric IDsSPOF without HA setup
Snowflake 64-bit Layout
Bit Allocation
1 bit
sign=0
41 bits
timestamp (ms)
~69 years
5 bits
datacenter
(32 DCs)
5 bits
machine
(32/DC)
12 bits
sequence
(4096/ms)
Throughput
Snowflake generates up to 4,096 IDs per millisecond per machine without any coordination or network calls. Total capacity: 1024 machines × 4096 IDs/ms = ~4M IDs/ms globally.
🔗
Vol 1 · Ch. 8
URL Shortener (TinyURL)
Hash long URLs to short codes. Handle redirects, collisions, and scale to billions of URLs.
ComponentDesign Detail
APIsPOST /api/v1/shorten → return shortURL. GET /{shortURL} → redirect to longURL.
Redirect type301 Permanent: browser caches, reduces server load. 302 Temporary: every request hits server, better for analytics. Choose based on needs.
Hash lengthBase62 (a-z, A-Z, 0-9): 62⁷ = 3.5 trillion unique URLs. For 365B URLs → 7-char hash is sufficient.
Hash approach AMD5/SHA-1 of longURL, take first 7 chars. Collision: append random salt and retry. Simple but requires DB lookup on each.
Hash approach BBase62 encode unique auto-increment ID. No collision, but exposes sequence and requires distributed ID gen.
Data modelshortURL (PK), longURL, createdAt, expiredAt, userId
Cache layerCache top 20% most-accessed short URLs in Redis. 80% of traffic from cache (Pareto principle). 80/20 rule.
ScaleStateless web tier behind LB. DB replication + sharding on shortURL key. CDN for redirect latency reduction.
🕷
Vol 1 · Ch. 9
Web Crawler
Systematically browse the web. BFS traversal with politeness constraints, deduplication, and distributed worker fleet.
ComponentDesign Detail
Seed URLsStart with well-connected URLs. Prioritize by PageRank, traffic, domain authority.
URL frontierPriority queue + politeness queue. Priority = importance score. Politeness = rate-limit per domain (1 req/s per host). Use BFS, not DFS (depth too deep).
HTML downloaderDistributed workers using consistent hashing. Robots.txt respected. Per-request timeout. Parallel downloads.
DNS cacheCache DNS lookups to avoid N lookups per download. Cache TTL ~1 min. Big performance win at scale.
Content parserExtract URLs from HTML. Validate and filter. Bloom filter for fast URL deduplication check.
Content dedupHash page content. Store hash → skip if already seen. Handles mirrors and duplicate content across domains.
Fault toleranceCheckpoint crawl state to storage periodically. Resume from last checkpoint on failure.
PolitenessHonor robots.txt. Rate-limit per domain. Identify crawler via User-Agent. Avoid crawl traps (infinite loops).
🔔
Vol 1 · Ch. 10
Notification System
Multi-channel delivery (push, SMS, email) with deduplication, retry logic, and user preference management.
Notification Channels
ChannelProtocol / ProviderNotes
iOS PushAPNs (Apple Push Notification Service)Requires device token. Provider → APNs → device.
Android PushFCM (Firebase Cloud Messaging)Replaced deprecated GCM. Google-managed infrastructure.
SMSTwilio, Nexmo/VonageThird-party gateway. Best deliverability. Cost per SMS.
EmailSendGrid, MailchimpBetter deliverability than self-hosted SMTP.
System Design
ComponentDetail
Notification serviceSingle entry point. Builds payload. Queues to channel-specific workers via message queue.
Message queuesOne queue per channel type. Decouples producers from senders. Absorbs traffic spikes.
DeduplicationRedis cache with event_id → prevents duplicate sends on retry. Critical for at-least-once delivery.
Rate limitingDon't spam users. Limit push notifications per user per time window. Per-channel limits.
User preferencesUsers can opt out per channel. Check preference table before enqueuing. Cache preferences.
Retry logicExponential backoff with jitter. Dead-letter queue for permanently failed deliveries. Alert on DLQ depth.
📰
Vol 1 · Ch. 11
News Feed System (Facebook/Twitter)
Fan-out architecture for publishing and retrieving personalized feeds. The push vs pull tradeoff.
Two Critical Flows
Feed Publishing
User posts → API server → Post service → fanout service → message queue → workers write post_id to each follower's feed cache (Redis sorted set by timestamp)
Feed Retrieval
User requests → API server → news feed service → read from Redis feed cache → hydrate post + user data → return ranked feed
Fanout Strategy
ApproachHowBest ForProblem
Push (fanout on write)Pre-compute feed for each follower on postRegular users — fast readsExpensive writes for celebrities (10M followers)
Pull (fanout on read)Compute feed on read from followeesCelebrity accountsSlow reads — must aggregate many sources
Hybrid ★Push for regular users; pull for celebritiesProduction systemsMore complex but solves both extremes
Key Data Structure
Redis Sorted Set per user: {score: timestamp, member: post_id}. Store only post IDs in cache — fetch full data from post/user cache separately. This minimizes cache storage.
💬
Vol 1 · Ch. 12
Chat System (WhatsApp/Slack)
Real-time bidirectional messaging. WebSocket connections, presence, group chat, and multi-device sync.
Protocol Comparison
ProtocolPatternProsCons
WebSocket ★Full-duplex bidirectionalTrue real-time; server can push without pollingPersistent connections; harder to scale; requires pub/sub
HTTP Long PollingSimulated pushWorks everywhere; no special protocolHigher latency; server holds many open connections
SSEServer → client push onlySimple; built on HTTPOne-way only; can't send client messages
Architecture Components
ComponentDesign Detail
Chat serversHold WebSocket connections. ~100K connections per server. Consistent hashing routes user to assigned chat server.
Message storageKey-value store (HBase/Cassandra). Row key = channel_id + message_id (time-ordered). Not relational — write-heavy, no complex queries.
Message orderingSnowflake-style sequence ID per channel. Must be unique + ordered within a conversation.
Online presenceHeartbeat from client every 5s. Presence server stores last_active_at. Fan-out status changes to friends via Redis pub/sub.
Group chatMessage sent to channel. Fanout service writes to each member's inbox. Limit group size for fanout scalability (e.g. 500–10K).
Multi-device syncEach device polls from its last_message_id. Message queue per user for cross-device delivery.
🔍
Vol 1 · Ch. 13
Search Autocomplete System
Trie-based prefix matching with top-K suggestions. Batch aggregation + hot cache for sub-millisecond responses.
ComponentDesign Detail
Data collectionLog every search query with frequency. Aggregate daily in batch jobs (MapReduce). Build trie from aggregated frequency data weekly.
Trie structurePrefix tree. Each node stores top-k most frequent completions for that prefix cached at the node — avoids full tree traversal on lookup.
Query serviceRead from trie. Cache results in Redis/Memcached. Serve from cache for sub-ms response. Rebuild trie periodically, not per query.
Trie storageSerialize trie to document store or Redis. Shard by first character of prefix for horizontal scaling.
Real-time updatesDon't update trie on every query (too slow). Batch aggregate. Use probabilistic structures for real-time trending detection.
Browser optimizationsDebounce: only query after pause. Prefetch first few characters. CDN cache for common top-level prefixes.
Content filteringRemove hateful/inappropriate completions using content filters before storing in trie.
Vol 1 · Ch. 14
Design YouTube
Video upload pipeline with DAG transcoding, CDN delivery, adaptive bitrate streaming, and cost optimization.
Two Critical Flows
Video Upload
Client → pre-signed S3 URL (bypass API) → raw video S3 → message queue → transcoding workers (DAG pipeline) → CDN origin → serve
Video Streaming
Client → nearest CDN PoP → stream directly. NOT origin. Adaptive bitrate (DASH/HLS). Auto-quality based on bandwidth.
Architecture Deep Dive
ComponentDesign Detail
Transcoding pipelineDAG scheduler. Workers: split video into GOPs → encode at multiple bitrates (360p/720p/1080p/4K) → thumbnail → watermark → merge → upload CDN.
Pre-signed upload URLClient gets temp S3 URL. Uploads large file directly to S3. API server never handles video bytes — huge bandwidth saving.
CDN strategyPopular videos pre-warmed at all edges. Long-tail served from regional CDN or origin on demand. CDN reduces bandwidth cost 90%+.
Metadata DBVideo metadata (title, uploader, likes) in MySQL (sharded/replicated). Completely separate from video binary storage.
Streaming protocolsMPEG-DASH, HLS, Smooth Streaming. Protocol depends on client support. Adaptive bitrate: client switches quality based on current bandwidth.
Cost optimizationStore only popular videos at all CDN edges. Move infrequently accessed videos to cheaper cold storage. Compress thumbnails.
📁
Vol 1 · Ch. 15
Google Drive
Block-level storage with delta sync, deduplication, conflict resolution, and offline support.
ComponentDesign Detail
File uploadResumable chunked upload (~5MB chunks). Client retries failed chunks without restarting entire file. Chunks stored in object storage (S3).
Block storageSplit file into fixed blocks. Store each block by hash(block). Delta sync: only changed blocks re-uploaded on edit. Efficient for large files with small edits.
Metadata DBFile metadata: file_id, name, path, size, owner, created_at, blocks[]. MySQL. Separate from block storage.
Sync serviceNotification service pushes file change events to all user's devices. Long polling or WebSocket for real-time sync.
Conflict resolutionLast-write-wins OR present both versions to user (Dropbox approach). Vector clocks for advanced conflict detection.
DeduplicationBlock-level dedup: if hash(block) already exists, don't re-upload — just reference existing block. Major storage cost reduction.
Cold storageFiles not accessed in N months moved to cheaper cold storage (S3 Glacier). Transparent to user — retrieved on demand.
📍
Vol 2 · Ch. 1
Proximity Service (Yelp)
Find N nearest businesses within radius R. Geospatial indexing with Geohash or Quadtree.
Geospatial Indexing Approaches
Geohash
Encode lat/lng as base32 string. Nearby locations share prefix. Level 6 (~1.2km grid) suits most proximity searches. Search: target cell + 8 neighbors to prevent boundary edge cases.
Quadtree
Recursively subdivide 2D space into 4 quadrants until ≤100 businesses per cell. In-memory tree (~1GB for 200M businesses). Fast reads; complex real-time updates.
Design DecisionDetail
Read vs write ratioRead-heavy (searches) vs write-light (business updates). Separate read replicas for geospatial queries.
Boundary problemGeohash boundary: a business just across the cell boundary won't appear in exact cell search. Always search target cell + 8 surrounding cells.
Precision levelsGeohash length 4 = ~39km, 5 = ~4.9km, 6 = ~1.2km, 7 = ~152m. Choose based on search radius.
Cache strategyCache popular search results by (city, category) with TTL ~10min. Precompute for major metro areas.
Business serviceCRUD for business metadata separate from location/search service. Cache business info in Redis.
👥
Vol 2 · Ch. 2
Nearby Friends
Real-time location sharing. Unlike static businesses — friends move. Continuous GPS updates via WebSocket + Redis pub/sub.
Key Difference from Proximity Service
Businesses are static. Friends move in real-time. Location must be continuously pushed to friends, not just queried on demand.
ComponentDesign Detail
Location updatesClient sends GPS every 30s via WebSocket. Location history service stores to Cassandra (append-only, time-series).
Redis pub/subLocation update → publish to Redis channel per user. Friends subscribed to that channel receive updates instantly.
Fan-outFor each user who updates: find friend list → for each online friend → push update via their WebSocket connection.
Distance computationHaversine formula on incoming friend location update. Only surface friend if within threshold (e.g. 5km).
ScaleRedis Cluster. Pub/sub channels sharded by user_id. Location servers route pub/sub messages to correct WebSocket connection server.
🗺
Vol 2 · Ch. 3
Google Maps
Map tiles, routing graph, live traffic aggregation, and ETA prediction at global scale.
ComponentDesign Detail
Map tilesPre-rendered image tiles at zoom levels 0–21. Served from CDN keyed by tile ID (zoom/x/y). Client loads tiles on demand as user pans/zooms.
Road graphGraph: nodes = intersections, edges = segments with weight = travel time. Compressed adjacency list. Partitioned geographically.
Routing algorithmA* or Bidirectional Dijkstra. Preprocessed contraction hierarchies for fast long-distance routing. Live traffic adjusts edge weights dynamically.
Live trafficGPS data from millions of phones aggregated in real-time. Streaming MapReduce → segment travel times → update graph edge weights.
ETA predictionHistorical speed data + real-time traffic → ML model for ETA per segment. Sum along route. Recalculate on traffic changes.
Data encodingGeohash or S2 cells for location queries. Protocol Buffers for compact tile data transfer. ~50% size reduction vs JSON.
📨
Vol 2 · Ch. 4
Distributed Message Queue (Kafka)
High-throughput, durable, replayable event streaming. Partitioned log model with consumer groups.
ConceptDetail
Core modelProducer → Topic (partitioned) → Consumer Group. Each partition is an ordered, append-only log.
PartitioningTopic split into N partitions distributed across brokers. Partition key determines which partition (hash(user_id) % N). Enables parallel consumption.
Consumer groupsEach consumer in a group reads from disjoint partitions. Multiple groups read same topic independently without interference.
OffsetConsumer tracks its position (offset) per partition. Stored in __consumer_offsets topic. Enables replay and at-least-once delivery.
Delivery semanticsAt-most-once (fire/forget). At-least-once (ack + retry). Exactly-once (idempotent producer + transactional consumer). Most systems use at-least-once.
ReplicationEach partition: 1 leader + N-1 follower replicas. Leader handles reads/writes. Leader election via ZooKeeper/Raft on broker failure.
RetentionMessages retained for N days regardless of consumption. Consumers can replay from any offset. Enables event sourcing, multiple consumer groups.
📊
Vol 2 · Ch. 5
Metrics Monitoring & Alerting
Time-series data collection, storage, query, visualization, and alerting at infrastructure scale.
ComponentDesign Detail
Data collectionAgents on each host collect metrics (CPU, mem, disk, custom app metrics) at configurable interval (every 10s). Push to metrics collector.
Metrics collectorReceives data points. Validates. Writes to time-series DB. Kafka as buffer to handle collection spikes.
Time-series DBOptimized for sequential writes of (timestamp, value, tags). Examples: InfluxDB, Prometheus TSDB, OpenTSDB. NOT relational DB.
Query serviceQuery language (PromQL, Flux) to aggregate: sum, avg, p99 over time windows. Powers dashboards and alert evaluation.
AlertingRules define threshold conditions. Alerting service evaluates rules against metric stream. Routes to PagerDuty, Slack, email.
DownsamplingRecent data: 10s resolution. Older data: downsample to 1min, 1hr. Reduces storage cost without losing trend visibility.
Metric typesHost-level (CPU/mem), aggregated tier metrics (DB/cache perf), business metrics (DAU, revenue, retention, conversion).
📈
Vol 2 · Ch. 6
Ad Click Event Aggregation
Billion-scale click ingestion with streaming aggregation, late event handling, and billing-grade accuracy.
Core Challenges
High write volume (billions of clicks/day). Aggregation must be accurate for billing. Late-arriving events (network delay) must be handled. Results queried in real-time.
ComponentDesign Detail
Data flowAd click → Kafka → aggregation service (Flink/Spark Streaming) → aggregated results DB → query API
Aggregation serviceStreaming aggregation: count clicks per ad per N-minute window. Emit counts to results store. Handles out-of-order events with watermarks.
StorageRaw events: Kafka (7-day retention). Aggregated results: ClickHouse or Cassandra (fast range queries on time + ad_id).
Late event handlingWatermark: allow events up to X minutes late. Re-aggregate window on late arrival. Mark and reprocess affected windows.
Deduplicationevent_id (Snowflake ID) deduplicates at-least-once delivery. Redis Set per time window for fast dedup lookup.
ReconciliationEnd-to-end: reprocess raw Kafka events daily in batch (MapReduce) to validate streaming counts. Fix discrepancies. Critical for billing accuracy.
Kafka partitioningPartition by ad_id → same ad's events go to same partition → consistent aggregation without distributed join.
🏨
Vol 2 · Ch. 7
Hotel Reservation System
Inventory management with race condition prevention, idempotency, and overbooking buffer logic.
Core Challenge
Race condition: two users trying to book the last room simultaneously. Must prevent double booking without sacrificing performance.
ComponentDesign Detail
Data modelhotel, room_type, room_inventory(hotel_id, room_type_id, date, total_rooms, reserved_rooms), reservation(id, user_id, hotel_id, check_in, check_out, status)
Availability checkSELECT total_rooms - reserved_rooms WHERE date BETWEEN check_in AND check_out. Must be transactional with the booking write.
Optimistic lockingAdd version field: UPDATE room_inventory SET reserved=reserved+1, version=version+1 WHERE id=? AND reserved < total AND version=?. Retry on conflict.
DB transactionWrap availability check + update in single DB transaction with SELECT FOR UPDATE. Prevents race conditions at DB level.
Overbooking bufferHotel industry practice: allow ~5% over capacity to account for cancellations. Configurable per hotel and room type.
IdempotencyReservation request carries idempotency_key. Server deduplicates on this key. Safe to retry failed network requests without double booking.
Vol 2 · Ch. 8
Distributed Email Service
SMTP send/receive pipeline, S3 storage for bodies, full-text search, and deliverability infrastructure.
ComponentDesign Detail
ProtocolsSMTP (send), IMAP (retrieve, sync across devices), POP3 (download and delete). Modern webmail uses proprietary HTTP APIs.
Send flowUser → send API → metadata DB (store email) → message queue → SMTP outbound servers → DNS MX lookup → recipient mail server → inbox
Receive flowInbound SMTP server → content filter (spam/virus) → S3 (body) + metadata DB (headers/attachments list) → IMAP server → client
StorageEmail body: S3/object storage. Metadata + headers: Cassandra (append-only, query by user + time). Attachments: S3 with CDN for download.
SearchFull-text search on subject/body via Elasticsearch. Index updated asynchronously from write path. Not relational DB search.
Spam/virus filteringMultiple layers: IP reputation, content scoring, ML classifier, virus scanner. All queued before inbox delivery.
DeliverabilitySPF, DKIM, DMARC records prevent spoofing and impersonation. Dedicated IP warming for new sending IPs. Bounce handling.
🪣
Vol 2 · Ch. 9
S3-like Object Storage
Immutable blob storage with erasure coding, multipart upload, versioning, and placement service.
ComponentDesign Detail
Core abstractionImmutable objects stored by bucket/key. Flat namespace per bucket (not a real file system). Objects up to terabytes.
Upload pathClient → API service (generate UUID, write metadata) → data store routing → data nodes (replicate to N) → return object_id
Data nodesStore objects as files on disk. Heartbeat to placement service. N=3 replicas standard. Cross-AZ replication for durability.
Metadata DBObject metadata: object_id, bucket, key, size, hash, created_at, storage_node_ids. MySQL/Postgres. Separate from actual data.
Erasure codingSplit object into k data + m parity shards. Any k shards can reconstruct. 1.5× storage overhead vs 3× replication. Slower recovery but much more efficient.
Multipart uploadLarge objects split into parts (5MB–5GB each). Upload parts in parallel. Assemble on server. Retry only failed parts.
VersioningEach PUT creates new version. Delete creates delete marker. GET returns latest; specify version_id for older version. Critical for accidental delete recovery.
🏆
Vol 2 · Ch. 10
Real-time Gaming Leaderboard
Redis Sorted Set as the core data structure. Sub-millisecond rank queries for millions of players.
Core Data Structure
Redis Sorted Set: ZADD leaderboard {score} {user_id} — O(log N) insert/update. ZREVRANK for rank in O(log N). ZREVRANGE leaderboard 0 K-1 WITHSCORES for top-K in O(log N + K).
OperationRedis CommandComplexity
Update scoreZADD leaderboard score user_idO(log N)
Get user rankZREVRANK leaderboard user_idO(log N)
Get top-KZREVRANGE leaderboard 0 K-1 WITHSCORESO(log N + K)
Get scoreZSCORE leaderboard user_idO(1)
Design TopicDetail
PersistenceRedis is primary. Async write to MySQL for durability. Rebuild Redis from DB on restart. Eventual consistency acceptable.
ScaleRedis Cluster: shard by game_id or region. For global top-K across shards: each shard returns top-K, merge and sort at API layer.
Historical leaderboardsSeparate sorted sets per time window (daily/weekly/all-time). TTL on older sets to auto-expire. Key: leaderboard:{game_id}:{period}
Anti-cheatServer-side score validation. Score anomaly detection. Rate limit score update API per user.
💳
Vol 2 · Ch. 11
Payment System
Exactly-once processing, idempotency, distributed transactions, double-entry ledger, and reconciliation.
Core Challenges
Exactly-once processing. Idempotency across retries. Distributed transactions across services. Reconciliation for discrepancies. Regulatory compliance (PCI-DSS).
ComponentDesign Detail
Payment flowUser → payment service → PSP (Stripe/Adyen) → bank/card network → settlement → ledger update → notify user
IdempotencyEvery request carries unique idempotency_key (UUID). Payment service deduplicates on this key. Safe to retry on timeout without double charge.
Payment statesPending → Processing → Success / Failed / Cancelled. State machine with explicit transitions stored in payment_orders table.
Double-entry ledgerEvery payment = debit one account + credit another. Immutable append-only entries. Ensures books always balance. Enables full audit trail.
PSP idempotencyPSP (Stripe, etc.) also guarantees idempotency with their own key. Our key + their key → two levels of protection against duplicate charges.
ReconciliationDaily batch job compares internal ledger vs PSP settlement statement. Flag discrepancies. Human review for unmatched records.
Retry strategyExponential backoff with jitter. Dead-letter queue for permanently failed payments. Alert on DLQ depth. Never retry indefinitely.
Appendix
Quick Reference Tables
Database selection guide, CAP theorem, protocol comparison, and interview problem → answer lookup.
Database Selection Guide
Use CaseBest ChoiceWhyKey Commands
User profiles, orders, billing MySQL / PostgreSQL ACID transactions, complex JOINs, relational schema, strong consistency
e.g. Uber rider/driver profiles · Airbnb listings · Stripe payment records
  • SELECT * FROM users WHERE id=? LIMIT 1Primary key lookup — fastest query, uses clustered index
  • INSERT INTO orders (user_id, amount) VALUES (?,?)Write a new row; always wrap in transaction with related inserts
  • ALTER TABLE users ADD INDEX idx_email (email)Add secondary index online; use pt-osc on large tables to avoid lock
Session / cache / rate limiting Redis In-memory, sub-ms latency, sorted sets, pub/sub, TTL-based expiry
e.g. Twitter trending cache · Discord leaderboard · Slack session tokens
  • SET session:{id} {data} EX 3600Store session data with TTL; key expires automatically after N seconds
  • GET session:{id}Retrieve session; returns nil if expired — check before use
  • ZADD leaderboard {score} {user_id}Insert/update a player's score; Redis auto-sorts by score
  • ZREVRANGE leaderboard 0 99 WITHSCORESFetch top-100 players in descending score order, O(log N + 100)
Chat messages / time-series writes Cassandra Append-only, high write throughput, wide columns, tunable consistency
e.g. WhatsApp message store · Discord history · Instagram activity log
  • INSERT INTO messages (conv_id,msg_id,body) VALUES (?,?,?)Append a message; partition key = conv_id keeps all rows co-located
  • SELECT * FROM messages WHERE conv_id=? ORDER BY msg_id DESC LIMIT 50Fetch last 50 messages in a conversation; ORDER BY clustering key
  • CREATE TABLE messages (conv_id uuid, msg_id timeuuid, PRIMARY KEY (conv_id, msg_id))Wide-column schema: conv_id partitions data, msg_id orders within
Full-text search / autocomplete Elasticsearch Inverted index, fuzzy match, faceted filters, geo queries, aggregations
e.g. Airbnb listing search · GitHub code search · Spotify track search
  • GET /index/_search { "query": { "match": { "title": "hotel" } } }Full-text search across indexed fields; match = analyzed tokenization
  • GET /index/_search { "query": { "geo_distance": { "distance": "5km" } } }Find documents within radius of a lat/lng point
  • PUT /index/_mapping { "properties": { "title": { "type": "text" } } }Define field types; must do before indexing or reindex if changing
Product catalog / content (flexible) MongoDB Schema-flexible documents, rich queries, horizontal sharding
e.g. Netflix show catalog · Shopify product docs · Medium article metadata
  • db.products.find({ category: "shoes", price: { $lt: 100 } })Query documents with compound filter; uses compound index if present
  • db.products.createIndex({ category: 1, price: 1 })Create compound index for common query pattern; order matters
  • db.products.updateOne({ _id: id }, { $set: { stock: 42 } })$set updates specific fields without replacing the whole document
Social graph / fraud detection Neo4j Native graph traversal, friend-of-friend queries, relationship-heavy queries
e.g. LinkedIn connections · Twitter follow graph · Pinterest interest graph
  • MATCH (a:User)-[:FOLLOWS]->(b:User) WHERE a.id=$id RETURN bTraverse one hop in graph; returns all direct followers of user
  • MATCH (a)-[:FOLLOWS*2]->(c) WHERE a.id=$id RETURN DISTINCT cTraverse two hops; finds friends-of-friends for recommendations
  • CREATE (a:User {id:$id})-[:FOLLOWS]->(b:User {id:$bid})Create two nodes and a directed relationship in one query
Video / images / blobs S3 / Object Storage Cheap durable blob store, CDN integration, 11-nines durability, unlimited scale
e.g. YouTube raw uploads · Dropbox file blocks · Instagram photos
  • aws s3 cp video.mp4 s3://bucket/keyUpload file to S3; add --storage-class INTELLIGENT_TIERING for cost savings
  • aws s3 presign s3://bucket/key --expires-in 3600Generate a temporary URL valid for N seconds; no auth needed to access
  • aws s3api put-object --bucket b --key k --body fileUpload with explicit ACL; use public-read for CDN-served assets
Click events / analytics (OLAP) ClickHouse / Druid Columnar storage, fast aggregation on billions of rows, petabyte scale
e.g. Cloudflare 50M events/sec · Uber ad-click pipeline · Datadog metrics
  • SELECT ad_id, count() FROM clicks WHERE date=today() GROUP BY ad_idReal-time aggregation in ClickHouse; columnar scan is extremely fast
  • INSERT INTO clicks (ad_id, ts, user_id) VALUES (?,?,?)Append-only write; ClickHouse MergeTree is optimized for bulk inserts
  • CREATE TABLE clicks ENGINE=MergeTree() PARTITION BY toDate(ts) ORDER BY (ad_id,ts)MergeTree partitioned by date, sorted by ad_id+ts for fast time-range queries
Event streaming / message bus Kafka High throughput, replayable log, consumer groups, partitioned ordering
e.g. LinkedIn activity feed · Airbnb booking pipeline · Uber trip events
  • kafka-console-producer --topic events --bootstrap-server localhost:9092CLI producer for testing; not for production (no acks, no retries)
  • kafka-console-consumer --topic events --from-beginning--from-beginning replays all retained messages from offset 0
  • kafka-topics --create --topic events --partitions 12 --replication-factor 3Create topic with 12 partitions = max 12 concurrent consumers per group
Geospatial / nearby queries Redis GEO / PostGIS GEORADIUS for point-radius, PostGIS for complex polygons, spatial index
e.g. Yelp radius search · Uber surge zones · Google Maps POI lookup
  • GEOADD locations {lng} {lat} {biz_id}Store location as lng/lat; key is the entity ID (business, driver)
  • GEORADIUS locations {lng} {lat} 5 km ASC COUNT 20Find all entities within 5km sorted by distance; returns up to 20
  • GEODIST locations biz1 biz2 kmCalculate exact distance between two stored geo points in km
CAP Theorem Quick Reference
SystemCAPBehaviorKey Commands
HBase CP Blocks during partition. Strong consistency. Used in Hadoop ecosystem.
e.g. Facebook Messenger history · Apache HBase analytics at Yahoo
  • get 'table', 'rowkey', 'cf:col'HBase single-row read; rowkey design determines partition and sort order
  • put 'table', 'rowkey', 'cf:col', 'value'HBase single-cell write; atomic at cell level
  • scan 'table', {LIMIT => 10}Full table scan with limit; avoid without row range — scans all regions
ZooKeeper CP Distributed coordination, leader election. Blocks writes on partition.
e.g. Kafka broker coordination · Hadoop NameNode HA · Cassandra gossip seed
  • zkCli.sh create /lock ''Create an ephemeral ZK node for distributed locking
  • zkCli.sh get /config/serviceRead a config value; clients watch this path for real-time updates
  • zkCli.sh stat /leaderCheck node metadata: version, last-modified, number of children
MongoDB CP Primary-only writes. Secondary reads may be stale. Strong by default.
e.g. Robinhood trade records · Stripe config service · Coinbase account store
  • db.col.find({}, {readPreference: "primary"})Force read from primary replica — use for strong consistency reads
  • db.col.findOneAndUpdate({_id:id},{$inc:{v:1}},{returnDocument:"after"})$inc atomically increments version; returnDocument:after returns new value
  • db.col.createIndex({field:1},{unique:true})Unique index enforces no duplicates; fails write if value already exists
Cassandra AP (tunable) Eventual consistency default. QUORUM reads/writes give CP-like behavior.
e.g. Instagram timeline · Netflix viewing history · Discord messages (500M/day)
  • SELECT * FROM tbl WHERE key=? LIMIT 10Cassandra partition key lookup — must always include partition key
  • INSERT INTO tbl (k,v) VALUES (?,?) IF NOT EXISTSLightweight transaction (Paxos); prevents duplicate inserts with IF NOT EXISTS
  • CONSISTENCY QUORUMSet per-session consistency; QUORUM = majority of replicas must agree
DynamoDB AP (default) Eventual consistency default. ConsistentRead=true for strong (2× RCU cost).
e.g. Amazon shopping cart · Snapchat stories · Lyft real-time driver location
  • aws dynamodb get-item --table T --key '{"pk":{"S":"x"}}'Single-item read by primary key; add --consistent-read for strong consistency
  • aws dynamodb put-item --table T --item '...' --condition-expression 'attribute_not_exists(pk)'Write item to DynamoDB; condition expression prevents accidental overwrites
  • aws dynamodb query --table T --key-condition 'pk = :v'Query partition; cheaper than Scan — reads only one partition's data
Redis Cluster AP Eventual consistency during network partition. Single-node Redis = no partition.
e.g. Twitter rate limiter · Airbnb session store · Uber surge pricing cache
  • INCR rate:{user}:{window}Atomic counter increment for rate limiting; returns new count
  • EXPIRE rate:{user}:{window} 60Set TTL on rate limit key; auto-resets window every 60 seconds
  • CLUSTER INFOShow Redis Cluster health: slot coverage, replication state, node count
Google Spanner CP + external consistency Global strong consistency via TrueTime. Only CP system that scales globally.
e.g. Google Ads billing · YouTube content metadata · Stripe global ledger
  • SELECT * FROM T WHERE pk=@pkSpanner SQL read; routes to leader for strong consistency by default
  • INSERT INTO T (pk, val) VALUES (@pk, @val)Spanner write; must be inside a transaction for atomicity
  • BEGIN TRANSACTION; ...; COMMIT;Explicit Spanner transaction; uses TrueTime for external consistency
Communication Protocols
ProtocolPatternUse Case + ExampleKey Commands
REST / HTTP Request-Response Public APIs, CRUD operations, microservices, browser clients
e.g. GitHub API · Stripe Payments API · Twitter REST API
  • curl -X GET https://api.example.com/users/1 -H 'Authorization: Bearer TOKEN'REST GET with Bearer auth token in header
  • curl -X POST https://api.example.com/users -d '{"name":"Alice"}' -H 'Content-Type: application/json'REST POST with JSON body; always set Content-Type header
  • curl -X PUT https://api.example.com/users/1 -d '{"email":"a@b.com"}'REST PUT for full resource replacement; use PATCH for partial update
WebSocket Full-duplex persistent Chat, live gaming, collaborative editing, real-time dashboards
e.g. Slack messaging · Robinhood live quotes · Figma multiplayer
  • const ws = new WebSocket('wss://chat.example.com')Open persistent bi-directional connection; fires onopen when ready
  • ws.send(JSON.stringify({type:'msg', body:'hello'}))Send message to server; JSON.stringify converts object to string
  • ws.onmessage = (e) => console.log(JSON.parse(e.data))Receive message handler; JSON.parse to deserialize incoming data
SSE Server push (one-way) Live feeds, deploy logs, notification streams — client can't send back
e.g. GitHub Actions logs · Vercel deploy output · Twitter live timeline
  • const es = new EventSource('/stream')Open SSE stream; browser auto-reconnects on disconnect
  • es.onmessage = (e) => console.log(e.data)Handle server-pushed events; e.data is the payload string
  • // Server: res.write('data: {"msg":"hello"}\n\n')SSE format: 'data: payload\n\n' (double newline ends each event)
gRPC Binary RPC (HTTP/2) Internal microservice comms, streaming, low latency, type-safe contracts
e.g. Google internal services · Netflix gRPC mesh · Cloudflare edge workers
  • grpc.Dial('localhost:50051', grpc.WithInsecure())Open gRPC channel; use credentials.NewTLS in production (not Insecure)
  • stub.SayHello(ctx, &pb.HelloRequest{Name: 'Alice'})Make unary RPC call; stub generated by protoc from .proto definition
  • protoc --go_out=. --go-grpc_out=. service.protoGenerate Go gRPC client/server stubs from .proto schema file
GraphQL Flexible query Mobile clients with diverse data needs, reduces over-fetching/under-fetching
e.g. GitHub GraphQL API v4 · Shopify Storefront API · Facebook news feed
  • curl -X POST /graphql -d '{"query":"{ user(id:1){ name email } }"}'GraphQL query; fetches only requested fields (no over-fetching)
  • curl -X POST /graphql -d '{"query":"mutation { createUser(name:\"A\") { id } }"}'GraphQL mutation to write data; returns specified fields of new object
  • // Fragment: fragment UserFields on User { id name }Reusable field selection; reduces duplication across multiple queries
Long Polling Simulated push Legacy real-time where WebSocket unavailable; simple infrastructure
e.g. Basecamp classic · older Jira realtime · Dropbox early notification system
  • fetch('/poll?since=' + lastId).then(r => r.json()).then(handleUpdate)Long poll: send last known ID; server holds until new data or timeout
  • // Server: hold request open until new data or 30s timeoutServer keeps response open; flushes immediately when new data arrives
  • // Retry immediately on response receivedClient re-polls instantly after response; creates continuous polling loop
MQTT Pub/sub IoT IoT sensors and devices, low-bandwidth, unreliable networks, QoS levels
e.g. AWS IoT Core · Tesla vehicle telemetry · Philips Hue hub protocol
  • mosquitto_pub -h broker -t sensor/temp -m '22.5'MQTT publish; QoS 1 = at-least-once delivery with acknowledgment
  • mosquitto_sub -h broker -t sensor/#MQTT subscribe with wildcard; # matches all sub-topics of sensor/
  • mqtt.connect('mqtt://broker', {qos: 1})Connect with QoS 1; broker stores message if subscriber offline
WebRTC Peer-to-peer media Video/audio calls, screen sharing, P2P file transfer
e.g. Google Meet · Discord voice · WhatsApp video calls
  • const pc = new RTCPeerConnection(iceConfig)Create WebRTC peer; iceConfig contains STUN/TURN server URLs
  • const offer = await pc.createOffer(); await pc.setLocalDescription(offer)SDP offer describes media capabilities; set as local description first
  • pc.onicecandidate = (e) => signalingChannel.send(e.candidate)ICE candidates are network paths; send each to peer via signaling channel
Interview Quick-Fire: Problem → Answer
ProblemStaff-Level Answer + Concrete ExampleKey Commands
Reduce DB read load Add read replicas + Redis cache. Cache-aside pattern. Target >90% cache hit rate. Expire on write.
Real world: Netflix: 95% of API responses served from Redis · Twitter: feed from Redis sorted set, never DB
  • SET key {value} EX 300Cache-aside write with 5-min TTL; application sets on DB read
  • GET keyCache hit check; nil response = cache miss, fall through to DB
  • -- Add read replica: SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTEDDirect reads to replica; READ UNCOMMITTED avoids replica lock waits
Handle traffic spikes Stateless web tier + autoscaling. Message queue to buffer async work. Circuit breaker for downstream.
Real world: Shopify: queues checkout writes during Black Friday · Slack: SQS absorbs notification bursts
  • aws autoscaling put-scaling-policy --policy-type TargetTrackingScalingTarget-tracking autoscaling; scale out when CPU/requests exceed threshold
  • sqs.send_message(QueueUrl=url, MessageBody=json.dumps(event))Enqueue async work; decouples API response time from processing time
  • # Circuit breaker: hystrix.command('svc').execute()Fail fast when downstream is unhealthy; prevents cascade failures
Eliminate single point of failure Redundancy at every tier: dual LBs, multiple web servers, DB replication, multi-AZ cache.
Real world: GitHub: Anycast IP across multiple LBs · Netflix: active-active across 3 AWS regions
  • aws elb create-load-balancer --name my-lb --subnets sub1 sub2Create ALB across two subnets (AZs) for high availability
  • aws rds create-db-instance-read-replica --source-db-instance-identifier primaryAdd read replica; takes 5-20 min; no downtime on source
  • # Health check: GET /health → 200 OKLB health probe; remove instance from rotation on failure
Reduce global latency CDN for statics. GeoDNS routes to nearest DC. Edge caching. Regional service replicas.
Real world: Cloudflare: 300+ PoPs serve statics · Discord: regional voice servers with GeoDNS routing
  • aws cloudfront create-distribution --origin-domain-name bucket.s3.amazonaws.comCDN in front of S3 origin; set TTL and cache behaviors per path
  • # DNS: ALIAS api.example.com → latency-based routing policyLatency routing sends users to lowest-latency region automatically
  • curl -I https://cdn.example.com/img.png # x-cache: Hit from cloudfrontVerify CDN caching: x-cache:Hit = served from edge, miss = origin
Prevent double booking SELECT FOR UPDATE (pessimistic) or version-field CAS (optimistic). Idempotency key on every request.
Real world: Booking.com: SELECT FOR UPDATE on room_inventory · Stripe: idempotency key deduplicates retries
  • BEGIN; SELECT * FROM inventory WHERE id=? FOR UPDATE;Pessimistic lock: blocks concurrent writers until COMMIT
  • UPDATE inventory SET reserved=reserved+1 WHERE id=? AND reserved < total;Atomic decrement with guard clause; fails silently if already sold out
  • COMMIT; -- lock released, second request unblocks and sees updated rowRelease row lock; waiting transactions unblock and see updated values
Scale to 1B chat users WebSocket servers + Redis pub/sub for routing + Cassandra for durable message store + consistent hash.
Real world: WhatsApp: 1B users on Erlang + Cassandra · Discord: Elixir WebSocket + Cassandra + Redis pub/sub
  • PUBLISH channel:{user_id} '{"from":"A","body":"hi"}'Fan-out to all servers subscribed to this user's channel
  • SUBSCRIBE channel:{user_id}Chat server subscribes to receive messages for its connected users
  • -- Cassandra: INSERT INTO msgs(conv_id,msg_id,body) VALUES(?,now(),?)now() generates a time UUID — monotonically increasing message ID
Exactly-once payments Idempotency key stored in Redis (24h TTL) + PSP-level idempotency. Daily reconciliation vs PSP CSV.
Real world: Stripe: idempotency_key on every API call · Uber: Flink exactly-once for fare calculation
  • SET idem:{key} 1 NX EX 86400 -- 0=duplicate, 1=newNX = set only if Not eXists; returns 0 if already processed (duplicate)
  • stripe.charges.create(amount=100, idempotency_key='order_123')PSP-level idempotency: same key = same result, no double charge
  • -- Reconcile: SELECT * FROM payments WHERE status='PENDING' AND created_at < NOW()-INTERVAL 1 DAYFind stuck PENDING payments; retry or cancel after threshold time
Find nearby places Geohash level 6 in Redis GEO. Query target cell + 8 neighbors to fix boundary miss. Haversine filter.
Real world: Yelp: Redis GEORADIUS for restaurant search · Uber: geohash for driver matching grid
  • GEORADIUS locations {lng} {lat} 5 km ASC COUNT 20 WITHCOORDFind all entities within 5km sorted by distance; returns up to 20
  • GEOADD locations {lng} {lat} {biz_id}Store location as lng/lat; key is the entity ID (business, driver)
  • SELECT *, ST_Distance(geom, ST_Point(lng,lat)) d FROM places ORDER BY d LIMIT 20PostGIS exact distance sort; use spatial index (GIST) on geom column
Store + serve 1B videos S3 raw upload → parallel DAG transcoding → HLS segments → CDN. Metadata in MySQL. ABR streaming.
Real world: YouTube: TUS upload + GPU transcode farm + 500+ CDN POPs · Netflix: 1200 quality variants
  • aws s3 cp video.mp4 s3://raw-bucket/$(uuidgen).mp4Upload file to S3; add --storage-class INTELLIGENT_TIERING for cost savings
  • ffmpeg -i input.mp4 -vf scale=1280:720 -c:v libx264 output_720p.mp4Transcode to 720p H.264; add -crf 23 to control quality vs file size
  • # HLS: ffmpeg -i input.mp4 -codec copy -start_number 0 -hls_time 6 -hls_list_size 0 out.m3u8Segment into 6-second HLS chunks; -hls_list_size 0 = keep all segments
Generate IDs at 10K/ms Snowflake: 41-bit ts + 10-bit machine + 12-bit seq = 4096/ms/machine. ZooKeeper assigns machine ID.
Real world: Twitter Snowflake: all tweet IDs · Instagram: encodes shard ID + table in ID · Uber: UUID v5
  • # Snowflake: id = (ts_ms - epoch) << 22 | machine_id << 12 | seqBit layout: 41-bit timestamp + 10-bit machine ID + 12-bit sequence
  • SELECT NEXTVAL('global_seq') -- Postgres sequencePostgres atomic sequence; safe for concurrent callers, no gaps guaranteed
  • # ZooKeeper: create -s /ids/id- → returns id-0000001234Sequential ephemeral node; ZK appends monotonic number as suffix
Aggregate billions of events Kafka ingest → Flink tumbling windows → ClickHouse. Dedup by event_id in RocksDB state (24h TTL).
Real world: Uber ad-click pipeline: 50K clicks/sec · Cloudflare analytics: 50M events/sec into ClickHouse
  • kafka-console-consumer --topic clicks --from-beginning | wc -l--from-beginning replays all retained messages from offset 0
  • # Flink: stream.keyBy('ad_id').window(TumblingProcessingTimeWindows.of(Time.minutes(1))).sum('count')Tumbling 1-min window per ad_id; emits aggregate at window close
  • SELECT ad_id, count(*), sum(spend) FROM clicks WHERE ts > now()-60 GROUP BY ad_idReal-time last-60s aggregation; works in ClickHouse/TimescaleDB
Fix DB write bottleneck Shard by consistent hashing on write key. CQRS to split read/write paths. Async writes via Kafka.
Real world: Discord: shards by guild_id · Instagram: shards by user_id · Cassandra: partition key = conv_id
  • -- Shard key: user_id % 16 → route to shard NConsistent modulo sharding; rebalance requires migration if N changes
  • SHOW CREATE TABLE users\G -- check index coverageVerify all indexes and column types; \G formats output vertically
  • # CQRS: write to Kafka, async consumer updates read DBCommand writes to Kafka; async consumer projects to read-optimized DB
Design a rate limiterToken bucket in Redis with Lua script for atomicity. Key = userId:windowId. SETNX + EXPIRE for fixed window; sliding window = sorted set of timestamps. Return 429 + Retry-After header on breach. Fail-open if Redis down.
Real world: Stripe: per-API-key token bucket via Redis Lua — same atomic check+decrement on every request · GitHub: 5000 req/hr per token, X-RateLimit-* headers
  • -- Lua: atomic check+increment local count = redis.call('INCR', key) if count == 1 then redis.call('EXPIRE', key, window) end return count
  • ZADD sliding:{uid} {now} {now} -- add current ts ZREMRANGEBYSCORE sliding:{uid} 0 {now-window} ZCARD sliding:{uid} -- current count
  • redis-cli SET rate:{uid}:{window} 0 NX EX 60 redis-cli INCR rate:{uid}:{window}
Build a type-ahead / autocompleteTrie with top-K cached at each node. Redis sorted set per prefix: ZADD prefix:{str} score term. Query: ZREVRANGE prefix:{q} 0 4 for top-5. Build trie offline from search logs; push to Redis. Edge CDN caches responses per prefix.
Real world: Google Search: trie + ML ranking + personalization · Twitter: Redis sorted sets per prefix for @mention autocomplete · Elasticsearch prefix queries for Airbnb search
  • ZADD prefix:"sea" 9823 "seattle" 7201 "search" 6540 "seattle seahawks"
  • ZREVRANGE prefix:"sea" 0 4 WITHSCORES -- top-5 completions
  • GET /autocomplete?q=sea -- CDN cached, TTL 60s -- Cache-Control: public, max-age=60
Serve a social media feedFan-out on write for ≤5K followers: push post_id to each follower's Redis sorted set (score = timestamp). Fan-out on read for celebrities. Feed read = ZREVRANGE uid:feed 0 19. Hybrid: push to active followers only. Cap feed at 1000 items.
Real world: Instagram: fan-out on write to follower feeds via async Kafka workers · Twitter: push for normal users, pull for celebrities (>100K followers), merge at read time
  • ZADD feed:{uid} {ts} {post_id} -- fan-out write ZREMRANGEBYRANK feed:{uid} 0 -1001 -- cap at 1000
  • ZREVRANGE feed:{uid} 0 19 WITHSCORES -- latest 20 posts
  • -- Kafka fan-out worker consumer.subscribe('post.created') for follower_id in get_followers(post.user_id): redis.zadd(f'feed:{follower_id}', {ts: post_id})
Send notifications at scaleDecouple trigger from delivery via Kafka. Notification service reads events, looks up user preferences + device tokens. Route to channel workers (APNs, FCM, SMS, email). Retry with exponential backoff. Deduplicate with Redis SET (notif_id, TTL 24h). Batch APNs/FCM pushes.
Real world: Uber: Kafka event → notification fanout → FCM/APNs/SMS per channel worker · Airbnb: booking event triggers email via SendGrid + push via FCM, deduped by event_id
  • SET dedup:{notif_id} 1 NX EX 86400 -- 0=duplicate
  • -- APNs batch push (HTTP/2 multiplexing) curl -X POST https://api.push.apple.com/3/device/{token} -H 'authorization: bearer {jwt}' -d '{"aps":{"alert":"Your order is ready"}}'
  • aws sns publish --topic-arn arn:aws:sns:... --message '{"default":"msg","GCM":"{...}","APNS":"{...}"}' --message-structure json
Store and query time-series dataAppend-only writes: TimescaleDB (Postgres extension) or InfluxDB. Partition by time (daily/monthly chunks). Compress old chunks automatically. Downsample: 1s raw → 1min avg → 1hr avg via continuous aggregates. Retain raw 7 days, aggregates 1 year. Index on (metric_name, time DESC).
Real world: Datadog: InfluxDB-style storage, downsampled to 1min after 7 days · Tesla: TimescaleDB for vehicle telemetry, 10M datapoints/sec · Prometheus: local TSDB with 15-day retention
  • -- TimescaleDB: create hypertable partitioned by time SELECT create_hypertable('metrics', 'ts', chunk_time_interval => INTERVAL '1 day');
  • -- Continuous aggregate: 1-min rollup CREATE MATERIALIZED VIEW metrics_1min WITH (timescaledb.continuous) AS SELECT time_bucket('1 minute', ts) AS bucket, avg(value) FROM metrics GROUP BY 1;
  • SELECT time_bucket('1h', ts) AS hour, avg(value) FROM metrics WHERE metric='cpu' AND ts > NOW()-INTERVAL '7 days' GROUP BY hour ORDER BY hour DESC;
Build a distributed cacheConsistent hashing across N Redis nodes — same key always routes to same node. Virtual nodes (150/server) for even distribution. Cache-aside: app checks cache, miss → DB fetch → cache write. Write-through for strong consistency. Eviction: LRU. Monitor hit rate (target >90%). Warm cache on deploy.
Real world: Facebook Memcached: consistent hashing across 1000s of nodes, 99%+ hit rate · Netflix EVCache: multi-region Redis with write-through for session and metadata caching
  • -- Cache-aside pattern value = cache.get(key) if value is None: value = db.query(sql) cache.setex(key, TTL, value)
  • redis-cli --cluster create node1:6379 node2:6379 node3:6379 --cluster-replicas 1
  • redis-cli INFO stats | grep keyspace_hits redis-cli INFO stats | grep keyspace_misses # hit_rate = hits / (hits + misses)
Track millions of concurrent users onlineHeartbeat every 30s from client → presence service → Redis SETEX uid:online 1 35 (TTL slightly > heartbeat interval). WebSocket ping/pong as alternative. For friend presence: SMEMBERS friends:{uid} then batch MGET. Update on disconnect event. Approximate with Bloom filter for massive scale.
Real world: WhatsApp: heartbeat every 30s, last_seen stored in Redis · Slack: WebSocket ping/pong, server marks idle after 5min no activity · Discord: gateway heartbeat_interval per shard
  • SET online:{uid} 1 EX 35 -- heartbeat sets TTL EXISTS online:{uid} -- check if online
  • -- Batch presence check for friend list MGET online:{uid1} online:{uid2} online:{uid3} -- nil = offline, '1' = online
  • -- WebSocket heartbeat (client) setInterval(() => ws.send(JSON.stringify({type:'ping'})), 30000) // Server: reset TTL on ping receive
Implement distributed lockingRedis Redlock: SET key uuid NX PX 30000 (SET if not exists, expire 30s). Lock owner = UUID (prevents other process unlocking). Release: Lua script checks UUID then DEL (atomic). For stronger guarantees: ZooKeeper ephemeral node or etcd lease. Never use lock for >10s operations.
Real world: Redisson (Java): Redlock across 5 Redis nodes for quorum · Airbnb: Redis NX lock for inventory reservation · Kubernetes: etcd-based leader election for controller manager
  • -- Acquire: SET if Not eXists with PX expiry SET lock:{resource} {uuid} NX PX 30000
  • -- Release: Lua atomic check-then-delete if redis.call('GET',KEYS[1])==ARGV[1] then return redis.call('DEL',KEYS[1]) else return 0 end
  • -- ZooKeeper: ephemeral sequential node for lock zkCli.sh create -e -s /locks/resource- '' # Lowest sequence number = lock holder
Search across billions of documentsElasticsearch: inverted index on tokenized fields. Shards = horizontal partitions (each shard is a Lucene index). Replicas for HA + read scale. CDC pipeline from DB → Kafka → ES indexer keeps in sync (1-5s lag). Use filter context for structured fields (fast, cached). Query context for full-text (scored).
Real world: GitHub code search: custom Elasticsearch on 200M+ repos · Stack Overflow: Elasticsearch for question search · Airbnb: ES for listing search with geo_distance + text
  • PUT /listings/_search {"query":{"bool":{ "filter":[{"geo_distance":{"distance":"5km","location":{"lat":37.7,"lon":-122.4}}}], "must":[{"match":{"title":"coffee shop"}}] }}}
  • -- CDC sync: Debezium → Kafka → ES debezium.connector.postgresql.config: slot.name: es_sync publication.name: es_publication
  • PUT /listings/_settings {"index.number_of_shards":5, "index.number_of_replicas":1}
Handle large file uploads reliablyMultipart upload: client splits file into 5–100MB chunks, uploads each chunk independently to S3. Server issues pre-signed URL per part. Client uploads directly to S3 (bypasses your servers). After all parts complete, call CompleteMultipartUpload. Resumable: track uploaded parts in Redis. Show progress from part ETags.
Real world: YouTube: TUS protocol for resumable upload, splits to S3 parts · Dropbox: block-level chunking with SHA-256 dedup, direct-to-S3 upload · Google Drive: resumable upload session URI
  • aws s3api create-multipart-upload --bucket b --key video.mp4 # Returns UploadId
  • aws s3api upload-part --bucket b --key video.mp4 --part-number 1 --upload-id {id} --body chunk1.bin
  • aws s3api complete-multipart-upload --bucket b --key video.mp4 --upload-id {id} --multipart-upload '{"Parts":[{"PartNumber":1,"ETag":"..."}]}'
Sync data across devicesEvent sourcing: every change is an immutable event with a Lamport timestamp (or vector clock for multi-master). Sync = exchange events since last_sync_ts. Conflict resolution: last-write-wins by timestamp, or operational transform for collaborative editing. Store delta log in Cassandra. Client applies events on reconnect.
Real world: Dropbox: block-level delta sync, server is source of truth · Apple iCloud: CRDTs for conflict-free sync · Notion: operational transform for real-time collaboration · Figma: CRDT for multiplayer
  • -- Fetch delta since last sync SELECT * FROM events WHERE device_id != ? AND ts > ? ORDER BY ts ASC LIMIT 500
  • -- Apply event idempotently (upsert) INSERT INTO docs (id, content, ts) VALUES (?, ?, ?) ON CONFLICT (id) DO UPDATE SET content=EXCLUDED.content WHERE docs.ts < EXCLUDED.ts
  • -- Vector clock comparison # A dominates B if A[i] >= B[i] for all i # Conflict if neither dominates the other → merge needed
Build a payment processing systemIdempotency key (UUID) per request stored in Redis (NX, 24h TTL) prevents double-charge. Double-entry ledger: every transfer = debit + credit in one atomic transaction. Saga pattern for multi-step: charge card → update balance → notify. Async PSP webhook for confirmation. Nightly reconciliation vs PSP CSV.
Real world: Stripe: idempotency_key on every API call, double-entry ledger · Venmo: Saga for peer transfers, Redis idempotency gate · Coinbase: immutable ledger with daily reconciliation
  • SET idem:{key} 1 NX EX 86400 # 0 = duplicate request, skip processing
  • BEGIN; INSERT INTO ledger (user_id, amount, type) VALUES (?, -50, 'DEBIT'); INSERT INTO ledger (user_id, amount, type) VALUES (?, +50, 'CREDIT'); COMMIT;
  • -- Balance = ledger sum, never stored as column SELECT SUM(amount) FROM ledger WHERE user_id = ?
Design a job scheduling systemPriority queue (Redis sorted set: score = next_run_at epoch). Scheduler polls with ZRANGEBYSCORE ts 0 now LIMIT 10, atomically moves to processing set. Worker claims job via SETNX job:{id} worker_id. Heartbeat while processing. Timeout detection by separate monitor. Dead-letter queue for failed jobs after N retries.
Real world: Sidekiq (Ruby): Redis sorted set for scheduled jobs, workers BRPOP from queues · Airflow: DAG scheduler with Postgres for job state · AWS EventBridge: cron-style scheduling at cloud scale
  • -- Enqueue: score = Unix timestamp to run at ZADD scheduled_jobs {run_at_ts} {job_id}
  • -- Poll due jobs atomically ZRANGEBYSCORE scheduled_jobs 0 {now} LIMIT 0 10 -- Move to processing: ZREM + SADD atomically via Lua
  • -- Worker heartbeat while processing SET job:{id}:lock {worker_id} EX 30 # Monitor: scan for locks not refreshed in 60s → requeue
Implement a content delivery networkGeoDNS routes users to nearest PoP. Edge server checks local cache → origin fetch on miss. Cache key = URL + Accept-Encoding + Vary headers. TTL from Cache-Control. Purge via API (tag-based or URL). Origin shield: one intermediate layer absorbs stampede. Push model for known large assets (video segments). 99%+ cache hit ratio target.
Real world: Cloudflare: 300+ PoPs, Anycast routing, cache at edge for static · Akamai: origin shield to protect origin from stampede · Netflix Open Connect: ISP-embedded appliances with pre-populated content
  • # Cache-Control headers from origin Cache-Control: public, max-age=31536000, immutable # static assets Cache-Control: public, max-age=300, stale-while-revalidate=60 # API
  • aws cloudfront create-invalidation --distribution-id DIST_ID --paths '/videos/*'
  • # Verify cache hit at edge curl -I https://cdn.example.com/img.jpg # x-cache: Hit from cloudfront # x-amz-cf-pop: SEA19-P1
Build a recommendation engineCollaborative filtering: user-item matrix factorization (ALS). Two-stage: (1) Candidate retrieval — find 100-1000 candidates via approximate nearest neighbor (ANN) on user embedding. (2) Ranking — DNN re-ranks candidates with user context. Pre-compute embeddings offline (Spark), serve via FAISS or Pinecone. Update embeddings nightly.
Real world: Netflix: two-stage retrieval+ranking, embeddings updated daily · Spotify: ALS matrix factorization for Discover Weekly · Amazon: item-to-item collaborative filtering for 'Customers also bought'
  • -- FAISS approximate nearest neighbor search import faiss index = faiss.IndexFlatIP(128) # 128-dim embeddings index.add(item_embeddings) # offline build D, I = index.search(user_embedding, 100) # top-100
  • -- Spark ALS for matrix factorization als = ALS(maxIter=10, regParam=0.01, rank=50) model = als.fit(ratings_df) recs = model.recommendForAllUsers(100)
  • SELECT item_id, score FROM recommendations WHERE user_id = ? ORDER BY score DESC LIMIT 20
Design a URL shortenerBase62 encode a 7-digit counter (62^7 = 3.5T URLs) from a distributed ID generator (Snowflake or Redis INCR). Store short→long in Redis (hot) + Cassandra (durable). Redirect: 301 (cached by browser, saves future requests) vs 302 (tracked every time). Analytics: async Kafka click event. Custom aliases: check uniqueness before insert.
Real world: bit.ly: base62 encoding, Redis for hot redirects, MySQL for persistence · TinyURL: sequential counter in DB with Redis cache layer · Twitter t.co: 302 redirect to track all clicks
  • -- Generate short code: base62 encode atomic counter INCR url:counter -- returns e.g. 1000001 # base62(1000001) = 'ezV'
  • SET url:{code} {long_url} EX 86400 -- Redis with TTL -- Fallback: SELECT long_url FROM urls WHERE code=?
  • -- Redirect response HTTP/1.1 302 Found Location: https://original-url.com/path # 301 = permanent (browser caches, no analytics) # 302 = temporary (every redirect tracked)
Handle webhook delivery reliablyStore webhook payload in DB with status=PENDING. Async worker delivers via HTTP POST to customer endpoint. On 2xx: mark DELIVERED. On failure: exponential backoff (1s, 2s, 4s, ... up to 24h). After N retries: mark FAILED, alert customer. Signature HMAC-SHA256 in header for verification. Idempotency key in payload.
Real world: Stripe: webhook retry for 72h with exponential backoff · GitHub: webhook delivery log with redeliver button · Shopify: HMAC-SHA256 signature in X-Shopify-Hmac-SHA256 header
  • -- Exponential backoff retry schedule retry_at = now + (2 ** attempt) seconds UPDATE webhooks SET retry_at=?, attempt=attempt+1 WHERE id=?
  • -- HMAC signature for verification import hmac, hashlib sig = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest() # Header: X-Signature: sha256={sig}
  • -- Customer verifies signature expected = hmac.new(secret, request.body, sha256).hexdigest() assert hmac.compare_digest(expected, received_sig)
Design a comments / activity feed systemComments stored in Cassandra: partition by (entity_id, comment_id timeuuid). Fetch latest 20 per entity in O(1). For nested threads: adjacency list (parent_id column) or closure table for deep nesting. Real-time updates: SSE or WebSocket push via Redis pub/sub on entity channel. Reaction counts: Redis INCR per (entity_id, reaction_type).
Real world: Reddit: Postgres adjacency list for comment trees, Redis for vote counts · YouTube: Cassandra for top-level comments, Postgres for replies · Facebook: HBase for activity feed
  • -- Cassandra: partition by entity, cluster by time CREATE TABLE comments ( entity_id uuid, comment_id timeuuid, body text, user_id uuid, PRIMARY KEY (entity_id, comment_id) ) WITH CLUSTERING ORDER BY (comment_id DESC);
  • SELECT * FROM comments WHERE entity_id=? ORDER BY comment_id DESC LIMIT 20
  • -- Reaction count: atomic increment INCR reactions:{entity_id}:like GET reactions:{entity_id}:like
Build a real-time collaborative editorOperational Transform (OT) for convergence: every edit is an operation (insert/delete at position). Server serializes concurrent ops via a sequencer. Each op transformed against concurrent ops before applying. Alternative: CRDT (no central sequencer, conflict-free by design). WebSocket for real-time sync. Snapshot every 100 ops. ShareDB or Yjs for production use.
Real world: Google Docs: OT with central sequencer · Figma: CRDT for multiplayer design · Notion: CRDT for offline-first sync · VS Code Live Share: OT-based co-editing
  • -- OT: transform insert against concurrent delete # Op A: insert('X', pos=5) # Op B: delete(pos=3, len=2) — concurrent # A' = transform(A, B) = insert('X', pos=3) # adjusted
  • -- ShareDB (Node.js OT library) const doc = connection.get('articles', 'article-1') doc.subscribe() doc.submitOp([{p:['content',5], li:'X'}]) # insert at pos 5
  • -- CRDT: Y.js const ydoc = new Y.Doc() const ytext = ydoc.getText('content') ytext.insert(0, 'Hello') # always converges
Design a fraud detection systemReal-time scoring: Flink stream on transaction events, compute features in 1-min windows (velocity, geo-distance, device fingerprint, merchant category). Lightweight ML model (gradient boosting) scores in <50ms. Score >threshold: block + flag for review. Feedback loop: confirmed fraud retrains model weekly. Rules engine for hard blocks (known bad IPs, sanctioned countries).
Real world: Stripe Radar: ML + rules engine on every payment, 99.9% accuracy · PayPal: Flink real-time feature computation + GBM model · Uber: real-time geolocation anomaly detection for account takeover
  • -- Flink: velocity feature in 1-min window stream.keyBy('user_id') .window(TumblingProcessingTimeWindows.of(Time.minutes(1))) .aggregate(new VelocityAggregator())
  • -- Rules engine: hard block patterns if tx.amount > 10000 and tx.country in SANCTIONED: block() if velocity_1min > 50: flag_for_review()
  • -- Feature store: retrieve user features in real-time SELECT avg_tx_amount_30d, tx_count_1h, last_country FROM user_features WHERE user_id = ?
Store user sessions at scaleStateless tokens (JWT): session data encoded in signed token, no server state needed. Verify signature on every request. Refresh token (longer TTL) rotates access token. Revocation: maintain Redis blocklist of invalidated JTIs (token IDs). Alternatively: opaque session token → Redis HSET session:{id} with user data + TTL. Scale: Redis cluster, sticky sessions for WebSocket.
Real world: Auth0: JWT access tokens (15min TTL) + refresh tokens (30d) · Google: opaque session tokens in distributed Memcached · GitHub: personal access tokens hashed in DB, Redis session cache
  • -- JWT: sign and verify import jwt token = jwt.encode({'sub': user_id, 'exp': now+900}, SECRET, 'HS256') payload = jwt.decode(token, SECRET, ['HS256']) # verify + decode
  • -- Redis session store HSET session:{id} user_id 42 role admin last_seen {ts} EXPIRE session:{id} 3600
  • -- Revocation blocklist SET blocklist:{jti} 1 EX {token_ttl} # On each request: EXISTS blocklist:{jti}
Design an image/video processing pipelineUpload: client → S3 pre-signed URL (bypasses your servers). S3 event → SQS queue → transcoding workers (EC2 Spot or Lambda for cost). DAG of processing steps: thumbnail generation → format transcoding → CDN upload → DB metadata update. Store job state in DynamoDB. Idempotent workers: re-process if job state ≠ DONE. Serve via CloudFront.
Real world: YouTube: TUS upload → parallel GPU transcode farm → 1200+ quality variants → CDN · Instagram: Lambda for thumbnail resize on S3 event · Netflix: Cosmos microservices DAG for encoding
  • -- S3 event trigger to SQS aws s3api put-bucket-notification-configuration --bucket uploads --notification-configuration '{"QueueConfigurations":[{"Events":["s3:ObjectCreated:*"],"QueueArn":"arn:aws:sqs:..."}]}'
  • # FFmpeg: transcode to HLS ffmpeg -i input.mp4 -codec copy -start_number 0 -hls_time 6 -hls_list_size 0 -f hls output.m3u8
  • # Generate thumbnail at 10% into video ffmpeg -i input.mp4 -ss 00:00:$(duration*0.1) -vframes 1 -vf scale=320:-1 thumbnail.jpg
Build a multi-tenant SaaS platformThree isolation models: (1) Silo — separate DB per tenant (max isolation, high cost). (2) Bridge — shared DB, separate schema per tenant. (3) Pool — shared everything, tenant_id column with row-level security. Use Pool for most tenants, Silo for enterprise. Route at API gateway: extract tenant from subdomain/JWT claim → connection pool per tenant.
Real world: Salesforce: shared Oracle DB + row-level security (Force.com) · Shopify: shared MySQL sharded by shop_id · Stripe: separate Postgres schemas per isolation tier
  • -- Row-level security (PostgreSQL) CREATE POLICY tenant_isolation ON orders USING (tenant_id = current_setting('app.tenant_id')::int); ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
  • -- Set tenant context at connection time SET app.tenant_id = '42'; -- all queries auto-filtered
  • -- API gateway: extract tenant tenant = jwt_payload['org_id'] # or subdomain connection = pool.get_connection(tenant_id=tenant)
Handle cascading failures / circuit breakerCircuit breaker states: CLOSED (normal), OPEN (fast-fail, no calls to downstream), HALF-OPEN (probe 1 request to test recovery). Trip to OPEN when error rate >50% in a 10s window. Auto-recover after 30s timeout. Bulkhead: separate thread pools per downstream service so one failure can't exhaust all threads. Fallback: return cached stale data or default response.
Real world: Netflix Hystrix: circuit breaker + bulkhead for all inter-service calls · Resilience4j (Java): circuit breaker in Spring Boot microservices · AWS SDK: built-in retry + exponential backoff
  • # Resilience4j circuit breaker (Java) CircuitBreaker cb = CircuitBreaker.ofDefaults('inventory'); Supplier<Inventory> supplier = CircuitBreaker.decorateSupplier(cb, inventoryService::get); Try.ofSupplier(supplier).recover(ex -> cachedInventory);
  • # Track error rate in sliding window INCR errors:{service}:{window} INCR calls:{service}:{window} # error_rate = errors/calls; if > 0.5: open circuit
  • # Exponential backoff with jitter import random, time for attempt in range(max_retries): delay = min(2**attempt + random.uniform(0,1), 60) time.sleep(delay)
Design an audit log / event sourcing systemImmutable append-only log: every state change = event with (event_id, aggregate_id, event_type, payload, actor, timestamp). Store in Kafka (durable, replayable) + Cassandra (queryable). Current state = replay of all events or snapshot + delta. Never update/delete events. Query: by aggregate_id (who/what changed) or by actor (who did it). Retain 7 years for compliance.
Real world: Stripe: every API call appended to immutable event log · AWS CloudTrail: every API action logged · GitHub: all git operations append-only · Axon Framework: CQRS + event sourcing in Java
  • -- Append-only event log (Cassandra) CREATE TABLE events ( aggregate_id uuid, event_id timeuuid, event_type text, actor_id uuid, payload text, PRIMARY KEY (aggregate_id, event_id) );
  • -- Replay events to rebuild state SELECT * FROM events WHERE aggregate_id=? ORDER BY event_id ASC
  • -- Kafka: produce immutable event producer.send(ProducerRecord('audit-log', aggregate_id, json.dumps({'type':'ORDER_PLACED','actor':uid,'ts':now})))
Implement cursor-based paginationNever use OFFSET (scans all preceding rows, O(N)). Use a cursor = encoded last-seen value (e.g. base64 of {id, created_at}). Query: WHERE (created_at, id) < (cursor_ts, cursor_id) ORDER BY created_at DESC, id DESC LIMIT N+1. Return N items + has_more flag. Stable under inserts (unlike OFFSET). Works with any sort order.
Real world: Stripe API: 'starting_after' object ID cursor on all list endpoints · Twitter: since_id/max_id on timeline · GitHub: Link header with next page cursor · Instagram Graph API: after cursor param
  • -- Keyset pagination: composite cursor SELECT * FROM posts WHERE (created_at, id) < ({cursor_ts}, {cursor_id}) ORDER BY created_at DESC, id DESC LIMIT 21 -- fetch 21, return 20 + has_more flag
  • -- Encode cursor (base64) import base64, json cursor = base64.b64encode(json.dumps({'ts': row.ts, 'id': row.id}).encode()).decode()
  • -- API response with next cursor {"data": [...], "next_cursor": "eyJ0cyI6...", "has_more": true}
Design a config management systemCentral config store (etcd or Consul) with watch API — services subscribe to their config namespace. On change: push to watchers within 1s. Config versioned (immutable: config/{service}/{version}/{key}). Rollback = point to previous version. Local cache with TTL as fallback if config service is down. Feature flags: A/B testing by user cohort via config values.
Real world: LaunchDarkly: feature flag service, SDK caches flags locally · etcd: Kubernetes cluster config and leader election · Netflix Archaius: dynamic properties refreshed from config service every 60s
  • # etcd: watch for config changes etcdctl watch /config/payment-service/ --prefix # Triggers on any key change under prefix
  • etcdctl put /config/payment-service/max_retry 3 etcdctl get /config/payment-service/max_retry
  • # ZooKeeper: watch a config node watcher = lambda event: reload_config() zk.get('/config/feature-flags', watch=watcher)
Build a data pipeline / ETL systemSource → Kafka ingest → stream processor (Flink for real-time, Spark for batch) → sink (data warehouse, S3, Elasticsearch). Idempotent sinks: UPSERT or dedup by event_id. Schema registry (Avro + Confluent) for backward-compatible evolution. Dead-letter queue for malformed records. Monitor: lag per Kafka consumer group. Reprocess by resetting consumer offset.
Real world: Airbnb: Kafka → Spark → Hive data warehouse · LinkedIn: Kafka → Samza → Elasticsearch + HDFS · Uber: Flink for real-time trip analytics → ClickHouse
  • # Reset Kafka consumer offset for replay kafka-consumer-groups --bootstrap-server broker:9092 --group etl-group --reset-offsets --to-datetime 2026-01-01T00:00:00 --execute --topic events
  • # Monitor consumer lag kafka-consumer-groups --bootstrap-server broker:9092 --describe --group etl-group # LAG column = messages behind latest offset
  • -- Flink: exactly-once sink with upsert env.setStateBackend(new EmbeddedRocksDBStateBackend()) env.getCheckpointConfig().setCheckpointingMode(EXACTLY_ONCE)
AWSAmazon Web Services — Key Services
Core AWS services mapped to system design patterns, with pros/cons, cost tiers, and CLI commands.
ServiceCategoryUse In SystemsPros / ConsCostKey CommandsJava Single-Server Equiv.
EC2ComputeWeb servers, app servers, any stateless compute tier
e.g. Scale From Zero (web tier), YouTube (transcode workers), Stock Exchange (matching engine)
Full OS control, widest instance variety, spot instances for 90% cost savings on batch
Must manage OS patching, slower to scale than Lambda/ECS
On-demand ~$0.10/hr (t3.medium), Spot ~$0.03/hr, Reserved 1yr saves 40%
  • aws ec2 run-instances --image-id ami-xxx --instance-type t3.mediumLaunch EC2 instance; add --user-data for bootstrap script on startup
  • aws ec2 describe-instances --filters Name=instance-state-name,Values=runningList running instances; use --query to extract specific fields
  • aws ec2 terminate-instances --instance-ids i-xxxxxxxxxPermanently delete instance; data on instance store is lost
JVM Process on bare metal
// EC2 = your JVM process on a physical/virtual machine Runtime rt = Runtime.getRuntime(); int cpus = rt.availableProcessors(); // instance vCPUs long ram = rt.maxMemory(); // -Xmx heap // Scale: add more threads, not more machines
EC2 gives you the machine. Your JVM is the tenant. Vertical scale = bigger instance (-Xmx, more vCPUs).
S3Object StorageBlob storage for any large files, static assets, data lake
e.g. YouTube (raw video + HLS segments), Google Drive (file blocks), Notification (attachments)
11-nines durability, unlimited scale, CDN-ready, lifecycle tiering to Glacier
Not a filesystem, eventual consistency for overwrite-then-read (now strong by default)
Standard $0.023/GB/mo · Glacier $0.004/GB/mo · GET $0.0004/1K requests
  • aws s3 cp file.mp4 s3://bucket/keyUpload file; add --multipart-threshold 100MB for large files
  • aws s3 presign s3://bucket/key --expires-in 3600Generate a temporary URL valid for N seconds; no auth needed to access
  • aws s3api put-object-acl --bucket b --key k --acl public-readSet ACL after upload; public-read enables CloudFront/direct browser access
java.io.File / NIO Path
// S3 = local filesystem Path file = Path.of("/data/uploads/video.mp4"); Files.write(file, bytes); // PUT object byte[] data = Files.readAllBytes(file); // GET object String url = file.toUri().toString(); // no presign needed
S3 is a networked filesystem. Locally: Files.write/readAllBytes. Durability = RAID. CDN = Nginx serving /static.
RDS / AuroraRelational DBACID transactions, user data, financial records, any relational schema
e.g. URL Shortener (short→long), Hotel Reservation (inventory + bookings), Payment (ledger)
Managed MySQL/Postgres, Multi-AZ HA, Aurora 5× faster than MySQL, auto-scaling storage
Max ~32K IOPS, vertical scaling limited, expensive at large scale vs self-managed
db.t3.medium ~$0.068/hr · Aurora Serverless v2 ~$0.12/ACU-hr · storage $0.10/GB/mo
  • aws rds create-db-instance --db-instance-identifier mydb --engine aurora-mysqlCreate managed DB; add --multi-az for automatic failover across AZs
  • aws rds create-db-snapshot --db-instance-identifier mydb --db-snapshot-identifier snap1On-demand backup; snapshot stored in S3, retained per retention policy
  • aws rds failover-db-cluster --db-cluster-identifier myclusterForce promote a read replica to primary; useful for testing HA
JDBC + HikariCP connection pool
HikariConfig cfg = new HikariConfig(); cfg.setJdbcUrl("jdbc:mysql://localhost/db"); cfg.setMaximumPoolSize(20); // RDS max_connections HikariDataSource ds = new HikariDataSource(cfg); try (Connection c = ds.getConnection(); PreparedStatement s = c.prepareStatement( "SELECT * FROM users WHERE id=?")) { s.setLong(1, userId); ResultSet rs = s.executeQuery(); }
RDS = managed MySQL/Postgres. Locally: same JDBC driver, HikariCP for pooling. Multi-AZ HA = JDBC failover URL.
ElastiCache (Redis)In-memory CacheSession store, rate limiter, leaderboard, pub/sub, feed cache
e.g. Rate Limiter (token bucket), News Feed (sorted sets), Nearby Friends (GEO), Chat (pub/sub)
Sub-ms latency, Redis data structures (sorted sets, streams), Cluster mode for horizontal scale
Data must fit in RAM, not durable by default (needs RDB+AOF), cluster adds complexity
cache.t3.micro ~$0.017/hr · cache.r6g.large ~$0.169/hr · data transfer extra
  • aws elasticache create-replication-group --replication-group-id my-redisCreate Redis cluster with primary + replicas; specify --num-cache-clusters
  • redis-cli -h endpoint SET key value EX 3600Direct Redis command; EX sets expiry in seconds (TTL)
  • redis-cli -h endpoint CLUSTER INFOShow Redis Cluster health: slot coverage, replication state, node count
ConcurrentHashMap + Caffeine cache
// ElastiCache Redis = in-process cache Cache<String, Object> cache = Caffeine.newBuilder() .maximumSize(10_000) // memory bound .expireAfterWrite(5, MINUTES) // TTL .build(); cache.put("session:123", sessionData); Object val = cache.getIfPresent("session:123"); // No ZADD/pub-sub — use PriorityQueue + AtomicLong
Redis sorted sets → TreeMap. Pub/sub → Disruptor or LinkedBlockingQueue. Caffeine for TTL-based caching.
DynamoDBNoSQL Key-ValueHigh-throughput key lookups, session store, shopping cart, serverless backends
e.g. Notification (device tokens), Ad Click (dedup state), Nearby Friends (location store)
Fully managed, single-digit ms at any scale, DAX cache layer, DynamoDB Streams for CDC
No joins, limited query flexibility, hot partitions, expensive at high RCU/WCU
On-demand: $1.25/million WRU · $0.25/million RRU · $0.25/GB/mo storage
  • aws dynamodb put-item --table-name T --item '{"pk":{"S":"x"},"sk":{"S":"y"}}'Write item to DynamoDB; condition expression prevents accidental overwrites
  • aws dynamodb query --table-name T --key-condition-expression 'pk = :v'Query partition; cheaper than Scan — reads only one partition's data
  • aws dynamodb update-item --table-name T --key '...' --update-expression 'SET #c = #c + :one'Atomic counter increment; ADD or SET expression — never read-modify-write
ConcurrentHashMap<String, Object>
// DynamoDB = distributed HashMap ConcurrentHashMap<String, Map<String,Object>> table = new ConcurrentHashMap<>(); // PUT item table.put(pk, Map.of("sk",sk, "data",val)); // GET item Map<String,Object> item = table.get(pk); // Conditional write (IF NOT EXISTS) table.putIfAbsent(pk, newItem); // No secondary index → maintain separate Map
DynamoDB partition key = HashMap key. Conditional writes = CAS via putIfAbsent / replace. GSI = second HashMap.
SQS / SNSMessagingAsync task queue, fan-out notifications, decoupling microservices
e.g. Notification System (channel fan-out), Payment (async PSP retry), Web Crawler (URL queue)
SQS: at-least-once delivery, unlimited throughput, FIFO option. SNS: fan-out to millions
SQS not replayable (message deleted on consume), max 14-day retention, no consumer groups
SQS: first 1M free, then $0.40/million · SNS: $0.50/million notifications
  • aws sqs send-message --queue-url URL --message-body '{"event":"click"}'Enqueue message; set --delay-seconds for scheduled delivery
  • aws sqs receive-message --queue-url URL --max-number-of-messages 10Poll for up to 10 messages; delete each after processing to prevent re-delivery
  • aws sns publish --topic-arn arn:aws:sns:... --message 'hello'Fan-out to all topic subscribers (SQS, Lambda, HTTP, email) simultaneously
LinkedBlockingQueue + ExecutorService
// SQS = BlockingQueue, SNS = pub/sub via listeners BlockingQueue<Task> queue = new LinkedBlockingQueue<>(1000); // max depth ExecutorService workers = Executors.newFixedThreadPool(10); // Producer queue.put(new Task(event)); // blocks if full // Consumer workers.submit(() -> { while(true) process(queue.take()); }); // SNS fan-out → CopyOnWriteArrayList<Consumer<Event>>
SQS backpressure = BlockingQueue.put() blocking. DLQ = catch block → failedQueue. SNS = Observer pattern.
LambdaServerless ComputeEvent-driven processing, API backends, image resizing, scheduled jobs
e.g. Ad Click (event processor), URL Shortener (redirect function), Metrics (alert evaluator)
Zero server management, auto-scale to zero, pay per invocation, 15-min max duration
Cold starts (100ms–3s), 15-min timeout, no persistent connections, limited memory (10GB)
First 1M invocations free, then $0.20/million · $0.00001667/GB-second compute
  • aws lambda invoke --function-name myFunc --payload '{"key":"val"}' out.jsonSynchronous invocation; output written to out.json; async: add --invocation-type Event
  • aws lambda create-function --function-name f --runtime nodejs20.x --handler index.handlerDeploy function; specify role with least-privilege IAM permissions
  • aws lambda update-function-code --function-name f --zip-file fileb://func.zipDeploy new code; use --publish to create a new numbered version
Runnable / CompletableFuture
// Lambda = stateless function, invoked on demand ExecutorService pool = Executors.newCachedThreadPool(); // Async invocation CompletableFuture.runAsync(() -> { processEvent(event); // your handler logic }, pool); // Sync invocation (RequestResponse) Future<Result> f = pool.submit(() -> handle(req)); Result result = f.get(15, MINUTES); // Lambda timeout
Lambda cold start = new thread spin-up. 15-min timeout = Future.get(15, MINUTES). Scale to zero = cached thread pool.
CloudFrontCDNStatic asset serving, video streaming, API acceleration, DDoS shield
e.g. YouTube (HLS segments), URL Shortener (redirect caching), Google Maps (tile serving)
450+ PoPs globally, integrates with S3/ALB, signed URLs for private content, WAF built-in
Cache invalidation takes ~5min and costs money, custom SSL extra, limited routing logic
First 10TB/mo $0.0085/GB (US) · HTTPS requests $0.0100/10K · invalidation $0.005/path
  • aws cloudfront create-invalidation --distribution-id ID --paths '/videos/*'Purge cached objects; path /* invalidates everything (costs $0.005/path)
  • aws cloudfront get-distribution --id IDInspect distribution config: origins, behaviors, cache policies
  • aws cloudfront sign --url https://d.cloudfront.net/k --key-pair-id ID --private-key pk.pem --date-less-than 2026-12-31Generate signed URL for private content; requires CloudFront key pair
Caffeine + static file servlet
// CloudFront = HTTP cache in front of origin Cache<String, byte[]> httpCache = Caffeine.newBuilder() .maximumSize(500) // cache N responses .expireAfterWrite(1, HOURS) // Cache-Control TTL .build(); // Reverse proxy pattern byte[] response = httpCache.get(requestPath, path -> fetchFromOrigin(path)); // miss → origin // Cache invalidation httpCache.invalidate("/videos/*");
CloudFront = Nginx/Varnish locally. Caffeine as reverse-proxy cache. TTL = Cache-Control max-age. Invalidation = cache.invalidate().
Kinesis / MSKEvent StreamingReal-time event ingestion, stream processing, log aggregation, CDC
e.g. Ad Click (click stream), Metrics Monitoring (metric ingest), Google Maps (GPS pipeline)
MSK = managed Kafka (full API compatibility). Kinesis: simpler, serverless, auto-scaling
Kinesis: 7-day retention max, 1MB/record limit. MSK: more ops than Kinesis
Kinesis: $0.015/shard-hr + $0.014/million PUT · MSK: $0.21/broker-hr (kafka.m5.large)
  • aws kinesis put-record --stream-name s --data $(echo '{"e":"click"}' | base64) --partition-key kPublish event; partition-key determines shard routing (same key = same shard)
  • aws kinesis get-records --shard-iterator ITERPull batch from shard; advance shard iterator for next call
  • aws kafka list-clustersList MSK clusters; use --cluster-arn-list to filter specific clusters
Disruptor / LinkedBlockingQueue (replayable)
// Kafka/Kinesis = append-only replayable log // LMAX Disruptor for high-throughput single-JVM RingBuffer<EventSlot> ring = Disruptor.createSingleProducer( EventSlot::new, 1024, SINGLE); // Or simple: BlockingQueue + persist to file Queue<Event> log = new LinkedBlockingQueue<>(); Files.writeString(logFile, json + "\n", APPEND); // durable replay // Consumer group = multiple threads, shared offset
Kafka partition = ordered queue per key. Consumer group offset = shared AtomicLong. Replay = re-read from file offset.
EKS / ECSContainer OrchestrationMicroservices, long-running services, batch workloads at scale
e.g. Scale From Zero (web tier), KV Store (Cassandra cluster), Chat (WebSocket servers)
EKS: full Kubernetes, huge ecosystem. ECS: simpler, tighter AWS integration, Fargate serverless
EKS: complex ops, expensive control plane ($0.10/hr). ECS: proprietary, less portable
EKS: $0.10/cluster-hr + EC2/Fargate nodes · ECS: free control plane, pay for EC2/Fargate only
  • aws eks update-kubeconfig --name cluster --region us-east-1Merge cluster credentials into ~/.kube/config for kubectl access
  • kubectl get pods -n productionList pods in namespace; add -w to watch for status changes
  • aws ecs update-service --cluster c --service s --desired-count 5Scale ECS service; ECS schedules new tasks and drains old ones gracefully
Thread pool + service registry
// ECS/EKS = process lifecycle manager // Locally: threads are your 'containers' ExecutorService svc = Executors.newFixedThreadPool(4); // Health check loop (liveness probe) ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate( () -> healthCheck(), 0, 30, SECONDS); // Service discovery = local Map<String, InetAddress> Map<String,String> registry = new ConcurrentHashMap<>(); registry.put("user-service", "localhost:8081");
ECS task = JVM thread. Kubernetes Pod = OS process. Health probe = scheduled health check. Service mesh = localhost routing.
GCPGoogle Cloud Platform — Key Services
Core GCP services mapped to system design patterns, with pros/cons, cost tiers, and CLI commands.
ServiceCategoryUse In SystemsPros / ConsCostKey CommandsJava Single-Server Equiv.
Compute EngineComputeGeneral-purpose VMs, GPU workloads, stateful services
e.g. Scale From Zero (web tier), YouTube (GPU transcode), Stock Exchange (low-latency)
Per-second billing, live migration (no downtime for host maintenance), strong GPU/TPU lineup
Smaller ecosystem than AWS EC2, fewer instance types, fewer regions
n2-standard-2 ~$0.097/hr · Preemptible ~70% discount · Committed use 1yr saves ~37%
  • gcloud compute instances create my-vm --machine-type n2-standard-2 --zone us-central1-aLaunch VM; add --preemptible for 70% discount on fault-tolerant workloads
  • gcloud compute instances list --filter='status:RUNNING'List VMs by state; --format=json for scripting
  • gcloud compute ssh my-vm --zone us-central1-aSSH via IAP tunnel; no public IP or firewall rule needed
JVM Process (same as EC2)
// Compute Engine = VM, same as EC2 // Your JVM is the only tenant Thread.currentThread().setPriority(Thread.MAX_PRIORITY); int cores = Runtime.getRuntime().availableProcessors(); // Preemptible VM = daemon thread (can be killed) Thread t = new Thread(task); t.setDaemon(true); // killed when JVM exits t.start();
Preemptible VM = daemon thread (terminated without notice). Live migration = JVM continues on new host, no restart.
Cloud Storage (GCS)Object StorageData lake, ML training data, video/image storage, CDN origin
e.g. YouTube (raw video), Google Drive (file blocks), Google Maps (tile storage)
Consistent performance, strong consistency (since 2021), excellent BigQuery integration
Egress costs add up, fewer storage classes than S3, less mature lifecycle management
Standard $0.020/GB/mo · Nearline $0.010/GB/mo · Coldline $0.004/GB/mo · ops $0.004/10K
  • gsutil cp file.mp4 gs://bucket/keyUpload to GCS; add -m flag for parallel multi-threaded transfers
  • gsutil signurl -d 1h private-key.json gs://bucket/keyGenerate signed URL valid for 1 hour using service account key
  • gsutil lifecycle set lifecycle.json gs://bucketApply lifecycle rules: transition to Nearline after 30d, delete after 365d
java.nio.file.Files (same as S3)
// GCS = durable networked filesystem Path uploads = Path.of("/data/uploads"); Files.createDirectories(uploads); Path obj = uploads.resolve("video.mp4"); Files.copy(inputStream, obj, StandardCopyOption.REPLACE_EXISTING); // List objects (like gsutil ls) try (Stream<Path> s = Files.list(uploads)) { s.forEach(System.out::println); }
GCS strong consistency since 2021 = Files.write then immediate Files.read is always safe. Lifecycle = scheduled Files.delete.
Cloud SQL / SpannerRelational DBSQL: regional ACID apps. Spanner: globally distributed strong consistency
e.g. Hotel Reservation (Cloud SQL), Payment (Spanner for global ledger), Ad Click (audit log)
Spanner: global strong consistency + 5 nines SLA, unique in industry. Cloud SQL: managed PG/MySQL
Spanner: expensive ($0.90/node-hr), complex schema design. Cloud SQL: max 96 vCPUs
Cloud SQL db-n1-standard-2 ~$0.095/hr · Spanner $0.90/node-hr + $0.30/million ops
  • gcloud sql connect my-instance --user=rootConnect via Cloud SQL Auth Proxy (automatic); encrypts traffic in transit
  • gcloud sql backups create --instance my-instanceTrigger on-demand backup; stored in Cloud Storage, retained per policy
  • gcloud spanner databases execute-sql my-db --sql='SELECT * FROM T LIMIT 10'Run SQL on Spanner; uses strong reads by default (routes to leader)
JDBC / JDBC + synchronized (Spanner)
// Cloud SQL = standard JDBC (same as RDS) // Spanner global consistency = synchronized writes public synchronized void transfer( long from, long to, BigDecimal amount) { // Spanner transaction = synchronized block // guaranteeing serializable execution debit(from, amount); credit(to, amount); } // Spanner TrueTime = System.currentTimeMillis() // + clock uncertainty window for ordering
Spanner serializable isolation = synchronized method. TrueTime = monotonic clock with bounded skew. No local equivalent truly matches.
Memorystore (Redis)In-memory CacheSession store, leaderboards, pub/sub, feed cache, rate limiting
e.g. Rate Limiter, News Feed (sorted sets), Nearby Friends (GEO), Chat (pub/sub routing)
Fully managed Redis, automatic failover, VPC-native, Redis Cluster support
No cross-region replication, limited to 300GB per instance, no Redis modules
Basic M1 (1GB) ~$0.049/hr · Standard (HA) doubles price · $0.049/GB/hr roughly
  • gcloud redis instances create my-redis --size=5 --region=us-central1Create Memorystore Redis; --size in GB, HA via --tier=STANDARD_HA
  • redis-cli -h IP -p 6379 SET key value EX 3600Direct Redis command against Memorystore; only accessible within VPC
  • gcloud redis instances describe my-redis --region=us-central1Show Redis instance details: host, port, state, memory usage
Caffeine cache (same as ElastiCache)
// Memorystore = managed Redis, same JVM pattern LoadingCache<String, String> cache = Caffeine.newBuilder() .maximumSize(50_000) .expireAfterWrite(Duration.ofMinutes(10)) .build(key -> loadFromDB(key)); // auto-load cache.get("user:42"); // hit or load cache.invalidate("user:42"); // evict on write
Memorystore = Redis, same as ElastiCache. LoadingCache auto-populates on miss = read-through cache pattern.
Firestore / BigtableNoSQLFirestore: mobile/web app data. Bigtable: wide-column, HBase-compatible, time-series
e.g. Nearby Friends (location history in Bigtable), Gmail (email index in Bigtable), Chat (messages)
Bigtable: petabyte scale, HBase API, ideal for time-series/analytics. Firestore: real-time sync
Bigtable: no SQL, schema design critical. Firestore: limited query, 1MB doc size, costly at scale
Bigtable: $0.65/node-hr + $0.026/GB/mo · Firestore: $0.06/100K reads, $0.18/100K writes
  • cbt -project p -instance i lsList Bigtable tables; cbt is the Cloud Bigtable CLI tool
  • cbt -project p -instance i read my-tableRead rows from Bigtable table; add --limit=10 to cap output
  • gcloud firestore import gs://bucket/export --collection-ids=usersRestore Firestore from GCS export; used for disaster recovery
TreeMap (Bigtable) / HashMap (Firestore)
// Bigtable = sorted key-value store TreeMap<String, Map<String,byte[]>> bigtable = new TreeMap<>(); // rows sorted by rowkey bigtable.put("user#001#2026", Map.of("cf:name", "Alice".getBytes())); // Range scan (Bigtable row range) bigtable.subMap("user#001", "user#002") .forEach((k,v) -> process(k,v)); // Firestore = nested ConcurrentHashMap Map<String,Object> doc = new HashMap<>(); doc.put("name", "Alice"); doc.put("age", 30);
Bigtable rowkey sort = TreeMap. Range scan = subMap(start, end). Column family = nested Map. Firestore = document store = nested HashMap.
Pub/SubMessagingEvent-driven architecture, async fan-out, stream ingestion, dead-letter queues
e.g. Notification System (channel fan-out), Metrics (ingest), Web Crawler (URL queue)
At-least-once delivery, 7-day retention, push+pull modes, exactly-once option, global
Messages not replayable after ack (unlike Kafka), no consumer groups, ordering needs keys
First 10GB/mo free · then $0.04/GB · snapshots $0.014/GB/mo
  • gcloud pubsub topics publish my-topic --message='{"event":"click"}'Publish single message to Pub/Sub topic; for bulk use publisher client lib
  • gcloud pubsub subscriptions pull my-sub --limit=10Pull up to 10 messages; must call ack separately to avoid re-delivery
  • gcloud pubsub subscriptions create my-sub --topic=my-topic --ack-deadline=60Create subscription with 60s ack deadline; increase for slow processors
LinkedBlockingQueue (same as SQS)
// GCP Pub/Sub = managed message queue // At-least-once = BlockingQueue + ack tracking BlockingQueue<Message> sub = new LinkedBlockingQueue<>(); Set<String> acked = ConcurrentHashMap.newKeySet(); // Process with manual ack Message msg = sub.take(); try { process(msg); acked.add(msg.id()); // acknowledge } catch (Exception e) { // not acked = redelivered (at-least-once) }
Pub/Sub ack deadline = visibility timeout. Unacked message redelivered = retry from queue.take(). Dead-letter = failedQueue.
Cloud RunServerless ContainersStateless APIs, event processors, ML inference, background jobs
e.g. URL Shortener (redirect service), Ad Click (event processor), Metrics (alert evaluator)
Deploy any container, scale to zero, concurrency model (1000 req/instance), HTTPS auto
Cold starts (up to 4s), no persistent local disk, max 60-min timeout, stateless only
First 2M requests/mo free · $0.40/million requests · $0.00002400/vCPU-second
  • gcloud run deploy my-svc --image gcr.io/proj/img --platform managed --region us-central1Deploy containerized service; --allow-unauthenticated for public HTTP
  • gcloud run services listList all Cloud Run services and their latest revision URLs
  • gcloud run revisions list --service my-svcShow all deployed revisions; use for traffic splitting and rollback
CompletableFuture / Virtual Threads (Java 21)
// Cloud Run = stateless HTTP handler, scales to 0 // Java 21 virtual threads = lightweight scale-to-zero try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { // Each HTTP request = one virtual thread // 1000 concurrent = 1000 virtual threads executor.submit(() -> handleRequest(req)); } // Cold start = new VirtualThread spin-up (~1ms) // vs Cloud Run cold start (100ms–4s)
Cloud Run concurrency model (1000 req/instance) = virtual thread per request. Scale to zero = no threads running between requests.
GKEKubernetesMicroservices, stateful workloads, ML training pipelines
e.g. Scale From Zero (web tier), KV Store (Cassandra on GKE), Chat (WebSocket fleet)
Google invented Kubernetes, Autopilot mode removes node management, Workload Identity
Autopilot less flexible than Standard, premium channel costs extra, complex networking
Zonal cluster free · Regional cluster $0.10/hr · plus node (Compute Engine) costs
  • gcloud container clusters get-credentials my-cluster --zone us-central1-aConfigure kubectl to access GKE cluster via kubeconfig
  • kubectl apply -f deployment.yamlApply Kubernetes manifest; idempotent — safe to run repeatedly
  • kubectl rollout status deployment/my-appWait for rolling update to complete; exits non-zero on failure
Thread pool + health check (same as EKS)
// GKE = Kubernetes, same JVM pattern as EKS/ECS // Pod = JVM process, Container = thread group ExecutorService podWorkers = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors()); // Readiness probe = flag checked by load balancer volatile boolean ready = false; // Liveness probe = heartbeat scheduler.scheduleAtFixedRate( () -> { if(!isAlive()) restart(); }, 0, 10, SECONDS);
GKE Autopilot removes node management = virtual threads remove thread management. Workload Identity = SecurityManager (conceptually).
BigQueryData WarehouseAnalytics on petabyte datasets, ad-hoc SQL, BI dashboards, ML training data
e.g. Ad Click (reporting), Metrics (long-term storage), Email (analytics), YouTube (view counts)
Serverless, petabyte-scale SQL, 0-node management, GIS functions, ML via BQML
Not for OLTP, query cost unpredictable (scans all data), streaming inserts expensive
Storage $0.020/GB/mo · Queries $5/TB scanned (first 1TB/mo free) · Streaming $0.010/200MB
  • bq query --use_legacy_sql=false 'SELECT * FROM proj.dataset.table LIMIT 10'Run standard SQL in BigQuery; --use_legacy_sql=false for modern syntax
  • bq load --source_format=NEWLINE_DELIMITED_JSON dataset.table gs://bucket/*.json schema.jsonBatch load JSON Lines file from GCS into BigQuery table
  • bq show --format=prettyjson proj:dataset.tableInspect table schema, partitioning, and row count
Java Stream + parallel collectors
// BigQuery = distributed SQL over large datasets // Locally: parallel stream over in-memory collection List<ClickEvent> events = loadAllEvents(); // fits in RAM? Map<String, Long> byAd = events .parallelStream() // multi-core .collect(Collectors.groupingBy( ClickEvent::adId, Collectors.counting())); // GROUP BY ad_id // For data > RAM: use Apache Flink/Spark locally
BigQuery = parallel columnar scan. Java parallel stream = same idea bounded by one machine RAM and cores. For > RAM: Flink/Spark.
Cloud CDNCDNStatic asset caching, video streaming, API response caching, DDoS mitigation
e.g. YouTube (HLS segment delivery), Google Maps (tile caching), URL Shortener
Tight integration with GCS/Cloud Run, anycast IP, signed URLs, cache invalidation API
Less mature than CloudFront/Fastly, fewer edge PoPs (~100 vs CloudFront's 450+)
Cache egress $0.008/GB (Americas) · Cache fill $0.010/GB · HTTPS requests $0.0075/10K
  • gcloud compute backend-services update my-backend --enable-cdn --globalEnable Cloud CDN on a backend service; applies to all its backends
  • gcloud compute url-maps invalidate-cdn-cache my-map --path '/videos/*' --globalPurge CDN cache for a path pattern; propagates in ~1-2 minutes
  • gcloud compute backend-services describe my-backend --globalInspect backend health checks, timeout, and CDN policy settings
Caffeine HTTP cache (same as CloudFront)
// Cloud CDN = HTTP reverse-proxy cache // Same local pattern as CloudFront Cache<String, ResponseEntity<byte[]>> cdnCache = Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(1, HOURS) .build(); ResponseEntity<byte[]> tile = cdnCache.get("/tiles/z6/x12/y34.mvt", key -> tileService.render(key)); // Invalidate on map data update cdnCache.invalidate("/tiles/z6/*");
CDN cache miss = origin fetch. Cloud CDN 100 PoPs = 100 Caffeine instances worldwide. Invalidation API = cache.invalidate().
AzureMicrosoft Azure — Key Services
Core Azure services mapped to system design patterns, with pros/cons, cost tiers, and CLI commands.
ServiceCategoryUse In SystemsPros / ConsCostKey CommandsJava Single-Server Equiv.
Virtual MachinesComputeGeneral-purpose VMs, Windows workloads, SQL Server, SAP, HPC
e.g. Scale From Zero (web tier), Stock Exchange (.NET matching engine), Email (Exchange compat)
Best Windows/SQL Server integration, hybrid cloud (Arc), Reserved Instances save 72%
Complex pricing, slower provisioning than GCP, less Linux-native feel
B2s ~$0.046/hr · D2s v5 ~$0.096/hr · Spot VMs up to 90% discount
  • az vm create -g myRG -n myVM --image UbuntuLTS --size Standard_B2sCreate VM; add --admin-username and --ssh-key-values for SSH access
  • az vm list -g myRG --output tableList all VMs in resource group in readable table format
  • az vm stop -g myRG -n myVM && az vm deallocate -g myRG -n myVMStop VM (still billed for disk); deallocate releases compute and stops billing
JVM Process (same as EC2/GCE)
// Azure VM = your JVM process on a machine // Windows VM = JVM on Windows OS String os = System.getProperty("os.name"); long maxHeap = Runtime.getRuntime().maxMemory(); // Reserved Instance = long-running daemon Thread daemon = new Thread(serverLoop); daemon.setDaemon(false); // non-daemon = keeps JVM alive daemon.start(); // Spot VM = preemptible (handle SIGTERM) Runtime.getRuntime().addShutdownHook( new Thread(this::gracefulShutdown));
Azure Spot VM = preemptible process; register shutdown hook for graceful drain. Windows affinity = best for .NET/SQL Server workloads.
Blob StorageObject StorageUnstructured data, backups, media files, data lake (ADLS Gen2)
e.g. YouTube (video blobs), Google Drive (file blocks), Email (MIME attachments)
ADLS Gen2 for big data analytics, lifecycle management, immutable storage for compliance
Complex access tiers, slower than S3/GCS in some benchmarks, ACL model more complex
Hot LRS $0.018/GB/mo · Cool $0.01/GB/mo · Archive $0.00099/GB/mo · ops $0.0004/10K
  • az storage blob upload -c container -f file.mp4 -n key --account-name acctUpload file to Blob Storage; -n sets the blob name (path in container)
  • az storage blob generate-sas -c c -n k --permissions r --expiry 2026-12-31Generate SAS token with read permission expiring at specified date
  • az storage blob list -c container --account-name acct --output tableList blobs in container; add --prefix to filter by path prefix
java.nio.file.Files (same as S3/GCS)
// Azure Blob = durable object store Path blobRoot = Path.of("/mnt/blob-storage"); // Hot tier = SSD-backed path Path hotBlob = blobRoot.resolve("hot/video.mp4"); Files.write(hotBlob, bytes); // Cool tier = HDD path (cheaper, slower) // Archive = tape (hours to retrieve) // ADLS Gen2 = hierarchical namespace Path dir = blobRoot.resolve("data/2026/01/"); Files.createDirectories(dir);
Blob tiers (Hot/Cool/Archive) = SSD vs HDD vs tape analogy. ADLS Gen2 = true directory hierarchy (unlike flat S3 key namespacing).
Azure SQL / Cosmos DBDatabaseAzure SQL: managed SQL Server. Cosmos DB: globally distributed multi-model NoSQL
e.g. Hotel Reservation (Azure SQL), Nearby Friends (Cosmos DB), Ad Click (Cosmos change feed)
Cosmos DB: 5 consistency models, multi-region writes, <10ms p99. Azure SQL: full T-SQL
Cosmos DB: expensive at scale, complex RU capacity planning. Azure SQL: Windows-centric
Azure SQL GP 2vCores ~$0.362/hr · Cosmos DB $0.016/RU/s-hr + $0.25/GB/mo
  • az sql db create -g myRG -s myServer -n myDB --service-objective S1Create Azure SQL database; --service-objective sets DTU/vCore tier
  • az cosmosdb database create --name acct --db-name dbCreate Cosmos DB database; containers within it store actual data
  • az cosmosdb sql container create -a acct -g rg -d db -n container --partition-key-path /pkCreate container with partition key; choose high-cardinality partition key
JDBC (SQL) / ConcurrentHashMap (Cosmos)
// Azure SQL = JDBC, same as RDS/Cloud SQL // Cosmos DB 5 consistency levels: // Strong → synchronized read // Bounded Staleness → volatile read // Session → ThreadLocal<Version> // Consistent Prefix → AtomicReference // Eventual → plain field read volatile long version = 0; // session consistency AtomicReference<State> state = // consistent prefix new AtomicReference<>();
Cosmos 5 consistency levels map to Java memory model guarantees: synchronized > volatile > AtomicReference > plain read.
Azure Cache for RedisIn-memory CacheSession store, distributed cache, leaderboard, pub/sub, rate limiting
e.g. Rate Limiter (token bucket), News Feed (sorted sets), Chat (pub/sub routing)
Managed Redis, geo-replication, Enterprise tier with Redis modules (Search, JSON, TimeSeries)
Enterprise tier very expensive, no Redis Cluster in Basic/Standard, limited to 730GB
C1 Basic (1GB) ~$0.055/hr · C2 Standard (6GB) ~$0.201/hr · P1 Premium ~$0.494/hr
  • az redis create -g myRG -n my-redis -l eastus --sku Standard --vm-size c1Create Azure Cache for Redis; Standard tier = primary + replica for HA
  • redis-cli -h my-redis.redis.cache.windows.net -p 6380 -a KEY --tlsConnect with TLS on port 6380; -a provides the access key
  • az redis firewall-rules create -g myRG -n my-redis --rule-name allow --start-ip 0.0.0.0 --end-ip 255.255.255.255Open Redis to specific IP range; restrict to VNet CIDR in production
Caffeine cache (same as ElastiCache)
// Azure Cache for Redis = managed Redis // Enterprise tier adds Redis modules: // RediSearch → Lucene in-process // RedisJSON → Jackson ObjectMapper cache // RedisTimeSeries → RRD4J / Micrometer Cache<String, SearchIndex> searchCache = Caffeine.newBuilder() .maximumSize(5_000) .build(); // Redis ZADD for rate limiting AtomicInteger reqCount = new AtomicInteger(); if (reqCount.incrementAndGet() > LIMIT) reject();
Enterprise tier Redis modules: RediSearch = embedded Lucene. RedisTimeSeries = RRD4J or Micrometer. Rate limit = AtomicInteger + reset timer.
Service Bus / Event HubsMessagingService Bus: enterprise messaging, queues, topics. Event Hubs: Kafka-compatible streaming
e.g. Notification System (Service Bus fan-out), Ad Click (Event Hubs stream), Metrics (ingest)
Event Hubs: Kafka protocol support (zero code change), dead-letter queues, AMQP support
Event Hubs: 90-day max retention (vs Kafka unlimited), no consumer group offset management
Service Bus Standard $0.10/million ops · Event Hubs Basic $0.028/million events
  • az servicebus queue send -g myRG --namespace-name ns --queue-name q --body 'hello'Send message to Service Bus queue; use SDK for batch sends
  • az eventhubs eventhub create -g myRG --namespace-name ns -n hub --partition-count 4Create Event Hub with 4 partitions = max 4 concurrent consumers
  • az eventhubs eventhub consumer-group create -g rg --namespace-name ns --eventhub-name hub -n cgCreate consumer group; each group reads independently from offset 0
LinkedBlockingQueue (SB) / Disruptor (EH)
// Service Bus = durable queue with sessions BlockingQueue<Message> serviceBus = new PriorityBlockingQueue<>(); // ordered delivery // Sessions = per-key ordering guarantee Map<String, BlockingQueue<Message>> sessions = new ConcurrentHashMap<>(); // Event Hubs = Kafka-compatible, use Disruptor RingBuffer<Event> eventHub = Disruptor.createMultiProducer( Event::new, 4096, MULTI); // Consumer group = independent ring buffer cursor
Service Bus sessions = per-key FIFO queue. Event Hubs partition = Disruptor RingBuffer with independent consumer cursors.
Azure FunctionsServerlessEvent-driven code, HTTP APIs, timer jobs, blob triggers, queue processing
e.g. Ad Click (event processor), Notification (channel worker), URL Shortener (redirect)
Durable Functions for stateful workflows (fan-out/fan-in), tight Azure integration, C#/.NET native
Cold starts in Consumption plan, 5-min default timeout (extend to 10min), .NET bias
First 1M executions free · $0.20/million executions · $0.000016/GB-second
  • func init myFuncApp --dotnet && func new --name HttpTrigger --template 'HTTP trigger'Scaffold Azure Functions project with .NET runtime
  • az functionapp create -g myRG -n myFunc --storage-account sa --consumption-plan-location eastusDeploy Function App on Consumption plan; auto-scales to zero
  • func azure functionapp publish myFuncDeploy local function code to Azure; use --publish-local-settings for config
Virtual Threads / CompletableFuture
// Azure Functions = event-driven, scales to zero // Durable Functions = stateful workflow // Locally: CompletableFuture chaining CompletableFuture .supplyAsync(() -> readInput(event)) // Activity 1 .thenApplyAsync(data -> process(data)) // Activity 2 .thenAcceptAsync(result -> store(result)) // Activity 3 .exceptionally(ex -> { compensate(event); return null; // saga rollback });
Durable Functions fan-out/fan-in = CompletableFuture.allOf(). Saga compensation = exceptionally() rollback. Timer trigger = ScheduledExecutorService.
AKSKubernetesContainerized microservices, stateful apps, ML workloads at scale
e.g. Scale From Zero (web tier), Chat (WebSocket fleet), KV Store (Cassandra on AKS)
Free control plane, Windows node pool support, Azure AD integration, Dapr support
Slower cluster provisioning, complex networking (CNI choices), AAD setup friction
Control plane free · Pay for VM nodes · System node pool min ~$0.096/hr (D2s v3)
  • az aks create -g myRG -n myAKS --node-count 3 --node-vm-size Standard_D2s_v3Create AKS cluster; add --enable-managed-identity for workload identity
  • az aks get-credentials -g myRG -n myAKSMerge AKS credentials into kubeconfig; use --admin for cluster-admin access
  • kubectl apply -f deployment.yaml && kubectl rollout status deploy/my-appApply Kubernetes manifest; idempotent — safe to run repeatedly
Thread pool + health check (same as EKS/GKE)
// AKS = managed Kubernetes on Azure // Windows node pool = JVM on Windows ExecutorService windowsPool = Executors.newFixedThreadPool(4); // Dapr sidecar = in-process interceptor var interceptor = new DaprInterceptor(); interceptor.onInvoke((req, next) -> { log(req); return next.apply(req); // pass-through }); // AAD Workload Identity = SecurityManager
AKS Windows node = JVM on Windows (best for .NET). Dapr sidecar = AOP interceptor for state/pub-sub/service invocation abstraction.
Azure CDN / Front DoorCDN & EdgeCDN: static assets. Front Door: global load balancer + WAF + CDN in one
e.g. YouTube (HLS delivery), Google Maps (tiles), URL Shortener (redirect caching)
Front Door: global Anycast, intelligent routing, WAF, automatic failover between regions
Front Door Premium expensive, CDN PoP count lower than Cloudflare/Akamai, complex pricing
CDN Standard $0.0075/GB (Zone 1) · Front Door $0.01/GB data transfer + $35/mo base
  • az cdn endpoint purge -g myRG --profile-name cdn -n endpoint --content-paths '/*'Invalidate CDN cache for path pattern; use /* for full purge
  • az network front-door create -g myRG -n myFD --backend-address myapp.azurewebsites.netCreate Front Door with backend origin; configure health probe separately
  • az cdn endpoint create -g myRG --profile-name cdn -n endpoint --origin myapp.azurewebsites.netCreate CDN endpoint with custom origin (App Service, Storage, etc.)
Caffeine + load balancer map
// Front Door = global LB + WAF + CDN // Locally: cache + routing map Cache<String, Response> cdnCache = Caffeine.newBuilder() .expireAfterWrite(1, HOURS).build(); // Intelligent routing = latency-weighted map Map<String, Integer> backends = Map.of( "us-east", 12, // latency ms "eu-west", 89, "ap-south", 156); String best = backends.entrySet().stream() .min(Map.Entry.comparingByValue()) .map(Map.Entry::getKey).orElseThrow();
Front Door Anycast = route to lowest-latency backend. Locally: min-latency Map lookup. WAF = Servlet Filter for request validation.
Azure Monitor / Log AnalyticsObservabilityMetrics, logs, traces, alerts, dashboards for all Azure services and custom apps
e.g. Metrics Monitoring (Azure-native), Scale From Zero (health checks), Payment (audit logs)
Unified platform for metrics+logs+traces, Application Insights for APM, KQL query language
KQL has learning curve, data ingestion costs escalate quickly, retention costs extra beyond 90d
Basic metrics free · Log Analytics $0.30/GB ingest (first 5GB/mo free) · Alerts $0.10/rule/mo
  • az monitor metrics list --resource /subscriptions/.../myVM --metric 'Percentage CPU'Fetch metric values for a resource; use --interval PT1M for per-minute data
  • az monitor log-analytics query -w WORKSPACE_ID --analytics-query 'AzureActivity | limit 10'Run KQL query against Log Analytics workspace; great for cross-resource queries
  • az monitor alert create -g myRG -n myAlert --scopes /subs/.../myVM --condition 'avg Percentage CPU > 80'Create metric alert; fires when condition (avg CPU > 80%) sustained
Micrometer + SLF4J + OpenTelemetry
// Azure Monitor = metrics + logs + traces // Micrometer (metrics) MeterRegistry registry = new PrometheusMeterRegistry(); Counter reqs = registry.counter("http.requests"); reqs.increment(); // SLF4J (logs → Log Analytics) log.info("payment.processed", kv("amount", 100), kv("userId", id)); // OpenTelemetry (traces → App Insights) Span span = tracer.spanBuilder("processOrder").startSpan(); try { ... } finally { span.end(); }
Application Insights = OpenTelemetry SDK. Log Analytics KQL query = structured log search. Alert = Micrometer gauge threshold watch.
Azure OpenAI / CognitiveAI / MLLLM inference, embeddings, speech-to-text, vision, document intelligence
e.g. Autocomplete (semantic suggestions), Email (spam classification), Ad Click (fraud ML)
Enterprise SLA for OpenAI models (GPT-4, DALL-E), private endpoints, compliance certifications
Regional availability limited, quota constraints, model lag behind OpenAI.com releases
GPT-4o $5/million input tokens · $15/million output tokens · embeddings $0.13/million tokens
  • az cognitiveservices account create -g myRG -n myOAI --kind OpenAI --sku S0 -l eastusCreate Azure OpenAI resource; requires capacity allocation in Azure portal
  • curl https://myOAI.openai.azure.com/openai/deployments/gpt-4o/chat/completions?api-version=2024-02-01 -H 'api-key: KEY' -d '{"messages":[{"role":"user","content":"hello"}]}'Direct REST call to Azure OpenAI; use SDK (openai-python) for production
  • az cognitiveservices account deployment create -g myRG -n myOAI --deployment-name gpt4o --model-name gpt-4oDeploy a model version to your Azure OpenAI resource for inference
DJL / OpenNLP / local model inference
// Azure OpenAI = hosted LLM inference // Locally: Deep Java Library (DJL) or ONNX Model model = Model.newInstance("gpt2"); Predictor<String,String> predictor = model.newPredictor(translator); String result = predictor.predict(prompt); // Or: call local Ollama via HTTP HttpClient client = HttpClient.newHttpClient(); HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("http://localhost:11434/api/generate")) .POST(BodyPublishers.ofString( "{\"model\":\"llama3\",\"prompt\":\"" + prompt + "\"}")) .build();
Azure OpenAI = hosted GPU inference. Locally: DJL for Java-native ML, or Ollama for LLM via HTTP. No local equivalent matches GPT-4 quality.
No results found. Try a different search term.
Database Chooser
When to reach for each database — with signal words and anti-patterns
📊 Database comparison table
Database Type Best For Tradeoff
PostgreSQL/MySQLRelationalACID transactions, complex joins, financial data, user profilesLimited write throughput (~5K writes/sec single node); schema migrations costly at scale
CassandraWide ColumnHigh write throughput, time-series, messaging, IoT, activity logsNo joins; must design schema around access patterns; eventual consistency default
RedisIn-memory KVCache, session store, rate limiter, leaderboard, pub/sub, ephemeral stateData must fit in RAM; not durable by default; cluster adds complexity
MongoDBDocumentFlexible/changing schema, JSON documents, content management, catalogsJoins are expensive (must denormalize); less suited than Cassandra for extreme write scale
ElasticsearchSearch IndexFull-text search, autocomplete, log analytics, geo queries, faceted filtersNot source of truth; ~1s index lag; complex operational tuning; not for ACID
InfluxDB / TimescaleDBTime-seriesMetrics, monitoring, IoT sensors, financial tick data, append-only measurementsHigh-cardinality tags destroy performance; purpose-built, inflexible for other workloads
Neo4jGraphSocial graphs, fraud detection, recommendations, knowledge graphsLower write scale; rarely the answer unless relationships are the primary query dimension
S3 / Object StorageBlob StoreImages, video, audio, backups, large files, static assetsObject granularity only (no in-place updates); not queryable; CDN required for performance
🔍 Per-database decision guides
RelationalPostgreSQL / MySQL
  • Reach for it when: you need ACID, complex JOINs, or financial correctness
  • Scale ceiling: ~5K writes/sec single node. Add read replicas for read scaling. Shard when write QPS exceeds this.
  • Schema discipline: migrations on live tables require pt-online-schema-change or gh-ost at scale
  • Tradeoff: the most capable default DB — use it until you have a concrete reason not to
user profilesordersbillingACID transactions
Wide ColumnCassandra / HBase
  • Reach for it when: writes are massive and append-only, reads are simple key lookups
  • Design rule: one table per access pattern. Partition key determines node. Clustering key determines order.
  • No joins: denormalize everything. Flexibility is sacrificed for write scale.
  • Tradeoff: exceptional write scale but inflexible for ad-hoc queries
messagingtimelinesactivity logsIoT
In-memoryRedis
  • Reach for it when: latency must be sub-millisecond or data is ephemeral/frequently accessed
  • Key data structures: sorted sets (leaderboard), pub/sub (presence), streams (event log), hashes (session)
  • Durability: RDB + AOF for persistence — but never Redis as your only source of truth
  • Tradeoff: data must fit in RAM; cluster adds complexity
cachesessionrate limiterleaderboard
Search IndexElasticsearch
  • Reach for it when: query requires full-text search, fuzzy match, faceting, or geo_distance
  • Never source of truth: sync from primary DB via CDC or write-through. ES failure must not lose data.
  • Cardinality: high-cardinality fields as terms destroy index performance
  • Tradeoff: ~1s index lag; complex ops; not for ACID or primary storage
full-text searchautocompletelog analytics
🎯 How to justify your DB choice out loud
Template: "I'd use [DB name] for [component] because [access pattern]. The data is [shape/volume/consistency needs] and the primary query is [query type]. The tradeoff I'm accepting is [tradeoff]." Example (WhatsApp messages): "I'd use Cassandra for the message store because writes are massive and append-only, and the main read is 'get last N messages for conversation X' — a perfect partition key + clustering key query. The tradeoff is no ad-hoc queries, so I design the schema around this one access pattern." Example (Feed cache): "I'd use Redis sorted sets for the feed cache because reads must be sub-millisecond — ZREVRANGE is O(log N + K). I'm explicitly trading durability for speed: the feed can be rebuilt from Cassandra if Redis is wiped."
Pattern: name the DB → name the access pattern → name the consistency/scale requirement → name the tradeoff. That four-part answer signals staff-level thinking.
⚠️ Common anti-patterns interviewers penalize
  • Using Redis as primary storage — Redis is a cache first. Always have a durable source of truth behind it.
  • Storing BLOBs in PostgreSQL — images, videos, large files belong in S3 + CDN. Only store the object URL in the DB.
  • Using Cassandra for ad-hoc queries — Cassandra is optimized for known access patterns. Need flexibility? Use Postgres or Elasticsearch.
  • Making Elasticsearch your source of truth — ES index is a derived view. Always sync from a primary DB. An ES failure should not lose data.
  • Choosing a DB before knowing your access patterns — always derive the data model from your API design.
  • Saying "I'd use Cassandra" without explaining why — always add: "because writes are append-only at massive scale and I only need partition key lookups."
  • High-cardinality tags in InfluxDB — using user_id as a tag creates billions of series and destroys performance.
Easy
Medium
Hard