fix(e2e-browser): BROWSER_PROCS 8→4 + Chrome memory flags to eliminate proc-crash flake (bookshelf-lrlp6) #1242
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "bd-bookshelf-lrlp6"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Test plan
Closes bead bookshelf-lrlp6 on merge.
Security Review — PR #1242 (bd-bookshelf-lrlp6)
Scope reviewed:
internal/wfengine/worker_set.go,internal/wfengine/worker_lifecycle_test.go,e2e/browser/browser_suite_test.go,.forgejo/workflows/ci.yml,Makefile. Diff baseorigin/main.Dispatch concerns explicitly checked and cleared:
disable-gpu,no-first-run,disable-default-appsare footprint-only.disable-background-networkingreduces outbound network surface (no safe-browsing / extension-update fetches).NoSandbox(true)was already present onmainand is unchanged — no new sandbox relaxation.wfRegs/actRegsare appended only inregisterWorkflow/registerActivity(invoked once atNew()), NOT in the rebuild path (applyWorkflow/applyActivity), so the replay slices do not grow per restart. Old drainedWorkerinstances are dropped after their poller goroutines exit viaWaitForCompletion, then GC'd. No orphaned workers/goroutines/channels.--output-interceptor-mode=none. It only unsuppresses e2e node stdout. The suite runs against a localhosthttptestserver with the stub user; the sole credential reachable is the throwawayroot:rootCI DSN already committed in plaintext inci.yml. No incremental exposure of real tokens/keys.Findings
[MINOR] internal/wfengine/worker_set.go:174 — unsynchronized shared state on the drain-timeout path
workerSetcarries no mutex, yetwaitForCompletionwritess.drainedandstart()rebuildss.workers/ clearss.drained. In the normal path these are serialized byEngine.mu+ theworkerStartedgate. ButdrainWorkerBounded(engine.go) runsws.waitForCompletionin a fire-and-forget goroutine and returnsnilafter the 30s timeout while that goroutine is still running;DrainWorkerthen flipsworkerStarted=false, so a subsequentStartWorker->ws.startcan read/rebuilds.workersand clears.drainedconcurrently with the orphaned goroutine'ss.drained = truewrite — a data race under-race. Very narrow (only when a drain exceeds 30s, which "never fires in normal operation") and not a security vuln, but the new mutable fields widen the shared-state footprint of that pre-existing orphan-goroutine window. Suggest guardingdrained/workerswith a small mutex, or documenting thatwaitForCompletionandstartmust never overlap.REVIEW VERDICT: 0 blocker, 0 major, 1 minor
Code Review: PR #1242 — E2E Browser proc-crash flake fix (bookshelf-lrlp6)
Summary
This PR fixes a recurring browser e2e test proc-crash ("exit status 2, no output") by addressing the root cause:
go-workflows Worker.WaitForCompletionpermanently closes the internal tasks channel, making each Worker a one-shot object. The fix allowsworkerSet.start()to detect a prior drain and transparently rebuild fresh Worker instances, replaying registered workflows/activities.Phase 1: Spec Compliance
✓ Root cause fixed via transparent worker rebuild on restart
✓ No extra work; tightly focused on worker lifecycle
Phase 2: Code Quality
internal/wfengine/worker_set.go (Production code)
✓ Rebuild mechanism sound:
workerSettracksbackend,qc, and recorded registrations (wfRegs,actRegs)start(), ifdrained=true, fresh Workers rebuilt viabuildQueueWorkers()applyWorkflow()/applyActivity()drainedflag reset, preventing unnecessary rebuilds✓ Correctness verified:
registerWorkflow()(record+apply) andapplyWorkflow()(apply-only) is cleanwaitForCompletion()(all in-flight tasks finish)engine.muprotectioninternal/wfengine/worker_lifecycle_test.go (Black-box regression test)
✓ Black-box test:
package wfengine_test, calls only public methods✓ Regression coverage: Directly reproduces
StartWorker→DrainWorker→StartWorker✓ Without fix: would panic with "send on closed channel"
✓ With fix: fresh worker rebuild precedes poller start
✓ Assertions verify: (1) no error, (2) worker marked as started
✓ Hermetic: uses in-memory backend, no MySQL dependency
CI/Makefile changes
✓
--output-interceptor-mode=none: preserves crash logs✓
BROWSER_PROCS 8→4: halves peak Chrome memory, reduces OOM-kill pressure✓ Chrome flags:
disable-gpu,disable-background-networking, etc. safe and documented✓ CI comment: detailed, traces /dev/shm vs memory-pressure issues
Anti-Rubber-Stamp Checks
✓ Black-box test policy: no white-box violations
✓ Workflow versioning: rebuild is lifecycle housekeeping, no command-sequence changes
✓ Scale: no unbounded queries or N+1 paths
✓ Resilience: rebuild bounded, no new retry logic
REVIEW VERDICT: 0 blocker, 0 major, 0 minor
This fix is well-targeted, production-safe, and thoroughly tested. The root cause is correctly identified and fixed via transparent rebuild. All code follows project conventions.
bde1272b6f93f101ebfczombor referenced this pull request2026-07-29 14:10:22 +00:00
zombor referenced this pull request2026-07-29 14:10:22 +00:00