diff --git a/docs/demos/retry.md b/docs/demos/retry.md index 9ce0fe0..ea05156 100644 --- a/docs/demos/retry.md +++ b/docs/demos/retry.md @@ -7,7 +7,7 @@ a dead backend can't be amplified.
-

That was one client. The real danger of blind retries shows up at scale — when many clients retry the same outage at once.

+

That was one client recovering from a blip. The real danger of blind retries shows up at scale — when many clients hit a real outage at once.

@@ -20,22 +20,8 @@ document.addEventListener('DOMContentLoaded', function () { ? { ok: false, ms: 0.05, label: 'blip' } : { ok: true, ms: 0.05 }, chainB: { retry: { maxAttempts: 3, baseDelay: 0.1, maxDelay: 5.0 }, budget: { ttl: 10.0, minRetriesPerSec: 10.0, percentCanRetry: 0.2 } } }, - { id: 'sustained', label: 'Sustained outage', dur: 12.5, - fault: (now) => (now >= 2.0 && now < 9.0) - ? { ok: false, ms: 0.05, label: 'DOWN' } : { ok: true, ms: 0.05 }, - chainB: { retry: { maxAttempts: 3, baseDelay: 0.1, maxDelay: 5.0 }, - budget: { ttl: 10.0, minRetriesPerSec: 10.0, percentCanRetry: 0.2 } } }, ], - buildStops: (scenario) => scenario.id === 'sustained' ? [ - { when: (s) => s.now >= 1.2, spot: ['ifA', 'ifB'], title: 'A backend blip appears', - body: 'Both clients hit the same transient errors. Watch how each responds.' }, - { when: (s) => s.now >= 2.4, spot: ['ifB'], title: 'httpware retries the blip', - body: 'The plain client surfaces the error immediately. httpware retries with backoff — most of these recover on attempt 2 or 3, invisibly to the caller.' }, - { when: (s) => s.mw.budgetExhausted, spot: ['ifB'], title: 'The budget refuses to amplify', - body: 'On a SUSTAINED outage, blind retries would multiply load. The budget is spent — httpware STOPS retrying and fails fast, protecting the dying backend instead of hammering it.' }, - { when: (s) => s.now >= 10.0, spot: ['ifA', 'ifB'], title: 'Blip: recovered. Outage: contained', - body: 'Retry rescues transient errors without turning a real outage into a storm. That cap is the whole reason the budget exists.' }, - ] : [ + buildStops: () => [ { when: (s) => s.now >= 1.2, spot: ['badWrapA', 'badWrapB'], title: 'A backend blip appears', body: 'Both clients are about to hit the same transient errors. Keep your eye on the ✗ failed counts — they start equal at zero.' }, { when: (s) => s.now >= 2.4, spot: ['badWrapA', 'badWrapB'], title: 'Plain surfaces every error; httpware retries', @@ -47,7 +33,7 @@ document.addEventListener('DOMContentLoaded', function () { HttpwareDemo.mountHerd('#retry-herd', { clients: 20, - intro: 'A real backend rarely dies cleanly — it flaps: fails, recovers, fails again. These strips show backend call-rate over time for twenty clients through three dips. Press play and watch the shape.', + intro: 'Retry rescues a transient blip (above) — but it can’t fix an outage: retried or not, the caller sees the same failures. There the danger isn’t caller failures, it’s amplification. A real backend rarely dies cleanly — it flaps: fails, recovers, fails again. These strips show backend call-rate over time for twenty clients through three dips. Press play and watch the shape.', scenario: { id: 'storm', dur: 12.5, fault: (now) => { const down = (now >= 2.0 && now < 4.0) || (now >= 5.5 && now < 7.5) || (now >= 9.0 && now < 11.0); diff --git a/planning/changes/2026-07-19.03-drop-single-client-sustained-scenario.md b/planning/changes/2026-07-19.03-drop-single-client-sustained-scenario.md new file mode 100644 index 0000000..b2b14cf --- /dev/null +++ b/planning/changes/2026-07-19.03-drop-single-client-sustained-scenario.md @@ -0,0 +1,44 @@ +--- +summary: Dropped the redundant "Sustained outage" scenario from the single-client retry demo (retry can't help the caller on a sustained outage, so its counters read ~equal and misled) and moved the myth-bust into the herd intro, which now owns the outage story with numbers that pop. +--- + +# Change: Drop the single-client retry demo's sustained-outage scenario + +**Lane:** lightweight — docs demo only, one file (`docs/demos/retry.md`), no capability contract moves. + +## Goal + +The single-client retry demo's "Sustained outage" scenario shows plain ✗82 vs +httpware ✗81 — near-identical, because retry genuinely cannot rescue a caller +from a *sustained* outage (the backend is down the whole time). That is correct +model behavior, but it makes the scenario read as "no difference", against the +suite's design where every scenario spotlights a number that pops. Its stop copy +also overclaimed ("httpware STOPS retrying and **fails fast**") while the +fast-failed counter stays 0 — a budget-exhausted request still hits the backend +once and is counted as ✗, not a fast-fail. + +## Approach + +Remove the sustained scenario from the single-client demo, leaving only the +"Brief blip" scenario, where retry's caller-visible win is self-evident (plain +✗6 vs httpware ✗0). The sustained/amplification story now lives — with numbers +that pop (naive ~18× vs httpware ~3×) — in the herd view directly below, added +in `2026-07-19.02`. Preserve the "retry rescues blips, not outages" myth-bust as +one line of the herd intro rather than a whole flat scenario, and align the +bridging prose (the single-client demo now shows a blip, not an outage). + +`buildStops` loses its `scenario.id === 'sustained'` branch and becomes the +single blip stop-list. + +## Files + +- `docs/demos/retry.md` — remove the `sustained` scenario + its stop branch; + simplify `buildStops`; add the myth-bust to the herd `intro`; retune the + bridging `

`. + +## Verification + +- [ ] `just docs-build --strict` clean; `just check-planning` OK. +- [ ] jsdom harnesses green: `verify-demos.js` (retry single-client tour still + completes with the one blip scenario), `verify-herd.js`, `verify-real-page.js`. +- [ ] `node --check docs/demos/engine.js` (unchanged, sanity).