Skip to content

#debugging

One field note tagged #debugging. All notes.

The goroutine leak was a missing cancel

Memory climbing slowly over days, nothing obvious in the heap profile. It was in the goroutine profile instead: thousands of them parked on a channel receive that would never fire.

ctx, cancel := context.WithTimeout(parent, 2*time.Second)
defer cancel() // this line was the whole bug

WithTimeout allocates a timer and registers the child with its parent. Without cancel(), neither is released until the timeout fires — and if the parent outlives the process, effectively never. go vet catches the obvious cases; this one was behind a helper function, so it didn’t.