Repository navigation
fix(worktree-care): squash checks answer under load and in big repos (private test lock, RLIMIT_DATA) - #29729
Conversation
…t runs a merge-tree care_a_squash_merged_branch_is_finished failed in the full Linux suite at d3cf70b (finished = None after 0.146 s) and passed alone. The scan helper uses the library's in-memory memo, whose merge-tree lock is the host-wide $HOME/.cmux/worktree-archive/.merge-tree.lock. When any other process holds it (another test's merge-tree, the person's cleanup daemon), the check is "busy, unknown" and the branch reads as unfinished. This test now holds that host lock through the scan, which makes the failure deterministic. Red on this commit; the fix follows.
…n their own folder The care_lifecycle scan helper used inventory::scan, whose in-memory memo takes the host-wide merge-tree lock in $HOME/.cmux/worktree-archive. That lock is nonblocking by design: busy means "unknown, ask again next pass". In the full suite every care_lifecycle test process (and the person's own cleanup daemon) shares it, so a squash check that met another merge-tree returned no answer and care_a_squash_merged_branch_is_finished saw finished = None (feat d3cf70b, job 01ac6d0216103926556037f5). The helper now scans with a per-process FinishedMemo in the test's own scratch folder, so its lock is private, the way pass::run already keeps its lock beside its store. The product behavior does not change.
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Shipped in:
|
…ssary qualification) The fleet macOS run of 0a877e3 printed 9 unused_qualifications warnings: the new `use std::sync::{Arc, LazyLock}` made the existing std::sync::Arc paths redundant. Behavior is unchanged.
…ets a squash answer
Second cause of the care_a_squash_merged_branch_is_finished base red,
measured with diagnostics on the Linux runner (job fec1f9b5): the `git`
on PATH there is /usr/local/bin/git, FastActions' gitwrapper
(github.com/FastActions/fa/agent/gitwrapper, go1.27.3) in front of
/usr/lib/git-core/git. merge-tree runs under a 768 MiB RLIMIT_AS, and
the Go runtime reserves address space it never uses; under load it
starts more threads and dies at init ("runtime: out of memory: cannot
allocate 4194304-byte block (3932160 in use)", exit 2), so the squash
check has no answer and the branch reads as unfinished (2 of 80 copies
per full suite, jobs 76cc4bf6, 7925207d, d625e10d, fec1f9b5).
On a person's Linux machine the same holds for any such git (a Go
wrapper, or Git's own read-only pack mappings past 768 MiB in a big
repository): the check is unknown on every pass, so a squash-merged
worktree is never seen as finished.
The new test puts a `git` on PATH whose merge-tree first maps 1 GiB of
read-only anonymous memory (reserved, never written), then runs native
Git: red under RLIMIT_AS. A second test holds the cap: 1 GiB of
writable memory is still refused, and the check stays unknown.
…LIMIT_AS RLIMIT_AS counts address space a child only reserves: a Go git wrapper's runtime (FastActions' gitwrapper on the hosted Linux runners) and Git's read-only pack mappings. Under the 768 MiB cap such a git died at random with almost nothing in use, and worktree care read the squash check as unknown. RLIMIT_DATA counts the writable memory the child allocates (brk and private writable mappings), which is what #27107 meant to bound; 1 GiB of writable memory is still refused. The limit is renamed memory_limit in cmux-git and cmux-worktree-care. macOS is unchanged (no limit there).
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Review (claude-review-subagent): SHIP at 9326144 I read the diff 1. RLIMIT_DATA in place of RLIMIT_AS (cmux-git/src/run.rs:316-339): correct.
2. care_lifecycle helper (care_lifecycle.rs:72-85, 251-266): correct.
3. care_bounds Linux tests (care_bounds.rs:254-330): correct, and red/green is shown.
Nits (they do not block):
|
Summary
Fixes the base red
cmux-worktree-care::care_lifecycle care_a_squash_merged_branch_is_finished. It failed in the full Linux suite at featd3cf70bb(job01ac6d0216103926556037f5, hq run 41232603829:care_lifecycle.rs:261panicked withNoneafter 0.146 s) and passed when it ran alone. The flake had two causes. The second is a product defect.Cause 1 (test isolation). The test's
scanhelper calledinventory::scan, whose in-memory memo takes the host-wide merge-tree lock$HOME/.cmux/worktree-archive/.merge-tree.lock. That lock does not wait, by design (#27107): when it is busy, the check is "unknown, ask again next pass". Allcare_lifecycletest processes start in the same 100 ms and use that lock. The helper now scans with oneFinishedMemoper test process in its own scratch folder, so its lock is private, aspass::runkeeps its lock beside its store.Cause 2 (product).
merge-treeran under a 768 MiBRLIMIT_AS(Linux only, from #27107).RLIMIT_AScounts address space that a process only reserves. Measured:giton PATH is/usr/local/bin/git, FastActions'gitwrapper(go version -m: go1.27.3,github.com/FastActions/fa/agent/gitwrapper), in front of/usr/lib/git-core/git. Its Go runtime reserves address space and, under load, died at start withruntime: out of memory: cannot allocate 4194304-byte block (3932160 in use), exit 2 (diagnostic jobs76cc4bf6,7925207d,d625e10d,fec1f9b5: 2 of 80 copies in each full suite). git-lfs is configured in the runner's/etc/gitconfig, butmerge-tree --write-treeruns no filters, and the process that threw is the wrapper./usr/lib/git-core/git merge-tree --write-tree --no-messages HEAD~50 HEADunderulimit -v 786432exits 128 withpackfile ... cannot be mapped ... Cannot allocate memory. Underulimit -d 786432it exits 0. Git maps pack windows of up to 1 GiB read-only.Product impact. On Linux, when the
giton PATH reserves address space that it does not use (a wrapper written in Go, or C git in a repository with a pack bigger than about 768 MiB, such as cmux), the squash check gets no answer. A squash-merged worktree then never shows as finished, and worktree care never cleans it up. The check is not remembered, so each pass tries again, but with a big pack it fails each time. A Linux or Cloud machine with a cmux checkout gets this. Branches merged as ancestors are not affected (merge-base --is-ancestorruns without the limit).Fix.
cmux-gitsetsRLIMIT_DATAinstead ofRLIMIT_AS(the parameter is renamedmemory_limit).RLIMIT_DATAcounts the writable memory that the child allocates (brk and private writable mappings). It does not count unused reservations or read-only pack mappings. Thus it still bounds merge-tree's memory, as #27107 intended.#[cfg(target_os = "linux")]). macOS is unchanged and has no limit.a_merge_tree_that_writes_past_the_memory_cap_has_no_answermaps 1 GiB of writable memory in merge-tree. The limit refuses it, and the check stays unknown.cmux-git/src/run.rs, onlyrun_git_scratch_objects_within_limitedpasses a limit torun. Its only caller iscmux-worktree-caregit::read_scratch_objects, whose only caller isfinish()formerge-tree. All otherruncalls (run_git_within, the windowed and records readers,run_git_scratch_objects_within) passNone, so they do not change.@teamleaderleo: this changes the limit that you added in #27107 (
4b4cea908645) fromRLIMIT_AStoRLIMIT_DATA, for the reasons above. The 768 MiB value, the deadline and the locks do not change.cmux-worktree-careandcmux-githave no row in OWNERS.md.Testing
All runs use
scripts/ci/cmux-tui-rust-check.sh test <filter>on the fleet. I read each log and checked that each one compiledcmux-gitandcmux-worktree-careat its ref. The gate reruns a failed test alone and then passes it as FLAKY with exit 0. Thus I count a pass only from the first full run, never from the exit code.Focused commands:
--arg test --arg care_a_squash_merged_branch_is_finished(cause 1) and--arg test --arg a_git_that_reserves_unused_address_space(cause 2).0cebe1552c53e4efNone), in the full run and alonedadb7d6fa18ea61aa_git_that_reserves_unused_address_space, Linuxbf8e7ddd3d509f63+ 20 copies (scratch)care_, Linux:261withNone. The gate rerun passed them as FLAKY, exit 03f7652d593261448care_, Linuxa_*tests; their evidence is the full suites below)7d07594593261448care_, macOS 26 (aws-m4pro-4)9dcfd3d793261448-D warnings, Linux94e96255,59079680,b40a7a31,fe07c0d7,282a225f(infra rerun of4d43fd11)11e4364b)a_git_that_reserves_unused_address_space_still_gets_an_answeranda_merge_tree_that_writes_past_the_memory_cap_has_no_answerpassed on the first run, 0 FLAKYBefore the product fix (head
0a877e3f+ 20 copies), 5 full suites showed 2 failures in 105 copies (181afddd,ca4719fc). This found cause 2.Every full suite also shows the same 22
acpmuxfailures (cli::SIGABRT, plusdaemon_memoryandephemeral_runin some runs) and oncecmux-tui::cli apps_store. They are base reds: the same 22 failures appear on0a877e3f, which changed only a test file incmux-worktree-care.Review receipt: #29729 (comment) (SHIP at
932614489c3f). Its nits are not fixed here: the per-processcare-lifecycle-finished-*temp folder is not removed, and the shims' PATH/HOME restore is skipped if a scan panics (the file's existing pattern).Observed / Not run
Observed: the runs in the table, and the
ulimit -v/ulimit -dmeasurement of native git in this checkout (above).Not run / known limits:
4d43fd11(load run 1): NOT RUN (stale warm target, hq#5737): its warm cargo target did not recompilecmux-worktree-careand ran a stale binary (13787 tests, base line numbers). I reran it once (282a225f) and reported the fault to the lead.cargo test(one process, many threads), tests can still collide on the in-processMERGE_TREEmutex ininventory.rs, which also does not wait. CI does not see this, because nextest runs each test in its own process. This PR does not change that.host_merge_tree_lockuses the real$HOME/.cmux/worktree-archive/.merge-tree.lockby design: it holds the lock that the product uses. If a person's cleanup daemon runs a pass at the same moment, that pass has no answer for one check and asks again in its next pass.python3on the host (they fail, not skip, without it). TheRLIMIT_DATAbehavior on a real Cloud machine is not tested separately. The measurement above used this VM's kernel.Performance
No perf numbers: the bound value and the code path do not change. Only the kind of limit changes. The perf harness
finished_scan_perf_harnesswas not run.Changelog
Fixed: On Linux, worktree cleanup now finds squash-merged worktrees in large repositories and with git wrappers on PATH