Health Checks Draining and Failure Capacity

Scaling DNS and Load Balancing · 40 min

System Design · Module 5 · Lesson 4 of 4 · 40 min

Health Checks Draining and Failure Capacity

Trace removal of an unhealthy server, distinguish readiness from liveness, and test whether surviving capacity can handle the peak.

Step through a backend removal →
Failure detection, safe request completion and sufficient surviving capacity are three different obligations.

01 The idea

The problem this lesson solves

Backend B stops responding at 10:00:00. The balancer does not instantly know this, requests already in flight do not disappear, and A and C may not have enough spare capacity. Failover is a sequence of detection, routing change and recovery. This lesson makes those stages explicit and explains how readiness, draining and retry rules interact.

Use the peak traffic and failure budgets from module 4 to judge whether an added server actually helps.

ReadinessWhether an instance should receive new work now.
LivenessWhether a process is considered alive enough to keep running under the platform policy.
DrainingStopping new assignments while allowing existing work a bounded chance to finish.

02 Worked example

Continue routing after B becomes unavailable

Health Checks Draining and Failure Capacity worked diagram01FailureB stops responding02Probe windowSome calls still selectB03RemovalOnly A and C eligible04RecoveryCapacity + retry policyRead events in time order, from left to right.Health Checks Draining and Failure Capacity compact diagram01FailureB stops responding02Probe windowSome calls still select B03RemovalOnly A and C eligible04RecoveryCapacity + retry policy
Failure detection takes time. Removing B prevents new selections after detection; it does not recover requests already sent to B.
1
All healthyFor six equal-cost requests, round robin over A,B,C produces A,B,C,A,B,C. Each receives two.
2
Observe a failureAfter request 2, health checking marks B unavailable. The simulation removes B before request 3 and preserves the next eligible position, C.
3
Continue routingThe sequence becomes A,B,C,A,C,A. Requests 3 through 6 never select B, but request 2 is not magically recovered.
4
Check remaining capacityA and C must handle the new total. If each was already saturated, removing B still leaves an overloaded system.

03 The method

Understand the mechanism

Distinguish process life from serving readiness

A liveness check asks whether the process should be considered alive under the platform’s policy. A readiness check asks whether it should receive new work. A process can be alive while still loading configuration, warming critical state or waiting for a required dependency.

Do not make every downstream failure trigger an app restart automatically. If all instances restart when the shared database slows, they may create a synchronized recovery storm without fixing the database. Readiness and liveness should reflect deliberate failure handling, not duplicate the same expensive dependency check by habit.

A useful readiness endpoint is cheap, bounded and meaningful for the work the instance will receive. An endpoint that always returns 200 while the instance cannot serve its main operation is misleading. An endpoint that executes a heavy query on every probe can itself create load.

Define startup, normal serving, degraded mode and shutdown behavior separately. The health-check contract determines which of those states is eligible for traffic.

Detection takes observations and time

An active health check runs periodically and may require several failures before removal. Probe interval, timeout, failure threshold and scheduling determine the observation window. A failure just after a successful probe may remain undetected longer than one just before a probe.

For example, checks every five seconds requiring two failed checks do not imply instantaneous removal at the moment the process fails. The exact delay depends on probe duration and alignment. During that interval, new requests may still be selected for B.

Passive signals can detect failures from real traffic, but they also need policy. A single timeout may reflect a transient network problem rather than a dead instance. Aggressive ejection can remove too much capacity; slow ejection can send too much work to a failing backend.

Recovery also needs hysteresis or readiness evidence. Immediately returning a recovering instance to full traffic can overwhelm it before caches and connections are ready. Balance rapid recovery with avoiding repeated in/out flapping.

Account for in-flight requests

Removing B from the eligible pool affects future selections after the routing change. It does not recover a request already sent to B, undo a committed operation or move an established connection to another server.

The caller or proxy may retry eligible operations, but only within their contract and deadline. A read may be repeatable while a creation needs an idempotency key. A timeout after a write reaches B leaves the familiar ambiguous-outcome problem.

The course simulation removes B before request 3 and continues from the next eligible position. Its selections are A,B,C,A,C,A. Request 2 was selected for B; the model does not claim it succeeded merely because the total selection count is six.

Keep metrics for selected attempts, completed successes, timeouts and retry outcomes. Otherwise a routing dashboard can look healthy while users experience failures during detection and recovery.

Drain an instance before stopping it

Planned shutdown is different from an unexpected crash. First stop sending new work to the instance. Then allow bounded in-flight requests to complete, close or signal persistent connections according to protocol, and terminate after the configured deadline.

The draining deadline must fit the service’s request model. A short HTTP operation can often finish quickly. A streaming connection may need an explicit client reconnection or resume protocol. Waiting forever prevents deployment; terminating immediately can interrupt avoidable work.

Ensure the app stops accepting new local work as it drains. A durable background job should either complete safely or be recoverable by another worker. A task that existed only in process memory can be lost even when HTTP draining looks correct.

Test the order of events in the actual deployment environment. Readiness changes, balancer propagation and process termination are not necessarily simultaneous. A termination grace period is useful only when it covers the observed routing and completion behavior.

Provision survivors and protect them from overload

Suppose three instances each have a safe budget of 100 QPS and the service receives 240 QPS. Normal operation has 300 QPS modeled capacity. Losing one leaves 200 QPS against 240 QPS demand, so successful removal of the failed instance still creates overload.

The remaining instances need reserve capacity, or the service needs a defined degraded/admission policy until capacity returns. Autoscaling has a delay; it cannot retroactively serve requests that timed out while new instances started.

Retries can worsen the shortfall. If failed requests immediately retry against A and C, their offered load rises beyond the original 240 QPS. Bound retries, apply backoff and shed work that cannot finish within useful deadlines.

Health policies must avoid a cascade in which overloaded survivors fail checks and are removed, concentrating traffic further. The response may involve preserving partially useful capacity under controlled admission rather than declaring every slow instance dead.

Include the balancer and configuration in the failure model

A single load-balancer process can be a failure point even when the application pool is redundant. Managed entry services or redundant proxy deployments have their own failure domains, configuration propagation and capacity limits. State what the deployment guarantees rather than drawing one box and assuming it is highly available.

Configuration mistakes can affect all healthy instances at once. A bad route, certificate or health-check path may make the whole pool unreachable. Rollout validation, staged changes and a tested rollback path therefore matter alongside machine redundancy.

Run failure drills that cover one app crash, one slow backend, planned draining, a cold replacement and an entry-point configuration change. Observe user outcomes and recovery time, not merely whether probes eventually turn green.

Failover is complete only when the service resumes acceptable behavior within its promised boundary. Detection, selection, capacity and operation recovery each contribute a different part of that result.

Guided tutorial

  1. Draw the detection window

    Place a backend failure between two periodic probes. Mark when the failure becomes observable and when the configured threshold removes the backend. Add a user request inside that gap. Its failure is possible even though the health-check mechanism is working as configured.

  2. Reproduce the simulation

    Run the healthy and removed-backend presets. Compare A,B,C,A,B,C with A,B,C,A,C,A. Count selections, then separately mark the request sent to B as potentially failed. Explain why a selection statistic is not a completion statistic.

  3. Calculate survivor load

    Use three 100-QPS instances and 240-QPS demand. Remove one and calculate the 40-QPS capacity shortfall. Propose reserve capacity or a documented admission policy, then show how unrestricted retries could make the deficit larger.

  4. Plan a graceful deployment

    Write the sequence: mark unready, stop new routing, drain bounded in-flight work, handle persistent connections and terminate. Specify a test that starts a request immediately before draining. Verify its outcome and the time until the instance receives no new work.

Observation commands and policy pseudocode

# Observational commands; responses vary over time
dig example.com A
curl -I https://example.com

# Simplified routing policy, not a full proxy configuration
eligible = instances whose readiness is healthy
backend = next_round_robin(eligible)
forward request using a bounded upstream deadline

Check your result

With B removed before request 3, the model selects A, B, C, A, C, A. Selection counts are A=3, B=1, C=2. They do not prove that the request sent to B succeeded.

05 Cheat sheet

Review the decisions

Use this table to explain the mechanism and its limit without memorizing a product name.

Choice or conceptRememberCheck
Readiness removalStops new assignmentsDetection and propagation take time
DrainingLets in-flight work finishNeeds a deadline and stream policy
Safe retryRecovers an eligible requestRequires operation semantics
Failure headroomHandles load after removalHealth checks do not add capacity

06 Where & why

Check the primary references

Use official references for protocol and implementation details. The questions here are teaching prompts.

Primary reference
NGINX HTTP load balancing

Read NGINX HTTP load balancing

Primary reference
Google SRE: handling overload

Read Google SRE: handling overload

State the implementation version and the workload assumptions when defending your own design.

07 Interview questions

Explain the reasoning out loud

Answer before opening the explanation. The questions progress from vocabulary to decisions and failure behavior.

What does Readiness mean?
Whether an instance should receive new work now.
What does Liveness mean?
Whether a process is considered alive enough to keep running under the platform policy.
What does Draining mean?
Stopping new assignments while allowing existing work a bounded chance to finish.
Why distinguish readiness from liveness?
An instance can be alive but not ready for new work. Restarting every alive process during a shared slowdown can worsen recovery.
Does removing a target recover its in-flight request?
No. The existing operation may fail or have an unknown outcome. Retry and recovery follow the operation’s contract.
Why can a green probe coexist with user errors?
The probe may not exercise the failing request path. Probe depth and thresholds need a deliberate trade-off.
What should happen before terminating a healthy old instance?
Stop new assignments, allow routing state to propagate and drain existing work within a bounded deadline.
What must be measured during failure testing?
Successful throughput, tail latency, retries, dependency saturation and surviving capacity, not only the healthy-target count.

08 Practice problems

Practice a new case

Solve the new cases before revealing hints. Complete worked solutions follow the set.

Readiness decision

Easy
A process is running but has not loaded its required local configuration; should it receive new requests?
Show the hint
Alive and ready are separate.

In-flight work

Easy
A target leaves the healthy pool while a POST is in flight; does removal prove the write failed?
Show the hint
Routing eligibility does not reveal commit state.

Failure capacity

Medium
Check whether four instances budgeted at 80 QPS each can serve 260 QPS after one fails.
Follow-up
Healthy routing is not extra capacity.
Show the hint
Count only survivors.

Drain timeout

Medium
A stream lasts longer than the rollout drain deadline; specify a client-visible policy.
Show the hint
A finite drain cannot wait forever.

Shared probe failure

Medium
Every app target becomes unready during a 10-second database slowdown; explain the amplification risk.
Show the hint
The dependency is shared.

Deployment incident

Hard
Explain a spike in duplicate creations during rolling deploys and propose a fix across the proxy and API.
Follow-up
Old instances are terminated while some requests are in flight.
Show the hint
Join draining with the timeout ambiguity from the API lesson.

Worked solutions

1. Readiness decision

It should remain unready until the required initialization succeeds, under the documented deployment policy.

2. In-flight work

No. The operation may have committed or failed. Use its retry/recovery contract rather than infer the outcome from target removal.

3. Failure capacity

Three survivors provide 240 QPS, below 260. At least five such instances are needed to retain four survivors and 320 QPS after one failure under this model.

4. Drain timeout

Define a reconnect/resume mechanism or documented termination behavior at the deadline. Coordinate retries and state recovery instead of assuming every stream finishes.

5. Shared probe failure

All frontends may disappear simultaneously and need time to recover readiness. Design degraded behavior and thresholds so a shared transient failure does not automatically remove every useful serving path.

6. Deployment incident

An old instance may commit before losing its response; an automatic retry creates another object. Drain existing work, bound retry behavior, and implement durable scoped idempotency keys in the API. Monitor retries and duplicate-key hits to verify the change.