Is there a generalized pattern for dealing with exceptions in go blocks? As I see it, I have three options: 1. carefully bubble up exceptions until I leave the async context then throw it 2. Use an exception chan that I pass around my async architecture, listen to it at the top, and handle when I get something out of it 3. Catch exceptions near the call site of exception-throwing functions, convert them into non-exceptional but meaningful values, and write code on every go block in the architecture that accounts for these values I would be glad to write up a writeup on this with some input because I think it's a murky but useful concept but I don't think I have all the answers here
how you do this is going to depend a lot on what "style" you are using for async operations:
1. future like
2. service like
future like implies you want something to have as similar as possible behavior to just calling a function, so @(future (+ 1 2)) will call + on another thread and block waiting for the result and rethrow exceptions. a function call shares the stack so people naturally want exceptions to "bubble" across contexts here.
server like is as if some component was a separate long running service that you interact with by exchanging messages. the model here doesn't share a stack, so exceptions bubbling makes less sense, a service would report its own errors (logging. metrics or whatever) and maybe send back an error message.
I like to see the service like pattern, I think it plays to core.async's strengths, but it seems like the future like pattern is more common
there are libraries that add exception bubbling on top of channel operations https://github.com/k13labs/futurama/ for example
I think my streaming work is more service oriented. I'm trading messages between long-running processes that execute actions. I pass a stream to the SSE endpoint
I broadly agree with @hiredmanβs take on this https://clojure.github.io/core.async/flow.html, the upcoming extension to core.async (not yet out) is explicitly the service context and flows have a standard exception channel that all services forward exceptions to (if they can't/don't handle them themselves) - that's kind of set up for you when you create a flow
Is there something in flow about preventing/debugging deadlocks?
Depends what you mean about deadlocks. Flow will let you ping processes to see their state and monitor the channels between them, so that would probably help debug if things are somehow blocked
I guess typically, you're meant to try/catch inside the Go block. That's really the answer here. Now, how to handle the catch inside the Go, is what others have said.
Given the latest changes, what should I expect would be the handling for JDK 21? Will go blocks now use vthreads?
I mean, if you are doing compute that is going to take 30+ seconds, it locks up the thread during that whole time. You were not supposed to do that on the go pool before, and similarly you're not supposed to use a vthread in those cases because it pins the OS thread.
I see that it seems like it defaults to :io optimized, but now it looks like all are just a cached thread pool. So does the go block now just use an unbounded thread pool same as thread ?
And when exactly would you receot :compute and :mixed ? Does thread now become :compute, io-thread becomes :io and go becomes :mixed ?
We have not yet made any changes to use vthreads, those are still coming. In the current release, all pools are cached thread pools. thread has been available for :mixed and will continue to have those semantics. We are not adding any construct for compute - just use normal threads (the :compute pool will be used for async flow).
I see, compute is for light compute like async flow, I assumed it was for heavy compute.
I dont know what distinction is supposed to mean
Compute as hard as you want :)
The point is youβre bound to a platform thread and not doing blocking io