fix: singleflight shares the winning caller's context with all waiters #456
Labels
No labels
backend
bug
chore
duplication
effort:complex
effort:medium
effort:trivial
enhancement
frontend
fullstack
priority:high
ready-for-agent
refactor
security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
dries/ocman#456
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Code-quality audit (P2).
internal/hostsvc/local/host.go:290-292(EnsureProjectOpencode) and:310-312(RestartProjectOpencode).Why it matters
singleflightcollapses concurrent ensure calls for one repo root, but the shared body captures the winning caller's context. If that caller's HTTP request is cancelled mid-launch,ensureLocked->waitForProbereturnsctx.Err(), and every coalesced waiter — whose own contexts are still live — receives a spurious failure for a launch that may well have succeeded. The user-visible symptom is a "failed to launch opencode" error on a project that is in fact running, and it only reproduces under concurrency, so it will be reported as flaky rather than diagnosed.The same applies to
RestartProjectOpencode, which shares the singleflight key.Suggested fix
Decouple the shared body from any single caller's lifetime:
Give the shared context its own bounded deadline so an abandoned launch cannot run forever.
Not applicable
The audit also flagged
internal/platforms/opencode/httpcache.go:161-181as the same pattern. It is not —getOrFetchusescontext.Background()and itsfetcherclosures (client.go:70,149,http.go:94) callrawGet, which takes no context. No caller context is captured there.Acceptance criteria
EnsureProjectOpencodecalls, cancel the first — the second still gets a successful result. Must fail on currentmain.Effort: S.