Monorepo management for opam overlays
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

monopam: fix fd leak in push — scope per-package switch

Git.Repository.open_repo acquires file descriptors. When 168 packages
share one Eio switch, all fds stay open until the switch exits.

Fix: wrap each per-package push in its own Eio.Switch.run.

Also adds push-stress.t: push 50 packages to catch fd leaks.

+87 -2
+9 -2
lib/push.ml
··· 384 384 let name = Package.subtree_prefix pkg in 385 385 update_progress name; 386 386 Log.debug (fun m -> m "Subtree push %s" name); 387 - match one ~sw ~proc ~fs ~config ~sources ~clean ~force pkg with 387 + match 388 + Eio.Switch.run @@ fun pkg_sw -> 389 + one ~sw:pkg_sw ~proc ~fs ~config ~sources ~clean ~force pkg 390 + with 388 391 | Ok Pushed -> loop (pkg :: pushed_repos) missing rest 389 392 | Ok Skipped -> loop pushed_repos missing rest 390 393 | Ok (Clone_failed url) -> ··· 410 413 let fetch_url = resolve_fetch_url ~sources pkg in 411 414 let push_url = Ctx.url_to_push_url ~knot fetch_url in 412 415 Log.info (fun m -> m "Pushing %s to %s" name push_url); 413 - let repo = Git.Repository.open_repo ~sw ~fs checkout_dir in 416 + (* Scope per-package: open_repo acquires fds that must be released 417 + before moving to the next package. Without this, 168 repos × 418 + multiple fds exhausts the process fd limit. *) 419 + Eio.Switch.run @@ fun pkg_sw -> 420 + let repo = Git.Repository.open_repo ~sw:pkg_sw ~fs checkout_dir in 414 421 (match 415 422 Git.Repository.ensure_remote repo ~name:"origin" ~url:fetch_url 416 423 with
+78
test/push-stress.t/run.t
··· 1 + Push stress test — many packages to catch fd leaks 2 + =================================================== 3 + 4 + Push 50 packages to verify file descriptors are properly closed between 5 + packages. With 50 repos × multiple git commands, this exceeds typical fd 6 + limits if handles leak. 7 + 8 + Setup 9 + ----- 10 + 11 + $ export NO_COLOR=1 12 + $ export GIT_AUTHOR_NAME="Stress" 13 + $ export GIT_AUTHOR_EMAIL="stress@test.com" 14 + $ export GIT_AUTHOR_DATE="2025-01-01T00:00:00+00:00" 15 + $ export GIT_COMMITTER_NAME="Stress" 16 + $ export GIT_COMMITTER_EMAIL="stress@test.com" 17 + $ export GIT_COMMITTER_DATE="2025-01-01T00:00:00+00:00" 18 + $ export HOME="$PWD/home" 19 + $ mkdir -p "$HOME" 20 + 21 + Create workspace with 50 packages: 22 + 23 + $ WS="$PWD/stress-workspace" 24 + $ mkdir -p "$WS/mono" "$WS/src" 25 + $ cd "$WS/mono" 26 + $ git init -q -b main 27 + 28 + Generate 50 packages, each with a dune-project and lib/dune: 29 + 30 + $ for i in $(seq 1 50); do 31 + > name="pkg-$(printf '%03d' $i)" 32 + > mkdir -p "ocaml-$name/lib" 33 + > cat > "ocaml-$name/dune-project" << EOFP 34 + > (lang dune 3.17) 35 + > (name $name) 36 + > (generate_opam_files true) 37 + > (package (name $name) (depends (ocaml (>= 5.2)))) 38 + > EOFP 39 + > cat > "ocaml-$name/lib/dune" << EOFD 40 + > (library (name $name) (public_name $name)) 41 + > EOFD 42 + > echo "let v = $i" > "ocaml-$name/lib/$name.ml" 43 + > done 44 + 45 + Create upstream repos for all 50: 46 + 47 + $ for i in $(seq 1 50); do 48 + > name="pkg-$(printf '%03d' $i)" 49 + > git init -q -b main "$WS/src/ocaml-$name" 50 + > done 51 + 52 + Set up monopam config: 53 + 54 + $ cat > monopam.toml << EOF 55 + > [monopam] 56 + > src_dir = "$WS/src" 57 + > EOF 58 + 59 + Initial commit: 60 + 61 + $ git add . && git commit -q -m "Add 50 packages" 62 + 63 + Push all 50 — this is the stress test. If fd handles leak, this will 64 + fail with "Too many open files" around package 30-40. 65 + 66 + $ monopam push 2>&1 | tail -1 67 + ✓ Changes pushed to your remotes. (*) 68 + 69 + Verify a few packages made it: 70 + 71 + $ cd "$WS/src/ocaml-pkg-001" && git log --format="%s" | head -1 && cd "$WS/mono" 72 + Add 50 packages 73 + 74 + $ cd "$WS/src/ocaml-pkg-025" && git log --format="%s" | head -1 && cd "$WS/mono" 75 + Add 50 packages 76 + 77 + $ cd "$WS/src/ocaml-pkg-050" && git log --format="%s" | head -1 && cd "$WS/mono" 78 + Add 50 packages