Resilience patterns protect distributed systems from cascading failures by controlling how services behave when dependencies degrade. The core insight is that partial failure is the normal state — something is always slow, overloaded, or down — and uncontrolled failure propagation turns a single slow dependency into a system-wide outage. Without explicit resilience boundaries, threads, sockets, and retries pile up until healthy parts of the system also degrade.

The two foundational patterns here are Circuit Breaker (stop calling a failing dependency and fail fast instead of waiting) and Rate Limiting (cap request volume so one caller cannot exhaust shared resources). In production .NET systems, these compose into a resilience stack together with timeouts, retries with exponential backoff, and fallbacks — each layer handling a different failure mode. Polly and Microsoft.Extensions.Http.Resilience wire these layers into a single HttpClient pipeline.

Choose a response by failure and overload

SignalResponseFailure containedNew cost
One call exceeds its latency budgetTimeoutReleases caller capacity and bounds tail latencyCan abandon work that still completes downstream
A safe operation fails transientlyRetry with capped exponential backoff and jitterHides brief transport or overload faultsAdds load and latency; can duplicate unsafe writes
A dependency fails persistentlyCircuit BreakerStops repeated calls and lets the dependency recoverFast failures during the open interval
One workload exhausts shared resourcesBulkheadPreserves capacity for other workloadsReserved capacity may sit idle
Incoming demand exceeds safe throughputRate Limiting or load sheddingRejects work before queues and latency grow without boundSome valid work receives 429 or degraded service
Producer outruns consumerBackpressureMakes demand follow downstream capacityPropagates slowdown or requires bounded buffering
Optional capability failsFallback or graceful degradationKeeps the critical path availableStale, partial, or lower-quality output

“Let it crash” is a supervision choice, not permission to ignore a dependency failure. It is safe only when a supervisor restarts an isolated unit, state recovery is defined, crash loops are bounded, and the caller still receives a controlled outcome.

Map mechanisms to failure domain and recovery

Fault tolerance starts by naming the unit that may fail and the recovery objective:

Failure domainMechanismContinues during failure?Recovery requirement
Process or instanceMultiple instances plus health-aware load balancingYes, if capacity remains and health checks remove the failed instanceReplace capacity and preserve request idempotency
Availability zoneReplicas spread across zonesYes, if quorum and routing tolerate one zone lossRebuild replicas without overloading survivors
RegionActive-passive or active-active regional designDepends on failover mode and data replicationDefine RTO, RPO, DNS/routing convergence, and split-brain controls
Storage deviceMirroring, erasure coding, or replicated storageDepends on redundancy level; RAID 0 provides noneReplace media and rebuild before another failure
Dependency overloadAdmission control, queues, backpressure, and degradationCritical functions can continueDrain bounded work and restore optional features gradually
Software defectIsolation, canary rollout, rollback, and feature flagOnly outside the affected blast radiusStop rollout, revert safely, and preserve compatible state

Replication is not a backup: replicas can copy deletion, corruption, or a bad deployment. Monitoring is not fault tolerance either; it detects conditions so automated or human recovery can act. Prove each mechanism by exercising the stated failure domain and measuring recovery time and data loss.

References

5 items under this folder.