Live data from Hacker News

The case of a leaky goroutine

brainbaking.com

51–60 of 87 posts

Re: The case of a leaky goroutine

#51
post #17

If Go allowed something like "handle = go foo()", goroutine could be automatically terminated when handle goes out of scope or becomes dead and is garbage collected. You can also use handle to cancel a goroutine etc. Go designers specifically avoided this model (of having a goroutine "id") for reasons I don't remember any more (may be to avoid making them heavier weight?) but this would be one way to stop leaky gorou…

The convention of passing ctx does almost the same thing though. Make a new context.WitCancel and pass it to the goroutine.

It just requires programmer cooperation, but as long as you pass ctx all through the stack down and handle err on the way back, it is not often you deal with it explicitly.

Re: The case of a leaky goroutine

#52
post #32

Earlier quoted context omitted.

So the idea is to randomly take down the program when the GC runs?

Let me try to explain. The current way (which must continue working the same way even if the language is changed) does not allow you to distinguish a goroutine that should terminate but hasn't due to some bug versus a goroutine that can legitimately run for a long time. Making the goroutine "id" explicit can allow you distinguish the two cases. Store the id in some global or long lived variable or array if you want t…

The Go way is to pass the long running goroutines a different ctx from the short running.

Or even one ctx per goroutine and cancel them dynamically according to whatever logic.

Re: The case of a leaky goroutine

#53

I'm gonna be that guy. The old man yelling at cloud. I don't get all this high level crap. Coroutines, goroutines, fibers, async/await. It's supposed to make concurrency easy and safe. But I just fail to build a working mental model for it. I get the rough idea, but every time there's an await I wonder where execution might jump next. And then you read stuff like this, how these super high level comfortable languages…

A goroutine is practically just a thread

Re: The case of a leaky goroutine

#54
post #2

It's a pity Go didn't have structured concurrency: https://vorpus.org/blog/notes-on-structured-concurrency-or-g... There's a library for it: https://github.com/sourcegraph/conc But this goes to one of the things I've been kind of banging on about languages, which is that if it's not in the language, or at least the standard library right at the beginning, sometimes it almost might as well not exist. Sometimes a new l…

Structure concurrency is to concurrency what structured program control are to program control. I.e. unstructured concurrency is like GOTOs. Not necessarily wrong, but certainly nerve-wracking.

That's a lovely analogy, and I concur :o)

Re: The case of a leaky goroutine

#55
post #41
post #37

Earlier quoted context omitted.

A quote from your link: > programmers are strongly encouraged to use appropriate synchronization to avoid data races Any time you need to "encourage" programmers to do the right thing, you have already failed in your language design. And I think OP agrees with me here. OP says "static checking of correct synchronization" which is irresponsibly absent from Go.

So it's absent from every language but Rust?

Javascript is even better, by not having multithreading at all. I am not joking, Go is much worse than Java and C#, but Javascript and Rust are the only mainstream languages where I've seen non-experts reliably write correct concurrent code. Maybe it's true for langauges like Elixir as well, but I haven't tried.

Re: The case of a leaky goroutine

#56
post #41
post #37

Earlier quoted context omitted.

A quote from your link: > programmers are strongly encouraged to use appropriate synchronization to avoid data races Any time you need to "encourage" programmers to do the right thing, you have already failed in your language design. And I think OP agrees with me here. OP says "static checking of correct synchronization" which is irresponsibly absent from Go.

So it's absent from every language but Rust?

Alternatively a language can choose not to expose anything low level enough to cause synchronization issues and only make available high level APIs that are correct by construction.

Go does neither. That's why there's this thread on HN that I bookmarked: https://news.ycombinator.com/item?id=31698503

Re: The case of a leaky goroutine

#57
post #42

I wish Go recorded the timestamp of goroutine and let you access them. An app I work on recently had a bug where goroutines would slowly build up over time. Turns out the bug is in the Growthbook SDK [1]. We can monitor the number of goroutines, but having a large number of goroutines waiting in the location that gets stuck is normal — we can only see such a problem over multiple days, in that the minimum value slowl…

Language support would be great, but could you add logs that record the creating and destruction of goroutines, giving them a unique UUID so you can track which one's haven't exited? Edit: Also, maybe the tool at this comment could've helped you? https://news.ycombinator.com/item?id=39817775

How would one log that? In our case the bug was in a goroutine started by a third-party library, so we don't have control over when it starts or exits.

I don't think Goleak would have helped here, because I believe it doesn't support concurrency. It's really designed to run in tests, not in production. It parses and searches stack traces, so it's not going to be performant.

Re: The case of a leaky goroutine

#58
post #42

Earlier quoted context omitted.

Language support would be great, but could you add logs that record the creating and destruction of goroutines, giving them a unique UUID so you can track which one's haven't exited? Edit: Also, maybe the tool at this comment could've helped you? https://news.ycombinator.com/item?id=39817775

How would one log that? In our case the bug was in a goroutine started by a third-party library, so we don't have control over when it starts or exits. I don't think Goleak would have helped here, because I believe it doesn't support concurrency. It's really designed to run in tests, not in production. It parses and searches stack traces, so it's not going to be performant.

I guess you’d need to fork the third-party library (or, if you can build locally, just yolo and chmod +w) and inject something to track those goroutine metrics. But I suppose by the time you’re doing that, you already know where the problem is.

Re: The case of a leaky goroutine

#59

Earlier quoted context omitted.

Uber also made something called fx, which is fantastic. You don't have to use it, but when you do, it helps ensure that you organize your code in a way that becomes very easily testable. It enforces a modular approach to composing together golang services. Being more easily testable helps prevent bugs, like these leaky goroutines.

`fx` is mostly just Dependency Injection in Go which has been a thing in Java forever. I'm curious though, when do you reach for `fx` in a non-industrial project and when do you not? I still use the same patterns of separating out the implementation from the interface but I've been wiring in the dependencies by hand. I'm curious if folks reach for `fx` immediately or if it's something that requires thought to add. Th…

I still wire things up by hand: pass concrete parameters, use defer foo.Close(), use context.Context for signaling close. It’s super obvious how stuff works. In a 200k line code base, there are maybe 2 components where I haven’t been able to simplify the shutdown sequence to my satisfaction, but I doubt a framework would add clarity there.

Maybe I just don’t know what I’m missing.

Re: The case of a leaky goroutine

#60

The explanation of the issue in ToDoneInterface really is not clear to me because of this: > The defer close() seems to close well, but it’s on the wrong channel. The `done` input channel is supposed to be closed by a caller, and the goroutine is closing the output channel, surely that's the point? Now from what I know of go channels and understand of the code involved, the `done` channel may never get closed by the…

At this point, I think done chan should be an anti-pattern. Just use context for coordination and cancellation.
Post reply on HN