Load Balancer — 26-Point Revision Notes (Enhanced)

Complete source-preserving HTML version — all supplied content retained. (Laymen purpose added for each point)

Below are 26 compact revision points designed to preserve the key judgment calls without turning this into a huge set of notes.

Load Balancer — 26-Point Revision Notes

1 L4 vs L7 — decide the layer first
🎯 Purpose (in plain terms): Before you pick a method to spread out traffic, you must first decide what kind of traffic you're looking at — just the basic connection (L4) or the actual content of the request (L7). It's like deciding whether you're sorting packages by weight (L4) or by what's inside the box (L7).
┌─────────────────────────────────────────────────────────────────┐
│                        L4 vs L7                               │
├───────────────────────────────┬─────────────────────────────────┤
│         L4 (TCP/UDP)          │          L7 (HTTP/gRPC)        │
├───────────────────────────────┼─────────────────────────────────┤
│ • Sees IP, port, connections  │ • Sees host, path, headers     │
│ • Forwards packets/connections│   cookies, method              │
│ • Blind to request content    │ • Can make per-request decisions│
│ • Fast, cheap, simple         │ • Rich, content-aware, heavier │
│ • Balances CONNECTIONS        │ • Can balance REQUESTS         │
└───────────────────────────────┴─────────────────────────────────┘

        Client ──TCP conn──► LB ──TCP conn──► Backend
        Client ──HTTP req──► LB (reads path/headers) ──► Backend

Key insight: Choosing the algorithm before choosing the layer is the wrong starting point.

2 Why L4 can be unfair
🎯 Purpose (in plain terms): L4 is like a bouncer who only counts how many groups (connections) enter, not how many people (requests) are in each group. One group might be a huge crowd (thousands of requests) while another is just a single person, but the bouncer treats them equally, which overloads the server.
        1 TCP Connection
        ┌─────────────────────────────────────────────┐
        │  Request 1 ──► Server A                     │
        │  Request 2 ──► Server A                     │
        │  Request 3 ──► Server A                     │
        │     ...                                     │
        │  Request 10,000 ──► Server A               │
        └─────────────────────────────────────────────┘

        Server A: ████████████████████████████████████  (overloaded)
        Server B: ████                                 (idle)
        Server C: ████                                 (idle)

This isn't an LB bug; it's a consequence of balancing at connection level.

  • L4 is still valuable because it is fast, cheap, protocol-agnostic, and simple
  • It works well when request volumes are similar across connections
3 L4 + L7 can coexist (layered design)
🎯 Purpose (in plain terms): You don't have to choose just one. Use L4 at the front door to quickly handle the crowd, and then an L7 behind it to smartly direct each person to the right room. It's a two-layer system for speed and smarts.
                    ┌─────────────────┐
                    │    Client       │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │   L4 LB         │  ← Fast edge, connection-level
                    │   (VIP/Anycast) │     Absorbs DDoS, connection flood
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │   L7 LB         │  ← Smart HTTP routing
                    │   (HAProxy/NGINX)│     Reads host/path/headers
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
        ┌─────────┐   ┌─────────┐   ┌─────────┐
        │ Service │   │ Service │   │ Service │
        │   A     │   │   B     │   │   C     │
        └─────────┘   └─────────┘   └─────────┘

Question: Don't ask "L4 or L7 for everything?" Ask: "Which layer should own which responsibility?"

4 Traffic entry is a separate availability problem
🎯 Purpose (in plain terms): Getting traffic to the load balancer itself is a different problem than distributing it to backend servers. If the front door is broken, it doesn't matter how well you've organized the rooms inside.
        ┌──────────────────────────────────────────────────────────────┐
        │              TRAFFIC ENTRY MODELS                          │
        ├──────────────────────────────────────────────────────────────┤
        │ 1. DNS-based:      client ──DNS lookup──► LB IP           │
        │    (coarse, TTL-limited failover)                         │
        │                                                           │
        │ 2. Anycast:        same IP announced from many locations  │
        │    (network routes to nearest healthy)                    │
        │                                                           │
        │ 3. Virtual IP (VIP): floating IP moves to healthy LB node │
        │    (fast failover, transparent to clients)                │
        │                                                           │
        │ 4. ECMP + multiple LB nodes: router spreads flows         │
        │    (active-active, scale-out)                             │
        └──────────────────────────────────────────────────────────────┘

        Client ──► DNS/Anycast/VIP/ECMP ──► LB ──► Backend Pool
  • Backend balancing assumes the LB is already reachable
  • If the LB itself dies, backend-selection logic is irrelevant
5 DNS vs VIP/Anycast — failover behavior
🎯 Purpose (in plain terms): DNS is like a phonebook that takes time to update when your address changes. VIP is like having a permanent address that instantly routes to a new house. VIP is faster for failover.
        DNS Failover:
        ┌─────────┐      ┌─────────┐
        │ Client  │──────►│ DNS     │  "LB IP = 10.0.0.1"
        └─────────┘      └─────────┘
                              │
                              ▼ TTL = 300s
        ┌─────────┐      ┌─────────┐
        │ Client  │──────►│ LB      │  10.0.0.1 dies
        └─────────┘      └─────────┘   Client keeps trying dead IP
                              │        until cache expires!
                              ▼
        Death + 300s = failover

        VIP Failover:
        ┌─────────┐      ┌─────────┐
        │ Client  │──────►│ LB1     │  10.0.0.1 (VIP) dies
        └─────────┘      └─────────┘
                              │
                              ▼ (seconds, not minutes)
        ┌─────────┐      ┌─────────┐
        │ Client  │──────►│ LB2     │  VIP moves to LB2
        └─────────┘      └─────────┘  Client IP never changes

Why VIP/Anycast wins: Network-level failover is more transparent/faster than DNS failover.

6 Backend discovery = source of truth
🎯 Purpose (in plain terms): Discovery is about knowing which servers exist. It's like having a list of all potential employees. But just because they're on the list doesn't mean they're ready to work.
        STATIC CONFIG                    DYNAMIC DISCOVERY
        ┌─────────────────┐             ┌─────────────────┐
        │ Load Balancer   │             │ Load Balancer   │
        │                 │             │                 │
        │ S1, S2, S3      │             │ ──► Registry   │
        │ (hardcoded)     │             │ ──► Control    │
        └─────────────────┘             │    Plane       │
              │                         └─────────────────┘
              ▼                              │
        Simple but stale             Complex but reflects reality
        Human edits                    Autoscaling, rolling deploys
        Can't keep up                 Self-registration possible

Discovery answers: "Does this backend exist?" It does NOT automatically answer: "Can it serve traffic now?"

7 Discovery and health are different
🎯 Purpose (in plain terms): Discovery tells you the server exists; health checks tell you if it's actually ready to do work. It's the difference between "the employee is in the building" and "the employee is at their desk, computer on, ready to take calls."
        ┌─────────────────────────────────────────────────────────────┐
        │         Discovery vs Health — Different Questions         │
        ├─────────────────────────────────────────────────────────────┤
        │                                                           │
        │   Registry says:    "S1 exists"                           │
        │                          │                                │
        │                          ▼                                │
        │   Health Check says: "S1 is ready"                        │
        │                          │                                │
        │                          ▼                                │
        │   LB Pool says:     "S1 is eligible for traffic"         │
        │                                                           │
        └─────────────────────────────────────────────────────────────┘

        BEFORE TRAFFIC:     Registered ──► Warming Up ──► Ready
                              (exists)       (not ready)   (eligible)

Key point: A warming-up server shouldn't receive traffic just because it registered. Stale registry can be tolerated with eventual consistency, but may delay discovering/removing capacity.

8 Backend selection — balance the right resource
🎯 Purpose (in plain terms): The goal is to balance the thing that is actually running low, like CPU, memory, or active connections. Don't just balance requests; figure out what's scarce and balance that.
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    SELECTION ALGORITHMS                           │
        ├─────────────────────┬───────────────────────────────────────────────┤
        │ ROUND ROBIN        │ R1→S1, R2→S2, R3→S3, R4→S1, R5→S2...        │
        │                    │ Simple, assumes similar request cost          │
        ├─────────────────────┼───────────────────────────────────────────────┤
        │ LEAST CONNECTIONS  │ S1=200, S2=50, S3=120 → new req → S2         │
        │                    │ Useful when connections ≈ work                │
        ├─────────────────────┼───────────────────────────────────────────────┤
        │ WEIGHTED           │ S1:5, S2:2, S3:1                             │
        │                    │ S1 S1 S1 S1 S1 S2 S2 S3                       │
        │                    │ Useful for heterogeneous capacity             │
        ├─────────────────────┼───────────────────────────────────────────────┤
        │ HASH-BASED         │ hash(client_id) → backend                     │
        │                    │ A→S2, B→S1, C→S2 (stable mapping)             │
        ├─────────────────────┼───────────────────────────────────────────────┤
        │ LATENCY-AWARE      │ Routes to fastest responding backend          │
        │                    │ Needs damping to avoid oscillation            │
        └─────────────────────┴───────────────────────────────────────────────┘

Senior question: "What resource is actually scarce on my backends?"

9 Round robin isn't automatically fair
🎯 Purpose (in plain terms): Round robin is like taking turns, but if one person's turn takes an hour and another's takes a minute, it's not fair. The server that gets the slow, heavy requests ends up doing most of the work.
        ┌─────────────────────────────────────────────────────────────────┐
        │          Round Robin assumes every request is equal            │
        ├─────────────────────────────────────────────────────────────────┤
        │                                                                 │
        │   Request 1: GET /health (1ms)    ──► Server A                 │
        │   Request 2: GET /health (1ms)    ──► Server B                 │
        │   Request 3: POST /report (30s)   ──► Server C  ◄── overloaded!│
        │   Request 4: GET /health (1ms)    ──► Server A                 │
        │   Request 5: GET /health (1ms)    ──► Server B                 │
        │   Request 6: POST /report (30s)   ──► Server A  ◄── overloaded!│
        │                                                                 │
        │   "Fair" request count ≠ Fair work distribution                │
        └─────────────────────────────────────────────────────────────────┘

The trap: 1 lightweight GET ≠ 1 heavy report-generation POST. The server can receive its "fair" number of requests while receiving an unfair amount of actual work.

10 Least-connections can also mislead
🎯 Purpose (in plain terms): Least-connections focuses on the number of open lines, not how busy each line is. A server might have many idle connections (like a waiting room) while another has fewer but very busy ones. You'll overload the busy one.
        ┌─────────────────────────────────────────────────────────────────────┐
        │         Least-connections ≠ Least-load                            │
        ├─────────────────────────────────────────────────────────────────────┤
        │                                                                   │
        │   Server A:  1,000 idle WebSocket connections                     │
        │              CPU: 5%    Memory: 20%    (plenty of headroom)       │
        │                                                                   │
        │   Server B:  50 active HTTP connections                           │
        │              CPU: 85%   Memory: 90%    (near overload)            │
        │                                                                   │
        │   LB sees:   A has 1,000 connections, B has 50                    │
        │   LB sends:  New request to B (least connections) ◄── wrong!      │
        │                                                                   │
        └─────────────────────────────────────────────────────────────────────┘

Lesson: Connection count is useful only if it correlates with real load (CPU, memory, active work).

11 Latency-aware routing needs damping
🎯 Purpose (in plain terms): This is like chasing the fastest checkout line. If you all rush to the same line, it slows down, and you all rush to the next one, causing a never-ending, chaotic game of musical chairs. You need to smooth out the reactions.
        ┌─────────────────────────────────────────────────────────────────┐
        │               OSCILLATION LOOP                                 │
        ├─────────────────────────────────────────────────────────────────┤
        │                                                                 │
        │   1. A is fast ──► LB sends traffic to A                       │
        │                     │                                          │
        │                     ▼                                          │
        │   2. A receives more load ──► A slows down                    │
        │                     │                                          │
        │                     ▼                                          │
        │   3. B now looks faster ──► LB swings traffic to B            │
        │                     │                                          │
        │                     ▼                                          │
        │   4. B receives more load ──► B slows down                    │
        │                     │                                          │
        │                     ▼                                          │
        │   5. Back to Step 1 ──► Continuous oscillation!               │
        │                                                                 │
        └─────────────────────────────────────────────────────────────────┘

Solutions: Smoothing, randomness, thresholds, damping factors. Aggressive reaction can create herd behavior that destabilizes the system.

12 Health checks are necessary but can lie
🎯 Purpose (in plain terms): A health check is like a quick "How are you?" from a manager. The server might say "I'm fine!" but be unable to handle a real customer request because its main tools (database) are broken. Don't trust the quick answer blindly.
        ┌─────────────────────────────────────────────────────────────────────┐
        │                  HEALTH CHECK TYPES                               │
        ├─────────────────────┬───────────────────────────────────────────────┤
        │ TCP Check           │ Can a TCP connection open?                   │
        │                     │ Proves: port is listening                    │
        │                     │ Does NOT prove: app can serve requests       │
        ├─────────────────────┼───────────────────────────────────────────────┤
        │ HTTP Check          │ Does GET /health return 200?                │
        │                     │ Proves: HTTP handler responds                │
        │                     │ Does NOT prove: dependencies work            │
        ├─────────────────────┼───────────────────────────────────────────────┤
        │ Deep Check          │ Does app + DB + dependencies work?          │
        │                     │ Proves: the whole request path               │
        │                     │ Risk: can overload dependencies              │
        └─────────────────────┴───────────────────────────────────────────────┘

        ┌─────────────────────────────────────────────────────────────────────┐
        │  HEALTH CHECK LIES:                                                │
        │  /health returns 200 OK  ──► LB thinks: "healthy"                 │
        │  But real request fails ──► auth down / DB timeout / cache miss   │
        └─────────────────────────────────────────────────────────────────────┘

Key point: 200 OK may only mean process alive, not real user request succeeds.

13 Health-check depth should match risk
🎯 Purpose (in plain terms): Don't do a full, expensive body scan for a simple checkup. Match the depth of the health check to how critical the server is. For important money stuff, do deep checks; for static content, a quick ping is enough.
        ┌─────────────────────────────────────────────────────────────────────┐
        │         HEALTH DEPTH vs COST OF BEING WRONG                       │
        ├─────────────────────────────────────────────────────────────────────┤
        │                                                                   │
        │   Low Risk (static content, read-only APIs)                       │
        │   ───────────────────────────────────                              │
        │   Shallow HTTP check ✓    (fast, cheap, sufficient)               │
        │                                                                   │
        │   Medium Risk (user profiles, product catalog)                    │
        │   ───────────────────────────────────────                          │
        │   Check cache + DB read ✓   (moderate depth)                      │
        │                                                                   │
        │   High Risk (payments, orders, settlements)                       │
        │   ──────────────────────────────────────────                       │
        │   Deep dependency check ✓   (expensive, but cost of failure       │
        │    with rate-limiting/caching)                                    │
        │                                                                   │
        └─────────────────────────────────────────────────────────────────────┘

Senior judgment: Define explicitly what "healthy" means for each service. Don't make every health probe expensive; rate-limit and cache deep checks.

14 Health thresholds prevent flapping
🎯 Purpose (in plain terms): Don't remove a server from service after a single hiccup, and don't add it back after a single success. Use a few consecutive failures to remove it, and a few successes to add it back. This prevents the server from bouncing in and out of service constantly.
        ┌─────────────────────────────────────────────────────────────────────┐
        │                  HEALTH STATE MACHINE                             │
        ├─────────────────────────────────────────────────────────────────────┤
        │                                                                   │
        │   ┌─────────┐      failures >= 3    ┌────────────┐               │
        │   │ HEALTHY │ ────────────────────► │ UNHEALTHY  │               │
        │   └─────────┘                        └────────────┘               │
        │        ▲                                   │                      │
        │        │                                   │                      │
        │        │     successes >= 2                │                      │
        │        └───────────────────────────────────┘                      │
        │                                                                   │
        │   Failure threshold = 3   (remove after 3 consecutive failures)   │
        │   Success threshold = 2   (re-add after 2 consecutive successes)  │
        │                                                                   │
        └─────────────────────────────────────────────────────────────────────┘

        Too sensitive:
        ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐  ──► Constant flapping
        │H│ │U│ │H│ │U│ │H│ │U│      Routing instability

        Too lax:
        H H H H H H H H H U U U U  ──► Broken server keeps receiving traffic

Tune based on: Cost of false removal vs cost of missed detection.

15 Connection handling is architectural
🎯 Purpose (in plain terms): You can either have the load balancer end the connection (like a receptionist who talks to you and then calls the back office) or just pass it through (like a mail slot). Termination gives you more control, but pass-through is simpler.
        PROXY MODEL (Terminating):
        ┌────────┐    conn1     ┌────────┐    conn2     ┌────────┐
        │ Client │──────────────►│   LB   │──────────────►│Backend │
        └────────┘              └────────┘              └────────┘
           │                        │                        │
           │◄─── TLS ──────────────►│◄─── New TLS/Plain ───►│
           │                        │                        │
           │    LB terminates client TLS, creates new        │
           │    connection to backend                        │

        PASS-THROUGH:
        ┌────────┐              ┌────────┐              ┌────────┐
        │ Client │──────────────►│   LB   │──────────────►│Backend │
        └────────┘              └────────┘              └────────┘
           │                        │                        │
           │◄───────────── TLS passthrough ─────────────────►│
           │                        │                        │
           │    LB forwards at lower layer,                  │
           │    no application awareness                     │

Trade-off: Termination gives visibility, TLS offload, header manipulation, L7 routing. Pass-through preserves end-to-end encryption but sacrifices content-based routing.

16 TLS termination changes the trust boundary
🎯 Purpose (in plain terms): When you end the encryption at the load balancer, you have to trust the connection from the balancer to the backend server. It's like having a secure mail room that then hands unsealed letters to the offices. The offices must trust the mail room.
        BEFORE TLS TERMINATION:
        ┌────────┐      TLS      ┌────────┐
        │ Client │───────────────►│Backend │
        └────────┘               └────────┘
           │                        │
           └─────── Trust ──────────┘

        AFTER TLS TERMINATION:
        ┌────────┐   TLS   ┌────────┐   plain/  ┌────────┐
        │ Client │────────►│   LB   │──────────►│Backend │
        └────────┘         └────────┘   new TLS └────────┘
           │                   │                    │
           │◄── Trust ────────►│◄── Trust ─────────►│
           │                   │                    │
           │   Two separately-trusted hops           │

Consequences:

  • Backend must trust the LB for forwarded identity/IP/headers
  • The LB becomes part of the security and failure path
  • Anything the backend believes about the client is only as trustworthy as the LB's own security
17 HTTP/2/gRPC changes the balancing problem
🎯 Purpose (in plain terms): HTTP/2 is like having a single giant pipe with many separate streams inside it. Balancing at the pipe level can send all streams to one server, which brings back the L4 problem. You need to balance at the stream level now.
        HTTP/1.1:  1 connection = 1 request at a time
        ┌──────────────────────────────────────────────┐
        │  Client ──► 1 connection ──► 1 request      │
        └──────────────────────────────────────────────┘

        HTTP/2:    1 connection = MANY concurrent streams
        ┌──────────────────────────────────────────────────────────┐
        │                  1 Connection                          │
        │  ┌────────────────────────────────────────────────┐     │
        │  │  Stream 1  │  Stream 2  │  Stream 3  │  ...   │     │
        │  └────────────────────────────────────────────────┘     │
        └──────────────────────────────────────────────────────────┘

        ┌──────────────────────────────────────────────────────────┐
        │  TRAP: Balancing per HTTP/2 connection                 │
        │                                                         │
        │  1 connection = 100 concurrent streams                  │
        │  LB sends all 100 streams to Server A                  │
        │                                                         │
        │  Recreates the exact L4 connection-fairness problem    │
        │  Just one abstraction layer higher!                    │
        └──────────────────────────────────────────────────────────┘

Modern design question: "Balance per connection or per stream/request?"

18 L7 routing is powerful — and dangerous
🎯 Purpose (in plain terms): L7 routing lets you send traffic to different places based on the URL, like a signpost. But if the signpost points to the wrong place, you'll send all your customers to the wrong store. A single mistake can break everything.
        ┌─────────────────────────────────────────────────────────────────────┐
        │                     L7 ROUTING RULES                              │
        ├─────────────────────────────────────────────────────────────────────┤
        │                                                                   │
        │   IF host = api.shop.com AND path = /checkout                     │
        │       ──► Checkout service pool                                   │
        │                                                                   │
        │   IF host = api.shop.com AND path starts /users                   │
        │       ──► User service pool                                       │
        │                                                                   │
        │   IF host = api.shop.com AND path starts /orders                  │
        │       ──► Order service pool                                      │
        │                                                                   │
        │   IF host = static.shop.com                                       │
        │       ──► Static content service pool                             │
        │                                                                   │
        └─────────────────────────────────────────────────────────────────────┘

        ┌─────────────────────────────────────────────────────────────────────┐
        │  DANGER: Bad routing rule                                         │
        │  IF host = api.shop.com AND path starts /orders                   │
        │       ──► User service pool  ◄── WRONG!                         │
        │                                                                   │
        │  Result: All /orders traffic gets 404 or errors                   │
        │  Blast radius: Entire order flow is blackholed                   │
        └─────────────────────────────────────────────────────────────────────┘

Key point: Routing config deserves code-like review/change control.

19 Don't accidentally turn LB into an API gateway
🎯 Purpose (in plain terms): A load balancer is for traffic management (like a traffic cop). An API gateway is for complex business logic (like a security guard, ticket checker, and translator). Don't make your traffic cop do the security guard's job; it makes failure more catastrophic.
        ┌─────────────────────────────────────────────────────────────────────┐
        │                  LB vs API Gateway                                │
        ├─────────────────────────────────────────────────────────────────────┤
        │                                                                   │
        │   Load Balancer:                                                  │
        │   ───────────────                                                │
        │   • Route by host/path                                            │
        │   • Distribute traffic                                            │
        │   • Health checks                                                 │
        │   • TLS termination                                               │
        │                                                                   │
        │   API Gateway (additional responsibilities):                     │
        │   ───────────────────────────────                                 │
        │   • Authentication/Authorization                                 │
        │   • Request/response transformation                              │
        │   • Rate limiting per tenant                                     │
        │   • Payload validation                                            │
        │   • Business policy enforcement                                  │
        │   • URL rewriting                                                 │
        │                                                                   │
        └─────────────────────────────────────────────────────────────────────┘

Why keep the LB "dumb"? Concentrating more logic at the LB = more logic every request depends on = larger blast radius.

20 Stickiness = deliberate fairness trade-off
🎯 Purpose (in plain terms): Stickiness is like having a regular waiter. It's good because they remember you and your order. But it's bad because if they get busy, they can't help others. Use it only when it's really beneficial (like for a user's shopping cart).
        WITH STICKINESS:
        ┌────────┐         ┌────────┐
        │ User A │────────►│ Server │
        └────────┘         │   S2   │
              │            └────────┘
              │                 ▲
              ▼                 │
        ┌────────┐         ┌────────┐
        │ User A │────────►│ Server │
        └────────┘         │   S2   │
              │            └────────┘
              │                 ▲
              ▼                 │
        ┌────────┐         ┌────────┐
        │ User A │────────►│ Server │  (always the same backend)
        └────────┘         │   S2   │
                           └────────┘

        METHODS:
        ┌─────────────────┬─────────────────────────────────────────┐
        │ Cookie-based    │ session123 → S2                        │
        │ Client-IP hash  │ hash(IP) → S3                          │
        │ Session-ID hash │ hash(session_id) → S1                  │
        │ Custom key      │ hash(user_id) → S2                     │
        └─────────────────┴─────────────────────────────────────────┘

Useful when: Local cache, in-memory session, warm context. Bad as a global default because: It creates hotspots. Prefer: Route-specific stickiness (only where needed).

21 Sticky backend failure must be recoverable
🎯 Purpose (in plain terms): If a user's "sticky" waiter gets sick, the user must be able to get a new waiter and recover. The system must handle the failure gracefully, even if it means losing some local context (like a partially filled cart).
        SCENARIO: User A is stuck to S2
        ┌────────┐         ┌────────┐
        │ User A │────────►│ Server │
        └────────┘         │   S2   │
                           └────────┘
                               │
                               ▼
                             S2 dies!
                               │
                               ▼
        ┌────────┐         ┌────────┐
        │ User A │────────►│ Server │  ← Remapped to healthy backend
        └────────┘         │   S3   │
                           └────────┘
        BUT:
        ┌──────────────────────────────────────────────────────────────┐
        │ • Sticky state (session, cache) is lost                     │
        │ • Application must rebuild/recover local state              │
        │ • Cache warm-up, session reload, reconnect logic            │
        └──────────────────────────────────────────────────────────────┘

Key principle: Health always overrides stickiness. If stickiness is required for correctness, failure becomes much harder.

22 Retry is a semantic decision
🎯 Purpose (in plain terms): Retrying a request can cause a duplicate order if the first attempt actually went through. It's like re-sending a letter—if the first one was delivered, the second one is a duplicate. You need an "idempotency key" to make retries safe for important things.
        RETRY RISK MATRIX:
        ┌──────────────────────┬─────────────────────────────────────────────┐
        │ REQUEST TYPE        │ RETRY SAFE?                                 │
        ├──────────────────────┼─────────────────────────────────────────────┤
        │ GET (read)          │ ✓ Yes — idempotent by design                │
        │ HEAD (read)         │ ✓ Yes — no state change                     │
        │ POST (write)        │ ✗ DANGER — duplicate if partially executed │
        │ PUT (idempotent)    │ ✓ Yes — if designed as idempotent           │
        │ DELETE (idempotent) │ ✓ Yes — if designed as idempotent           │
        │ PATCH (partial)     │ ✗ DANGER — can cause inconsistent state     │
        └──────────────────────┴─────────────────────────────────────────────┘

        RETRY SCENARIO:
        ┌────────────┐
        │ POST /order│ ────► Backend times out ────► Might have succeeded
        └────────────┘
              │
              ▼
        ┌────────────┐
        │ RETRY      │ ────► Backend processes again ────► DUPLICATE ORDER!
        └────────────┘

Golden rule: Idempotency key before retry on important write paths. Never make retry a blind universal LB feature.

23 Slow ≠ unhealthy
🎯 Purpose (in plain terms): A server might be slow but still running (like a tired employee). Don't mark it as dead, but do send it less traffic until it recovers. It's often better to reduce its workload than to remove it completely.
        HEALTH CHECK SAYS:
        ┌─────────────┐
        │ /health     │ ──► 200 OK ──► Healthy!  ──► Keep sending traffic
        └─────────────┘

        BUT REAL PERFORMANCE:
        ┌─────────────────────────────────────────────────────────────────────┐
        │  Response time:  15 seconds  ◄── Users are experiencing           │
        │  Error rate:     12%          timeout failures                    │
        │  Queue depth:    Growing                                         │
        └─────────────────────────────────────────────────────────────────────┘

        SOLUTION: Watch performance signals alongside binary health
        ┌─────────────────────────────────────────────────────────────────────┐
        │                                                                   │
        │  Signal               │ What to check                             │
        │  ──────────────────── │ ───────────────────────────────────────── │
        │  p95/p99 latency      │ Has it tripled?                          │
        │  Error rate           │ Is it rising?                            │
        │  Queue depth          │ Is it growing?                           │
        │  Active streams       │ Is it saturated?                         │
        │  Retry counts         │ Is the LB compensating for slowness?     │
        └─────────────────────────────────────────────────────────────────────┘

Prefer: Reducing traffic to a degraded backend rather than waiting for total failure.

24 Drain before removal + scale LB independently
🎯 Purpose (in plain terms): When taking a server out of service, tell it to finish its current work first before shutting it down. Also, scale your load balancers independently—don't make them tightly coordinated, or they'll slow each other down.
        DRAIN LIFE CYCLE:
        ┌────────────┐     ┌────────────┐     ┌────────────┐
        │  ACTIVE    │────►│ DRAINING   │────►│  REMOVED   │
        └────────────┘     └────────────┘     └────────────┘
              │                   │                   │
              ▼                   ▼                   ▼
        Receives         No new traffic,    Completely
        traffic          existing work      removed
                         finishes

        SCALING LB NODES:
        ┌─────────────────────────────────────────────────────────────────────┐
        │                                                                   │
        │   Clients ──► [ LB1 ][ LB2 ][ LB3 ][ LB4 ] ──► Backend Pool     │
        │                     │       │       │       │                      │
        │                     ▼       ▼       ▼       ▼                      │
        │               Each LB node operates independently                  │
        │               • Shared config from control plane                  │
        │               • Tolerate small eventual inconsistency              │
        │               • Avoid synchronizing every request                 │
        │                                                                   │
        └─────────────────────────────────────────────────────────────────────┘

Why no tight coordination: Adds latency, creates single point of contention.

25 Overload protection beats perfect balancing
🎯 Purpose (in plain terms): If more traffic arrives than your servers can handle, no amount of smart distribution will create new capacity. You need to protect the system by queuing, shedding, or rate-limiting traffic. It's better to serve 60% of customers well than to serve 0% because everything crashed.
        SCENARIO:
        ┌─────────────────────────────────────────────────────────────────────┐
        │  Incoming traffic = 100,000 RPS                                  │
        │  Safe capacity    =  60,000 RPS                                  │
        │                                                                   │
        │  Problem: 40,000 RPS more than backends can handle               │
        └─────────────────────────────────────────────────────────────────────┘

        WHAT "FAIR" DISTRIBUTION DOES:
        ┌────────────┐  ┌────────────┐  ┌────────────┐
        │  Server A  │  │  Server B  │  │  Server C  │
        │  33,333    │  │  33,333    │  │  33,334    │
        └────────────┘  └────────────┘  └────────────┘
              │               │               │
              ▼               ▼               ▼
        All overloaded ──► All fail together!

        WHAT OVERLOAD PROTECTION DOES:
        ┌─────────────────────────────────────────────────────────────────────┐
        │  Incoming 100k RPS                                                │
        │       │                                                            │
        │       ▼                                                            │
        │  LB detects saturation (queue depth, latency, CPU)                │
        │       │                                                            │
        │       ├──► Queue briefly (buffer)                                 │
        │       ├──► Rate limit (allow only safe capacity)                  │
        │       ├──► Shed load (return 503 to slow clients)                 │
        │       └──► Add capacity (scale out)                              │
        │                                                                   │
        │  Result: 60k RPS processed safely, remaining 40k shed/queued      │
        └─────────────────────────────────────────────────────────────────────┘

Fair distribution cannot create capacity. Protection preserves the system.

26 Leadership + production mindset
🎯 Purpose (in plain terms): Beyond the technical details, you need to think like a leader. Ask why a configuration exists, who owns it, and how to prevent problems. This is about governance, business risk, and continuous improvement—not just fixing the immediate bug.
        HEALTH STATE MACHINE:
        ┌────────────┐    ┌────────────┐    ┌────────────┐
        │  HEALTHY   │───►│ DEGRADED   │───►│ UNHEALTHY  │
        └────────────┘    └────────────┘    └────────────┘
              │                 │                  │
              │                 │                  │
              ▼                 ▼                  ▼
        ┌────────────┐    ┌────────────┐    ┌────────────┐
        │ DRAINING   │───►│  REMOVED   │    │            │
        └────────────┘    └────────────┘    └────────────┘

        OUR PLATFORM IN NUMBERS:
        ┌─────────────────────────────────────────────────────────────────────┐
        │                                                                   │
        │  • L4 + L7 layered                                                │
        │  • 80+ microservices                                              │
        │  • Mixed health depth (shallow + deep)                           │
        │  • Selective stickiness (route-specific)                         │
        │  • TLS at edge boundary                                          │
        │  • Rate-limit + 503 overload posture                             │
        │                                                                   │
        └─────────────────────────────────────────────────────────────────────┘

        DOMAIN-SPECIFIC RISKS:
        ┌─────────────────────────────────────────────────────────────────────┐
        │  MF redemption    → Retry risk       → Idempotency key            │
        │  IPO allotment    → Burst traffic    → Queue/overload protection  │
        │  NSDL demat       → Slow-but-alive   → Latency + deep checks      │
        │  Insurance        → Trust boundary   → Header verification/WAF    │
        │  LAMF             → Session pinning  → Scoped stickiness          │
        └─────────────────────────────────────────────────────────────────────┘

        LEADERSHIP QUESTION:
        Not just "How do we fix this incident?"
        But:
        ┌─────────────────────────────────────────────────────────────────────┐
        │  • Why does this configuration exist?                             │
        │  • Who owns the decision?                                         │
        │  • Does the original justification still exist?                   │
        │  • What's the governance gap that let this happen?                │
        │  • What's the business/compliance exposure?                       │
        └─────────────────────────────────────────────────────────────────────┘

One-line mental model

LAYER
  ↓
TRAFFIC ENTRY
  ↓
DISCOVERY
  ↓
HEALTH
  ↓
SELECTION
  ↓
CONNECTION / TLS
  ↓
L7 ROUTING / STICKINESS
  ↓
FAILURE + RETRY
  ↓
DRAIN / SCALE / OVERLOAD
  ↓
OBSERVE STATES
  ↓
SECURITY BOUNDARY
  ↓
PRODUCTION + LEADERSHIP JUDGMENT

Core Interview Principle

Don't start with "Which LB algorithm?"

Start with:

1. What exactly are we balancing? (connections? requests? streams?)

2. What can fail? (LB node? backend? network? dependency?)

3. What signal tells us it's failing? (health? latency? error rate?)

4. What is the cost of being wrong? (duplicate order? dropped request? customer impact?)

Quick Reference: The 26 Points in One View

#TopicKey Takeaway
1L4 vs L7Decide the layer before the algorithm
2L4 unfairnessBalances connections, not requests
3L4 + L7 coexistEach layer owns different responsibilities
4Traffic entrySeparate availability problem from backend balancing
5DNS vs VIPNetwork failover > DNS failover
6Backend discoveryExists ≠ ready for traffic
7Discovery + healthTwo different questions, combine both
8Selection algorithmsBalance the resource that's actually scarce
9Round robinCounts requests, not

Note: the source document's reference table ends mid-row at item 9 — preserved exactly as supplied, without completion.