Skip to content

Failure injection under load

A resilience experiment asks whether Atlas preserves correctness, exposes a confirmed fault, degrades deliberately, and recovers while governed traffic continues. Load without a fault measures capacity. A fault without traffic does not establish user-visible behavior under pressure.

Controlled experiment

sequenceDiagram
    participant Driver as Load driver
    participant Atlas
    participant Control as Fault controller
    participant Evidence
    Driver->>Atlas: fixed governed workload
    Evidence->>Evidence: healthy baseline
    Control->>Atlas: inject one named fault
    Evidence->>Evidence: independently confirm impact
    Driver->>Atlas: unchanged traffic through fault
    Control->>Atlas: remove fault
    Evidence->>Evidence: confirm removal + recovery window
Control Why it is required
healthy pre-fault interval Proves the target carried the workload before injection
one named fault Keeps cause and blast radius attributable
fixed workload and query corpus Prevents demand change from masking degradation
independent impact confirmation Proves the controller changed the intended boundary
protected and shed classes Separates survival from deliberate rejection
confirmed removal Establishes the start of recovery measurement
post-recovery window Detects flapping, stale state, and delayed retries

Abort when baseline is unhealthy, impact cannot be confirmed, required telemetry disappears, offered load collapses, cleanup fails, or data identity becomes uncertain. These are invalid or escaped experiments, not product resilience failures.

Define the blast radius

Before injection, record:

  • exact pod, dependency, network path, shard, volume, or resource pool;
  • affected release, dataset, replica, zone, and tenant boundary;
  • protected cheap, cached, health, audit, and control traffic;
  • work allowed to shed, including expected status and error classes;
  • manifests, catalogs, locks, artifacts, and cache state that must remain authoritative;
  • a cleanup path that does not depend on the disrupted component.

A global error rate cannot prove containment. Segment results by release, dataset, replica, route class, and cache condition wherever the claim depends on them.

Measure one timeline

stateDiagram-v2
    [*] --> Stabilizing
    Stabilizing --> Healthy: baseline passes
    Healthy --> Injecting: control begins
    Injecting --> Faulted: impact confirmed
    Faulted --> Removing: duration completes
    Removing --> Recovering: removal confirmed
    Recovering --> Recovered: invariants stabilize
    Recovering --> ResidualFailure: deadline or invariant fails

Measure detection from confirmed impact to first required signal, degradation from confirmed impact to restored user behavior, and recovery from confirmed removal to stable invariants. Do not collapse all three into one ambiguous duration.

Prove fault fidelity

Evidence point Required fact
controller Action, target, start, duration, and requested cleanup
dependency or resource Independent confirmation of the intended impact
Atlas detection First metric, event, trace, health, or breaker transition
clients Correct success, deliberate rejection, dependency failure, timeout, transport failure, or incorrect success
protection Timeout, breaker, cache, admission, or shedding policy that acted
removal Independent confirmation that the fault ended
residual state Replicas, catalog, store, caches, locks, and telemetry returned to authoritative state

A quick 200 with stale, partial, cross-dataset, or unverifiable content is more severe than a bounded explicit rejection. Availability budgets never authorize ambiguous data.

Current governed scenarios

Scenario Question Current expectation
store-outage-under-spike Can verified cached data survive store loss and a traffic spike? Cached requests return 200 or 304; uncached work fails explicitly
noisy-neighbor-cpu-throttle Do cheap routes survive CPU contention? Cheap work succeeds; heavy work may shed with 503
pod-churn Does instance replacement preserve bounded service? Readiness, error, latency, and recovery are recorded
load-under-rollout Can a candidate enter service under attributed traffic? Requires an executable rollout controller
load-under-rollback Can the prior release recover under traffic? Requires an executable rollback controller

The injection catalog is broader than the load catalog. A combined claim needs a run that names both the fault mechanism and load scenario. The current rollout and rollback entries lack their declared runner files and therefore do not supply executed evidence.

The store-outage budget is p95 ≤ 1,500 ms, p99 ≤ 3,000 ms, and error rate ≤ 10%. These limits bound degradation; they do not permit incorrect answers.

Transfer escaped experiments to incident response

Stop offered load, fence mutation, preserve the original clock, and transfer authority when impact escapes the declared boundary, cleanup cannot restore state, protected traffic exceeds its abort budget, an undeclared tenant or dependency is affected, or integrity becomes uncertain.

Retain experiment ID, release, dataset, controller action, confirmed target, offered load, first escaped impact, active protections, cleanup attempts, and last trusted state. The incident timeline begins with the original experiment; do not reset it or continue injecting faults for diagnostic convenience.

Cleanup is part of the verdict. Prove injected controls are absent, replicas and routing match intent, store and catalog identities agree, no partial object or poisoned cache was silently cleared, telemetry covers every window, and a second healthy observation remains stable.

Use Pod Churn Resilience for instance replacement and Rollout Under Load for release changes.