Repository navigation
Fix intermittent multi-lane boot EBUSY in cgroup setup (F1) - #97
Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
… a contested enable retries F1 (PR #97): write_file formatted the io::Error into the message, so is_busy/is_gone (which classify off error.chain()) never saw EBUSY and the first healthcheck exec landing between drain and enable bailed the boot. Squashed for one rebase across the changelog-fragment cutover (#89).
c465faf to
37a4f9f
Compare
|
Parent review: root cause and fix accepted.
I squashed and rebased onto The multi-lane |
Root cause
Intermittent
write cgroup.subtree_control: EBUSYat runner boot in the multi lane (CI run 39767096081, job 119785212884, PR #93).The drain+enable retry loop added in #68 never fired:
write_filewrapped failures withanyhow!("write {name} in {dir}: {e}"), formatting theio::Errorinto a string and destroying the error chain.is_busy(andis_gone) classify offerror.chain()looking for a matchingraw_os_error— unreachable through a formatted message. So the first orchestrator healthcheck exec (compose.yamlprobes/readyevery 10 s;docker execjoins the container cgroup by design) landing betweenis_drainedand thesubtree_controlwrite bailed the boot. The/readydetail matches exactly:enabling /sys/fs/cgroup: ... EBUSYwith nodescribe()suffix is the non-busy bail path, i.e.is_busyreturned false on an EBUSY. Unit tests hand-builtanyhow!(io::Error)directly, never throughwrite_file, which is why they stayed green.Fix (deterministic)
write_file(plus the two other cgroup read sites) keeps the OS cause withwith_context;is_busy/is_gonenow classify production errors.cgroups::setupruns inmainbefore the first child spawn, and the drain moves the runner itself into the leaf, so everything the runner forks afterward inherits the leaf. The bounded retry (50 x 100 ms) rides out transient external execs; a persistent squatter still degrades loudly past the budget. The retry is justified, not a loophole: the race is external (orchestrator healthchecks) and unavoidable from inside the container.drain_and_enable(verbatim loop, injectedenable/settleseams +proc_root) for the barrier-driven test.Verification
cgroup_write_errors_keep_their_os_cause: fails pre-fix (chain erased,None vs EISDIR), passes now.busy_enable_retries_past_a_spawn_racing_init: fake cgroupfs + fake procfs, squatter thread pinned into the drain-to-enable window by barriers (no sleeps); proves one contested enable retries and the racer drains next round.cargo fmt --check,cargo clippy --locked --all-targets -- -D warnings, fullcargo test --locked(590 passed),cargo build --release --locked, ts-rs bindings unchanged.bun run checkskipped: Rust-only diff, no node_modules in this env.e2elabel (this PR), watched to completion.Security review
Independent
security-reviewerpass over the diff (commitc465fafplus follow-up §1/§2 fixes in this branch): no blockers. Findings, all non-blockers:Gonearm skipped the group kill its own log claims (orphaned worker survives uncapped) — fixed here (sigkill_tree(pid)in theis_gonearm, ESRCH-safe per its contract).map_errsites (drain read, enable read-back) — fixed here (with_context).drain_to_leafswallows permanent denials as exited-mid-drain (burns the 5 s budget, still loud) — declined for this PR: boot-timing behavior change needing its own test; tracked as follow-up.Coordination: F2 runs in parallel; this branch touches
cgroups.rsonly for the boot-enable path plus the notes above. If F2's root cause lands in the same file for the same reason I will say so; no shared edits so far.