Repository navigation
fix(pipeline,mcp,daemon): fail an over-budget index attempt whole with a named reason (#1997, #832) - #2112
Merged
Conversation
…t-overshooting The extraction memory gate was advisory: when a full back-pressure cycle ended still over budget it latched "futile" and kept admitting files, so a host that could not hold the graph ran on until the kernel killed the worker (#1997, #832) and the supervisor then quarantined innocent files. Now the worker that latches futility runs ONE confirmation cycle (collect + bounded naps). If resident memory drained, the gate re-arms as before. If it is still over budget, the attempt fails whole: every worker stops pulling, extract returns the new CBM_PIPELINE_ABORT_OVER_BUDGET, the orchestrator discards the staging DB on that non-zero code like any other, and the previously serving generation is left byte-identical. The verdict is a plain return value so it travels as a clean-exit worker response, never a kill. The abort flag is its own atomic, not the shared cancel token, which belongs to client cancellation. The abort/persist codes move to pipeline.h alongside the new one (callers legitimately distinguish them); FORCE_FULL_REINDEX stays internal. Tests: the futility test now asserts the new code (the confirmation adds one cycle to its bound), and a new test proves fail-whole preserves the live DB byte-for-byte, keeps its node count, and leaves no stage residue. Refs #1997 #832 #1973 #1864 Co-Authored-By: Claude Fable 5.1 <[email protected]> Signed-off-by: Martin Vogel <[email protected]>
index_repository rendered every non-zero pipeline code as "Pipeline failed. Check repo_path exists and contains source files", which sent people debugging a path that was fine (#2020). Three outcomes now say what actually happened: - CBM_PIPELINE_ABORT_OVER_BUDGET (#1997, #832): status "error" with reason "over_memory_budget", previous_index "preserved", the budget and peak RSS in MB, and a hint naming CBM_MEM_BUDGET_MB / CBM_WORKERS / excludes. The handler logs index.abort reason=over_memory_budget. The CLI prints the same JSON and exits 1; a supervised worker still exits 0 with the response written, so the daemon passes it through verbatim instead of entering skip-and-continue recovery. - CBM_PIPELINE_ABORT_PRESERVE_DB: status "aborted_previous_preserved" (retry; the previous index still serves). - CBM_PIPELINE_PERSIST_FAILED: status "persist_failed" (disk/permissions). The incremental test helper goes fail-closed on error responses so a transient pre-publication abort surfaces once at the source instead of cascading into dozens of downstream count failures. Test: an in-process index over 64 files at a 1 MiB budget seam returns the named error, leaves the project DB the same size on disk, and index_status still reports the previous node count. Refs #2020 #1997 #832 Co-Authored-By: Claude Fable 5.1 <[email protected]> Signed-off-by: Martin Vogel <[email protected]>
…e job limit The daemon handed every index worker aggregate/physical_job_limit (a fixed quarter by default) regardless of how many jobs were running, so a lone worker on a host like #1864 was capped at a quarter of the memory the daemon itself was allowed — and with the budget now failing whole (decision A) that fixed slice would have turned an idle host into a guaranteed over_memory_budget. Decision 2 (#1997, #832): the slice is decided at spawn time as aggregate / jobs active right then (the job being spawned included), so a lone worker may use the whole aggregate and concurrent jobs split it. The explicit CBM_MEM_BUDGET_MB override keeps its precedence inside the worker (bf46b91); the sub-byte-per-slot guard is unchanged. The spawn line logs project, active_jobs, aggregate_mb and slice_mb so a fail-whole verdict can be read against the budget the worker really had. Tests: sequenced admission proves first=aggregate, second=aggregate/2; the racing 4-way admission test accepts any aggregate/1..4 slice (the divisor is scheduling-dependent there); a scripted CLEAN worker returning the over_memory_budget error response is passed through verbatim on the first attempt — no recovery loop, no quarantine. Refs #1997 #832 #1864 Co-Authored-By: Claude Fable 5.1 <[email protected]> Signed-off-by: Martin Vogel <[email protected]>
This was referenced Sep 9, 2026
2 of 4 tasks
2 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The indexing memory budget stops being advisory (decision A). Addresses #1997 and #832, and the OOM half of #1973/#1864.
Today the budget is enforced only inside parallel extract: after 40×3 ms backpressure spins still over budget, it latches
bp_futile, logssoft_overshoot, and keeps admitting until the kernel OOM-kills the worker and the supervisor blames files. Now:CBM_PIPELINE_ABORT_OVER_BUDGET(its own atomic flag, not the shared cancel token). Staging is discarded through the existing rc≠0 path, so the previously serving index is byte-identical and no partial graph is published. The worker exits 0 with astatus:"error"response, so the daemon passes it through in one attempt with no recovery loop or file quarantine.error#2020).index_repositoryrendersreason:"over_memory_budget",previous_index:"preserved",budget_mb,peak_rss_mb, and a hint namingCBM_MEM_BUDGET_MB; distinctaborted_previous_preserved/persist_failedstatuses replace the generic "Pipeline failed".Local verification (macOS):
daemon_application55,pipeline276,mcp314 (+4 skips),mem53,index_supervisor8, all green; Colima CI-lint (cppcheck + clang-format-20) passed; RED→GREEN→RED-on-revert on the new tests. CLI on the final binary prints the exact JSON, the DB is byte-identical after a fail-whole, andindex_statusstill serves the old graph. Windows PR #2030 (Job Object cap) is complementary and must sit above this cooperative gate.🤖 Generated with Claude Code