Resilience Demo: Naive vs. Resilient Integration
One small, self-contained repo (no database, minutes to clone and run) modeling a real pain point from third-party integration work: what happens when the dependency you're calling goes down mid-request. The naive client is a plain HttpClient call with no retry, no timeout override, no circuit breaker. The resilient client wraps the same call in a hand-composed Polly v8 pipeline (retry with backoff+jitter, circuit breaker, per-attempt timeout, graceful fallback) via Microsoft.Extensions.Http.Resilience, built test-first against a WireMock.NET-simulated dependency. A single k6 script drives both clients through an identical scripted health -> outage -> recovery cycle in one run, so the comparison reflects the exact same failure window, not two separately-timed tests.
- Verified during a real 30-second total outage: the resilient client's circuit breaker fails fast enough to handle 440 requests in that window versus the naive client's 160 (2.75x the throughput) — identical (100%) error rate since nothing can fix a full outage, but p95 latency is 3.07s naive vs 2.17s resilient because the naive client is still paying full retry+timeout cost on every rejected call.
- A 5-second-resolution timeline (not just before/after aggregates) captures the breaker actually tripping: one brief 8.8s latency spike as it evaluates the first failures, then a hard drop to ~20-30ms once it opens — and on recovery, a sharp but short-lived 65.7% error rate in the single bucket right after the dependency comes back (the breaker's half-open trial window), gone by the next bucket.
- Circuit breaker and retry parameters sized deliberately against the k6 load profile so the breaker genuinely trips within the test's outage window, not left at Polly's defaults which wouldn't trip in a short run.
- Resilience pipeline built test-first: retry counts, circuit-breaker open/half-open/close transitions (including a test that proves an open breaker is never retried — zero additional calls reach the dependency once tripped), and per-attempt timeout are all asserted against a real WireMock.NET-simulated dependency.