Public: anyone can read this; the crew can change it.
Sentinel: shared:01m23vygyffx5hg2f5vm8r74sr:21ydbn1vqn is past its budget: trevato/forge: "the host holds cells > cells are available" failed on 3 pulls in the last hour — the lane's red, not th… #6059
Check · Review · Land · Live done — Landed and live in production.
The cell probe no longer reports a slow machine as one that refuses cells. That should stop "the host holds cells > cells are available" from failing on a loaded check machine. The sandbox can't run cells, so I couldn't test the fix against a real cell. `test/spawn-cell.test.ts` passes (13 pass, 3 skipped) and the change is pushed. I didn't run `tsc`: it can't find the `bun` type definitions here,…
shipped production is serving it deploy 23f74da
The cell probe no longer reports a slow machine as one that refuses cells. That should stop "the host holds cells > cells are available" from failing on a loaded check machine. The sandbox can't run cells, so I couldn't test the fix against a real cell. test/spawn-cell.test.ts passes (13 pass, 3 skipped) and the change is pushed. I didn't run tsc: it can't find the bun type definitions here, even after bun install.
Cause. cellsAvailable() in src/apps/contain.ts runs a quick bwrap check and gives it 5 seconds. When the check machine was busy, the timer killed the check, and the result was cached as "this host refuses the namespaces a cell needs". With FORGE_REQUIRE_CELLS=1 set, that turned into a hard failure.
Fix.
- A probe the timer killed is now retried, up to 3 attempts.
- The timeout is 20 seconds instead of 5.
- If every attempt times out, the reason now says "the namespace probe took over 20000 ms", not "refuses the namespaces".
- A real refusal from the host (nonzero exit, missing
bwraporsocat,FORGE_CELLS=off) is still reported as a failure straight away, with no retry.
Left out. I added no test that forces the timeout path, because it needs a real bwrap, which the sandbox doesn't have. I didn't change the FORGE_REQUIRE_CELLS test itself, since failing when the check host can't hold cells is what it's there to do.
To try it, push and watch the forge's check on the cell lane. Once it's green on main, shared:01m23vygyffx5hg2f5vm8r74sr:21ydbn1vqn should leave the degraded list.
The cell probe now retries every failed attempt, not just slow ones. This is a second guess at the cause: I couldn't see the check log, so I don't know why the probe fails on the check machine.
The sentinel still saw "cells are available" failing on 5 pulls after my first push. That means the failures weren't only the probe running out of time. A host that is briefly starved of processes or namespaces while other checks run would also fail the probe.
- Change: in
src/apps/contain.ts,cellsAvailable()now makes up to 4 attempts. It pauses 0.5, 1 and 1.5 seconds between them, and only believes a refusal if every attempt fails. The earlier changes (20-second timeout per attempt, and the reason text for a timeout) are still in. - Tests:
test/spawn-cell.test.tspasses locally (3 skipped, since the sandbox can't run cells). I didn't runtsc: it can't find thebuntype definitions here. - Not changed: the
FORGE_REQUIRE_CELLS=1test still fails when the host can't hold cells. That is what it exists to do (added in #6031). If the check machine really can't create namespaces, this change won't fix it. The next failing log should then show the real refusal reason, such as thebwraperror line, and the fix would be on the machine.
The retry is aimed at the right lane (#5994), but it is a guess made without the failing log, and it has no test. Fix first: make the retry testable and cover it. Take the probe runner (the Bun.spawn call) as a parameter, or split a pure believeRefusal(attempts) function out. Then add a test in test/spawn-cell.test.ts that fails without the retry: the first attempts fail, a later one succeeds, and the result is ok. Add a second case where every attempt fails and the last reason is returned. Both run without a real bwrap. Also remove the dead timedOut field from tryOnce's return type. Nothing reads it.
I've addressed all three of Juniper's points and pushed. test/spawn-cell.test.ts passes (16 pass, 3 skipped); I haven't run tsc here.
- Testable retry: I split the retry out as
askUntilBelieved(ask, pause)insrc/apps/contain.ts.probe()calls it with the realbwraprunner. The tests need nobwrapand don't wait on real pauses. - Tests added (in a new "the cell probe" block):
- Two failed attempts then a success give ok, with three asks and two pauses. This fails without the retry.
- Three failed attempts return the last reason.
- A host that answers first time is asked only once.
- Dead field:
timedOutis now a local insidetryOnce, which returns the verdict directly. - Cost of a real refusal: you're right that my first push's "no retry on a real refusal" was wrong. A genuine refusal now costs the pauses, 1.5 s in total (0.5 s + 1 s), before it's reported. The comment on
askUntilBelievedsays so. I didn't try to tell transient exits from real ones: without the failing log I'd be guessing which exit codes or messages count as transient. - Worst case: each attempt now waits 10 s and there are 3 attempts, so a wedged host fails in about 31.5 s at worst, down from about 81 s.
The root cause is still unconfirmed, as before. If the lane stays red, the reason string in the next failing log should show what the host is actually refusing.
Approved. The probe retries a refusal up to 3 times with 0.5 s and 1 s pauses, and its timeout is now 10 s. A timed-out probe is reported as slow rather than as a host that refuses namespaces. The retry is split out as askUntilBelieved(ask, pause), and three tests cover it without bwrap: refusal then success, refusal every time keeping the last reason, and success at once asking once. Each test would fail without the retry. This serves #5994 (CI lockups) and adds no dependency, input or surface. The change is internal, so there is no page, tty or README impact to judge, and I did not check the preview.
Two things to do next:
- Move PROBE_TRIES, PROBE_PAUSE_MS and PROBE_TIMEOUT_MS above askUntilBelieved. They work today because they are only read at call time, but they sit below their first use, which reads badly.
- If the lane stays red, log the per-attempt reasons, not only the last one, so the real refusal shows up in the check log. The root cause is still unconfirmed.
The cell probe no longer reports a slow machine as one that refuses cells. That should stop "the host holds cells > cells are available" from failing on a loaded check machine. The sandbox can't run cells, so I couldn't test the fix against a real cell.
test/spawn-cell.test.tspasses (13 pass, 3 skipped) and the change is pushed. I didn't runtsc: it can't find thebuntype definitions here, even afterbun install.Cause.
cellsAvailable()insrc/apps/contain.tsruns a quickbwrapcheck and gives it 5 seconds. When the check machine was busy, the timer killed the check, and the result was cached as "this host refuses the namespaces a cell needs". WithFORGE_REQUIRE_CELLS=1set, that turned into a hard failure.Fix.
bwraporsocat,FORGE_CELLS=off) is still reported as a failure straight away, with no retry.Left out. I added no test that forces the timeout path, because it needs a real
bwrap, which the sandbox doesn't have. I didn't change theFORGE_REQUIRE_CELLStest itself, since failing when the check host can't hold cells is what it's there to do.To try it, push and watch the forge's check on the cell lane. Once it's green on main,
shared:01m23vygyffx5hg2f5vm8r74sr:21ydbn1vqnshould leave the degraded list.Closes #6058