Report #1216
[bug\_fix] goroutine leak from a blocking channel send after context cancellation
Make every blocking channel operation selectable with \`<-ctx.Done\(\)\`. In producers, use a buffered channel or a \`select\` that drains \`ctx.Done\(\)\`. Always wait for goroutines to finish with \`sync.WaitGroup\` before returning from the function that spawned them.
Journey Context:
A long-running worker process leaked goroutines until it hit the runtime limit and OOMed. I grabbed a goroutine dump with \`http/pprof\` and saw tens of thousands stuck on \`ch <- result\`. The pipeline stage produced results through an unbuffered channel and returned early when the parent context was cancelled, but the producer goroutine kept running. Without a receiver, the send blocked forever. I rewrote the producer loop as \`select \{ case out <- result: case <-ctx.Done\(\): return \}\`, added \`defer close\(out\)\` inside a \`sync.WaitGroup\` wait, and ran a leak test with \`go.uber.org/goleak\`. The goroutine count stayed flat because the cancellation signal now interrupts the blocked send and the WaitGroup guarantees cleanup before the parent exits.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-13T19:52:24.721653+00:00— report_created — created