fix: cap git status forks process-wide #526

Merged
dries merged 3 commits from fix/git-status-chat-blocking into main 2026-08-20 09:26:24 +02:00
Owner

Summary

  • Answering a permission prompt could hang until the sidebar's git status resolved: the 8-worker cap on git status lookups was per LookupMany call, so concurrent /api/git/info requests (several useGitInfo hooks mount at once) forked 3×8 git children and the burst stalled unrelated handlers.
  • Enforce the cap with one process-wide semaphore around the fetch in internal/git. Cache hits never wait; a caller whose context dies while queued gets a zero Info back without caching it (a cached zero would mark the dir "not a repo" for a full TTL).

Testing

  • New TestLookupMany_WorkerCapIsProcessWide fails on main (observed 24 concurrent fetches, want ≤ 8) and passes with the fix.
  • New TestCache_CancelledContextWhileWaitingForSlot covers the queued-then-cancelled path.
  • make test, make lint, go test -race ./internal/git/ all pass; internal/git coverage 83.5%.
## Summary - Answering a permission prompt could hang until the sidebar's git status resolved: the 8-worker cap on `git status` lookups was per `LookupMany` call, so concurrent `/api/git/info` requests (several `useGitInfo` hooks mount at once) forked 3×8 git children and the burst stalled unrelated handlers. - Enforce the cap with one process-wide semaphore around the fetch in `internal/git`. Cache hits never wait; a caller whose context dies while queued gets a zero Info back without caching it (a cached zero would mark the dir "not a repo" for a full TTL). ## Testing - New `TestLookupMany_WorkerCapIsProcessWide` fails on main (observed 24 concurrent fetches, want ≤ 8) and passes with the fix. - New `TestCache_CancelledContextWhileWaitingForSlot` covers the queued-then-cancelled path. - `make test`, `make lint`, `go test -race ./internal/git/` all pass; `internal/git` coverage 83.5%.
fix: cap git status forks process-wide
All checks were successful
CI / Frontend (pull_request) Successful in 7m48s
CI / Backend (pull_request) Successful in 10m32s
CI / Build (pull_request) Successful in 4m20s
CI / Coverage Results (pull_request) Successful in 20s
CI / Playwright E2E (pull_request) Successful in 7m25s
CI / Semantic Tag (pull_request) Successful in 6s
608d9e6699
The 8-worker cap on git status lookups was per LookupMany call, so
concurrent /api/git/info requests (the sidebar mounts several
useGitInfo hooks) could each bring their own pool. The combined fork
burst stalled unrelated handlers — answering a permission prompt hung
until the sidebar's git status resolved. Enforce the cap with one
process-wide semaphore around the fetch; cache hits never wait, and a
caller cancelled while queued gets a zero Info without poisoning the
cache.

Coverage ratchet: ✅ pass

Suite Baseline This PR Δ
frontend 71.22% 71.22% +0.00 ✅
go 81.30% 81.30% +0.00 ✅

Tolerance: -0.1%. Baseline stored on gh-pages.

<!-- coverage-ratchet --> ### Coverage ratchet: ✅ pass | Suite | Baseline | This PR | Δ | | |---|---|---|---|---| | frontend | 71.22% | 71.22% | +0.00 | ✅ | | go | 81.30% | 81.30% | +0.00 | ✅ | _Tolerance: -0.1%. Baseline stored on `gh-pages`._
fix: never cache git info fetched under a cancelled context
Some checks failed
CI / Frontend (pull_request) Successful in 7m28s
CI / Playwright E2E (pull_request) Has been cancelled
CI / Build (pull_request) Has been cancelled
CI / Backend (pull_request) Has been cancelled
CI / Coverage Results (pull_request) Has been cancelled
CI / Semantic Tag (pull_request) Has been cancelled
bd219314d2
The ctx.Done select case only protects the queued path: when a
semaphore slot is free and ctx is already cancelled, select picks a
ready case at random and can still run the fetch. git fails instantly
under a dead context, and the fabricated zero Info was cached, marking
the dir "not a repo" for a full 30s TTL. The frontend aborts git-info
requests on every dirs change, so this was a routine path.
fix: release git fetch slot on panic
All checks were successful
CI / Frontend (pull_request) Successful in 8m55s
CI / Playwright E2E (pull_request) Successful in 10m7s
CI / Backend (pull_request) Successful in 11m31s
CI / Build (pull_request) Successful in 6m38s
CI / Coverage Results (pull_request) Successful in 23s
CI / Semantic Tag (pull_request) Successful in 8s
bfc9c964c2
dries merged commit a1cd657748 into main 2026-08-20 09:26:24 +02:00
dries deleted branch fix/git-status-chat-blocking 2026-08-20 09:26:24 +02:00
Sign in to join this conversation.
No description provided.