Skip to content

The load balancer says healthy. Why do 30 percent of requests fail?

The Network Was Up. The Requests Were Not. · Part 6 of 6 · 6 min read

Read the worked explanation ↓

Interview question. A regional API has eight backends. One node passes /health every second and reports normal CPU, memory and process uptime. Yet about 30 percent of real customer requests routed through that node fail or time out. The aggregate regional availability dashboard dilutes the faulty node’s failure rate and does not identify the affected customer segment. Should the load balancer keep routing to it?

I would call this a possible gray failure. The node is neither completely dead nor completely functioning. It looks healthy to one observer and broken to another. This is a harder class of failure than a process crash because ordinary failure detectors are often designed to answer a simple question: did the process respond to my probe?

Before deciding whether to remove the node, we need to know which requests are failing. Does it affect only clients from one network, a particular route, large response bodies, IPv6, one backend dependency or a subset of TLS connections? A shallow /health endpoint might avoid every broken path. A deep health check might still pass if it uses cached credentials or follows a different network route.

The diagnostic objective is to connect the client-visible failure population to a backend and a failing path. Green machine metrics do not settle that.

Shallow health probes pass while a subset of client requests fail, requiring per-backend and client-observed failure detection with bounded outlier ejection.
Shallow health probes pass while a subset of client requests fail, requiring per-backend and client-observed failure detection with bounded outlier ejection.
Think first

A 12-node API fleet has one node with a 40 percent timeout rate for response bodies above 64 KB, while all nodes pass a 200-byte liveness probe. Design a diagnosis, per-node aggregation, temporary routing policy and tests for safe restoration. Explain why adding deeper probes alone may fail.

Take a few minutes to form an approach, then compare it with the worked explanation.

Check the exercise numbers

Assuming equal routing of requests above 64 KB, one of twelve nodes timing out on 40% of that class produces a 40% / 12 ≈ 3.33% failure rate within that class, if the other nodes succeed. If that class represents fraction f of each node’s traffic and other requests succeed, the whole-fleet failure rate is about 3.33% × f. Its size is not given, so the overall rate cannot be uniquely calculated. The affected node and customers still experience a serious partial failure.

Reveal the worked explanation

Why an aggregate dashboard hides the problem

Imagine eight backends receive roughly equal traffic. Seven succeed, while the eighth fails 30 percent of its requests. Assuming even distribution, the region's overall failure rate is 1/8 × 30% = 3.75%. A 3.75% fleet failure rate is a serious availability incident, not a harmless change. The aggregate error rate should trigger investigation, but it dilutes the faulty node’s 30% failure rate and cannot identify the affected customer segment. Fleet CPU may remain normal, while sticky sessions can expose individual customers to much worse failure rates.

The arithmetic assumes uniform routing. Load balancers may route by weights, reused connections or affinity. Long-lived connections can keep requests pinned to one backend and make the distribution uneven. Here the new problem is failure detection when the probe path and customer path disagree.

Microsoft Research calls this differential observability: components disagree about health because they observe different code paths or failure domains.

Finding the failing slice

I would examine successful and failed requests with dimensions such as target backend, route, source region or ASN where appropriate, protocol version, new versus reused connection, response size and downstream dependency. Trace sampling must include errors, not only successful requests that reached the service. Client-side error metrics matter because a request that never reached the application leaves no server span.

Next I would create a targeted synthetic probe that exercises the suspected user path. If image downloads fail, an HTTP probe returning 20 bytes of static text is the wrong test. If IPv6 requests fail while IPv4 works, probing a loopback endpoint over IPv4 will miss it. Synthetic probes are only useful when they overlap the failures we are trying to detect, and their cost should be controlled.

Possible causes include a faulty NIC queue, packet-size failure, stale dependency route or corrupted local cache. The symptom alone cannot choose among them. Compare the failing slices until captures or dependency traces isolate the path.

A backend showing normal CPU tells us that expensive work was not happening at scale on the node. It does not tell us whether requests successfully arrived, were admitted, or completed. Several earlier networking failures would evade that check.

What should the load balancer do?

Marking nodes unhealthy after any error is dangerous. If a shared database fails, ejecting all backends reporting errors can remove the entire fleet. If one customer submits malformed requests, treating those 4xx responses as backend failures would also produce false ejection. Define meaningful backend errors and compare enough samples against peers.

I would use per-backend success rates for eligible requests, connect errors and timeout rates, plus outlier detection with minimum volume thresholds. Once a node is a statistically meaningful outlier, reduce its weight or eject it temporarily with hysteresis and a maximum fraction of capacity removed. Observe whether customer errors improve. Keep a controlled way to probe and reintroduce the node, because permanently blacklisting a temporarily faulty host can waste capacity and mask the underlying defect.

When possible, an independent control plane should decide health using several signals rather than asking a potentially faulty node to certify itself. This does not mean running the full checkout workflow every second. Deep dependency checks are expensive and can worsen incidents. The useful combination is cheap liveness, meaningful readiness, client outcome telemetry and selective end-to-end probes.

Separate availability by customer journey too. A failing checkout matters even if thousands of image requests succeed.

The interview decision

Would I keep routing to the node? Not at full weight while its customer failure rate is a supported outlier, assuming enough healthy capacity is available. I would reduce or eject it in a controlled way and gather evidence, while guarding against mass ejection if the problem is shared. A temporary traffic shift is incident mitigation, not proof that the node's hardware is at fault.

I would set acceptance criteria around per-backend error distribution, client success by route, health probe coverage, ejection safety and recovery behavior under injected partial failures. Kill-one-node chaos tests are insufficient. We need tests where a node accepts TCP but delays half the responses, only one packet size fails, or one dependency route becomes unreachable.

In the interview I would keep returning to the same evidence: can this backend complete real user work on the affected path, and can routing around it avoid harming the remaining fleet?

Go deeper

The term gray failure was formalized in cloud-systems research describing differential observability. A load balancer's active check samples a different workload and vantage point from production clients. Passive outlier detection can be fooled by uneven tenant mix and low request counts, so compare matched traffic classes and account for uncertainty before ejecting capacity. Typical protections include a maximum ejection percentage, hysteresis, recovery probes, and precedence for direct transport failures over application-level business errors. Client telemetry and distributed tracing are complementary because the missing server span is itself evidence when a request fails before reaching the handler.

Check your reasoning

  • Define gray failure and differential observability with mismatched probes.
  • Compute regional effect under explicit equal-routing assumptions.
  • Correlate errors by backend and client-visible route, payload or network slice.
  • Use passive and targeted checks without mistaking business errors for host failure.
  • Bound outlier ejection and restoration to prevent fleetwide cascade.

Common mistake: If every backend returns 200 on /health, load-balancer routing is safe for all user requests.

How should routing respond when health probes pass but one backend is a supported customer-visible outlier?

Read the answer explanation

Compare client-visible outcomes and safely reduce traffic to a proven outlier — Probe success covers only the probed path. Client outcomes and bounded ejection are required for partial failures.

Apply it elsewhere

A storage cluster reports healthy disk and process liveness but one replica silently returns slow reads on a single partition range. Design client-side latency comparison and bounded replica avoidance without making a transient shared dependency failure evict every replica.

Compare your approach

Explain differential observability, separate client and probe paths, perform aggregate-to-backend error math, specify meaningful outlier statistics, protect against cascading ejection and validate with partial-failure injection.

One more constraint

If all backends show low CPU and successful `/health` probes, is a load balancer justified in concluding that they are equally healthy for real customer traffic?

Quick revision

A node can pass simple health checks while a subset of real client paths fail. This is a gray failure driven by differential observability. With eight evenly used backends, one failing 30 percent of its requests creates about a 3.75 percent fleet error rate, a serious incident even though aggregation dilutes the much worse experience for users pinned to it. Correlate client errors by backend, route and network path, use targeted probes and passive outlier evidence, then reduce traffic with hysteresis and bounded ejection. Avoid treating shared dependency failures as a reason to remove every backend.

References