Skip to content

[factory] A hang is not a failure: readinessReconcile can never trip its own breaker, so a wedged sweep is a permanent absorbing state #372

Description

@khaliqgant

Filed from the 2026-08-25 dispatch outage. This is the reason it lasted days
rather than minutes, and it is independent of any individual unbounded call.

The defect

readinessReconcile has a failureThreshold of 3 and a consecutiveFailures
counter. Through every wedge observed today, across five container boots and
two different root causes, that counter read 0.

08:24:00Z   rr=stalled   inFlightMs=1860000 (31.0 min)   missedPasses=30
            consecutiveFailures=0            failureThreshold=3
            intervalMs=60000

A sweep that never returns never throws. A hang is not a failure. So the
threshold cannot be crossed, the breaker cannot open, and any recovery gated on
consecutive failures — including container replacement — can never fire. The
subsystem is stuck in a state its own health model has no way to escape.

The public projection even reports ok: true alongside
status: "degraded" while dispatch is fully down.

Why the existing signals did not help

  • consecutiveFailures — structurally cannot move during a hang.
  • state: stalled — it is set, but nothing acts on it.
  • missedPasses — climbs correctly (one per interval) and is the honest
    signal, but nothing acts on it either.
  • inFlightMs — climbs exactly with wall clock and is the sharpest indicator
    available. Also unconsumed.

So the instrument already carries the truth. Nothing consumes it.

What done looks like

  1. A hang is classified as a failure. If inFlightMs exceeds some multiple
    of intervalMs (the observed wedges ran 30x and 31x over a 60s interval),
    the sweep is unhealthy in the sense the breaker understands — not merely
    labelled stalled.
  2. Recovery is reachable. Whatever action consecutiveFailures >= failureThreshold unlocks must also be reachable from a hang. Today a wedged
    sweep is a permanent absorbing state; see also factory-cloud#62, which
    makes the same point about /healthz being unable to see an offline
    dependency, and factory-cloud#63 on recovery being gated on a
    timer-written heartbeat.
  3. A must-fire test: a sweep whose inner call never returns eventually trips
    the same path a failing sweep trips. It must fail before the change.
  4. A must-not-fire test: a sweep that is merely SLOW but completing inside
    its budget does NOT trip it. Without this, the obvious wrong fix — treating
    any long sweep as failed — passes and introduces flapping.

Scope note

This is deliberately NOT a fix for any particular unbounded call. Today's
outage went through three of those:

  • factory-cloud#78 — unbounded FACTORY_STATE Durable Object calls
  • factory#368 — unbounded relayfile change-log tail reads
  • and a third layer, the retry of the now-bounded call

Each was real and each was fixed, and the wedge returned one layer down every
time. A detector that treats a hang as a failure is what makes layer N+1 a
degradation rather than an outage, regardless of which call is next.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions