Backend Scaling Part 2: Load Balancers, Databases, CDNs & Serverless
Horizontal scaling requires statelessness enforced through load balancers, read replicas, and sharding. CDNs reduce latency via geographic distribution. Asynchronous processing cuts perceived latency. Microservices and serverless solve organizational and cost problems but introduce complexity. Always measure before optimizing; prefer simple solutions; scale for current problems, not hypothetical millions.
Statelessness: The Foundation of Horizontal Scaling
Statelessness Definition
Statelessness means no server instance holds data exclusive to itself. Any request can be routed to any server and produce identical results. This is the core property enabling horizontal scaling.
Session Management Problem
If server A stores a user's session in memory after authentication, server B cannot verify that user on the next request, causing 401 errors. Solution: store sessions in centralized Redis accessible to all instances.
File Storage Problem
Storing uploaded files on a single server instance makes them inaccessible to other instances. Solution: use centralized object storage like S3 or Cloudflare R2.
Statelessness Checklist
For horizontal scaling: use Redis for in-memory data, centralized databases instead of SQLite files, object storage for files, and managed services for all state. No piece of information should live on a single instance.
Load Balancers: Routing Requests Intelligently
Load Balancer Role
A load balancer sits between clients and multiple server instances, deciding which server receives each request and returning responses. It's the middleman that makes horizontal scaling possible.
Round-Robin Algorithm
Simplest load balancer algorithm: sends requests in rotating order to servers A, B, C, A, B, C. Works well when all requests have similar cost and all servers have equal capacity.
Weighted Round-Robin
Variation of round-robin that accounts for different server capacities. A server with 8 GB RAM and 4 cores receives twice as many requests as servers with 4 GB RAM and 2 cores.
Least Connections Algorithm
Load balancer routes to the server with the fewest active connections. Better than round-robin for mixed request types: expensive requests (2s) stay on one server while lightweight requests (200ms) get routed elsewhere.
Least Response Time Algorithm
Routes requests to servers returning responses fastest. Servers struggling with resources get fewer requests; responsive servers get more.
Health Checks: Detecting Dead Servers
Load balancer sends test requests (GET) every second to all servers. If a server returns non-200 response, it's blacklisted from user traffic but continues receiving test requests. Once healthy again, it's restored to the pool.
Database Scaling: Read Replicas and Sharding
Why Databases Are Hard to Scale
Databases are stateful and hold data that must be consistent across instances. Unlike stateless application servers, you cannot simply duplicate a database—data consistency becomes the primary challenge.
Read Replicas Architecture
One primary database handles all writes; multiple replica databases handle reads only. Replicas are distributed geographically to reduce latency for users in different regions. Approximately 70-90% of requests are reads, so replicas handle most traffic.
Replication Lag Problem
Data travels at light speed through fiber optic cables (~200,000 km/s). From US to India (~20,000 km) introduces ~100-200ms latency. User updates name, then immediately refreshes; read replica hasn't received the update yet, showing stale data.
Solutions for Replication Lag
Route write-related reads to primary database; track replication lag and wait for sync before returning data; add planned latency in frontend before fetching updated data.
Sharding (Partitioning)
Divide a large table (e.g., billions of orders) into multiple physical database instances based on a sharding key (e.g., order date). Each shard holds a subset of data. Reduces query latency and allows independent scaling of each shard.
Modern Distributed Databases
PlanetScale (MySQL), Neon (PostgreSQL), CockroachDB, Yugabyte automatically handle sharding, replication, and distributed transactions. Recommended approach: use managed services rather than rolling your own database infrastructure.
Content Delivery Networks (CDNs): Geographic Latency Reduction
Physics Constraint: Speed of Light
Light travels ~200,000 km/s through fiber optic cables. A request from Tokyo to US East (Virginia) travels ~20,000 km, creating minimum ~100ms round-trip latency. This is a hard physical limit.
CDN Edge Nodes
Place CDN nodes (points of presence) geographically close to users. Tokyo user requests content from Tokyo CDN node instead of US server. Reduces round-trip distance from 20,000 km to ~100 km, cutting latency from 100ms to 2-3ms.
CDN Benefits: Load Distribution
By caching content at CDN nodes, primary server receives ~50% less traffic. Users request from nearest CDN node; only cache misses hit the primary server.
Static Content Caching
Cache JavaScript bundles, CSS, HTML, images, videos, fonts at CDN. Single-page applications (React, Vue) are ideal: deploy bundle to CDN, users fetch from nearest node.
API Response Caching
Cache API responses for content that changes infrequently (product catalogs, blog posts). Use cache invalidation/purging strategies (tag-based) to refresh when data updates.
CDN Security: DDoS Mitigation
Cloudflare and similar CDNs sit in front of servers, absorbing DDoS attacks. Attacker traffic distributed across CDN's massive network instead of hitting origin server. Prevents crashes and financial damage from attack-triggered autoscaling.
Edge Computing: Processing at CDN Layer
Beyond caching, CDN nodes can execute code (e.g., Cloudflare Workers using V8 isolates). Example: authenticate requests at edge, reject unauthorized users in 2-3ms instead of 100ms round-trip to primary server.
Edge Computing Constraints
CDN nodes have limited resources (1GB RAM, 1 CPU core vs primary servers with 8-32GB). Cannot run complex logic or long-running tasks. Ideal for authentication, routing, user customization, validation.
Asynchronous Processing: Reducing Perceived Latency
Synchronous vs Asynchronous Behavior
Synchronous: user waits for response before seeing result (e.g., profile update). Asynchronous: user sees success immediately; background work completes later (e.g., sending email). Not about code execution model but user experience.
Team Invite Example
User invites team member: validate, save to database (100ms), send email via external provider (300ms) = 400ms total. Solution: save to database, return success, push email task to queue. User sees success in 100ms; email sent asynchronously.
Queue-Based Architecture
Producer (main server) pushes task to queue (Redis Queue, RabbitMQ). Consumer/worker picks task from queue, executes it. User doesn't wait; task completes whenever worker processes it.
Ideal Use Cases
Email/notification sending, video/image processing, user data deletion, webhooks, file uploads. Any operation where user doesn't need immediate feedback.
Implementation: Bull MQ
Popular Node.js library using Redis. Handles error handling, rate limiting, retries. Easy to implement background jobs without building queue infrastructure from scratch.
Microservices: Scaling Teams, Not Just Machines
Monolith Definition
Single codebase with all functionality (auth, payments, notifications, orders) as modules. Deployed as one unit. Simple to develop, test, and deploy but becomes problematic at scale.
Monolith Problems at Scale
Deployment dependency: one team's unfinished code blocks another team's deployment. Scaling: cannot scale payment service independently from notification service. Tech stack: cannot use different languages for different modules.
Microservices Definition
Multiple independent services, each with own codebase, database, and deployment cycle. Services communicate via HTTP/gRPC. Solves organizational problems (team autonomy, independent scaling, tech flexibility).
Microservices Trade-offs
Network calls replace function calls (latency, failures). Debugging spans multiple services (need distributed tracing). Data consistency across services (replication lag, eventual consistency). Only justified for large teams (100+ developers).
When to Use Microservices
Large teams with clear boundaries. Independent scaling needs per service. Different technology requirements per service. Not recommended for startups or small teams; complexity outweighs benefits.
Serverless Computing: Pay-Per-Execution Model
Traditional Server Model
Always-on VM with fixed capacity (4GB RAM, 2 CPU cores). Pay 24/7 regardless of traffic. Capacity planning required: underprovision = crashes; overprovision = wasted money.
Autoscaling Limitations
Reactive, not proactive: scales only after detecting high load. Takes seconds to minutes to boot new instances. Set minimum/maximum limits: too low = crashes, too high = unexpected bills.
Serverless Model
No pre-assigned servers. Code (functions) deployed. On request, provider spins up instance, runs code, returns response. Pay only for execution time (milliseconds of CPU). No 24/7 costs.
Cold Start Problem
First request requires spinning up new instance: boot OS, start runtime, load code. Takes milliseconds to seconds depending on runtime. Subsequent requests within few seconds reuse instance (warm start).
Cold Start Mitigation
Use fast runtimes: Cloudflare Workers (V8 isolates, ~5ms), AWS Lambda with Firecracker (~50-100ms). Avoid Java, heavy frameworks. Keep instances warm with periodic pings (but costs money).
Serverless Constraints
Execution timeout (Lambda: 15 minutes max). Limited resources (1GB RAM typical). Stateless only: no persistent connections, no local file storage. No TCP connections to databases.
Ideal Serverless Use Cases
Event-triggered operations: video processing, image resizing, file uploads. Webhooks. Scheduled tasks. Not suitable for: latency-sensitive apps (banking, payments), long-running processes, database-heavy operations.
Serverless Reality
Powerful for specific use cases but not universal server replacement. Industry overhyped. Best used for event-driven workloads, not primary backend. Requires rethinking architecture (statelessness, event-driven design).
Performance Engineering: Principles and Mindset
Always Measure First
Use observability tools (Prometheus, Grafana, New Relic) to measure latency, errors, resource usage. Identify specific bottleneck before optimizing. Premature optimization wastes effort on wrong problems.
Understand Where Time Goes
Trace every request: routing (ms), deserialization (ms), database query (50-100ms), external API call (200-300ms), serialization (ms). Total latency is sum of all components. Fix the largest contributor first.
Prefer Simple Solutions
Vertical scaling before horizontal. Database indexing before caching. Monolith before microservices. Kubernetes only when necessary. Each component adds operational complexity and failure points.
Scale for Current Problems
Build for your current scale with reasonable headroom (10-20% buffer). Don't architect for a million users on day one. Most platforms never reach that scale. Learn bottlenecks through real usage.
Observability from Day One
Exception to 'keep it simple': implement logs, metrics, traces from start. Prevents random crashes, enables quick diagnosis. Pays off immediately as system grows.
Performance is a Mindset
Continuous learning through trial and error. Build systems that handle problems gracefully when they occur. Develop diagnostic skills. No single tutorial teaches this; experience is key.
Notable quotes
The key enabler of horizontal scaling is statelessness—no instance should hold data exclusive to itself. — Instructor
Microservices are not about scaling machines; they're about scaling teams and human organization. — Instructor
Always measure first. Premature optimization is the worst thing you can do when solving performance problems. — Instructor
Action items
- Implement health checks in your load balancer configuration to detect and blacklist failed server instances.
- Set up centralized session storage (Redis) and object storage (S3) to enable stateless horizontal scaling.
- Deploy observability stack (Prometheus, Grafana, or New Relic) to measure latency, errors, and resource usage across all system components.
- Profile your application to identify bottlenecks: measure database query time, external API latency, and request processing time.
- For read-heavy applications, configure read replicas in different geographic regions to reduce latency.
- Implement asynchronous processing for non-critical operations (emails, notifications, video processing) using a queue library like Bull MQ.
- Evaluate CDN integration for static assets (JavaScript, CSS, images) and cache API responses for infrequently-changing data.
- Document your current scale and growth projections; only adopt microservices or serverless if justified by specific organizational or technical needs.