top | item 37939369

Durable Coroutines for Go

69 points| greenSunglass | 2 years ago |github.com | reply

35 comments

order
[+] kitd|2 years ago|reply
Impressive work. This reminds me of an experimental JVM that was around about 20 years ago from a group called Velare. They could do durable coroutines just like this, but by throwing a particular exception rather than using `yield`. They also could return a value and allow the coro to be resumed with a value.

edit: here it is https://dl.acm.org/doi/10.1145/949344.949362 It was a built-in bytecode interpreter with suspendable threads

It changed significantly how you think about cooperating processes. It was like watching 2 processes have a conversation with each other without the controlling code getting involved in any way. Also, if you save the coros after each call, you can get step-by-step replays which are very helpful in debugging.

[+] skybrian|2 years ago|reply
The saved coroutine state is an implicitly-defined data structure and they don’t support any kind of migration when the code changes, so the durability will be quite limited.
[+] achille-roussel|2 years ago|reply
You’re correct that code migration is still a to do, we’ll be exploring how to do that in the future.

As you can imagine there are a lot of challenges with it, but ideas are welcome!

[+] tedunangst|2 years ago|reply
[+] deadfa11|2 years ago|reply
Eh, this one seems a reasonable trade-off to me at this point. If you try to handle every potential issue upfront, you'll never release, and this seems entirely fixable down the road. Persistent coroutines is a pretty challenging area to explore (tried this with Lua in a previous venture), and this seems a pretty minor point in that overall complexity. I'm curious, what else have you come across that justifies the "really brittle" conclusion?
[+] achille-roussel|2 years ago|reply
Yes, we planned to update the coroc compiler to generate this value instead of having it hardcoded so it will be more future proof.
[+] chris6f|2 years ago|reply
A source-to-source compiler, and a library that bundles runtime implementation details, was the path of least resistance. We'd love to integrate this with the Go compiler (`go build -durable`, vs. `coroc && go build -tags durable`), but the compiler is closed and maintaining a fork of Go is not feasible for us at this time.
[+] gsora|2 years ago|reply
Why?
[+] hnav|2 years ago|reply
I feel like once serializing god-knows-what state across program invocations is a requirement, I'd much prefer writing this explicitly so I can at least have a chance of debugging it

  type Task struct {
    I int
  }

  func (t *Task) Next() (bool, int) {
    if t.I < 3 {
      return t.I++, true
    }
    return 0, false
  }

  var t Task
  t.Next()
  json.Marshal(t)
[+] chris6f|2 years ago|reply
It's a good point, but the entire program would have to be written this way (you can't use the standard library, or any other dependencies).

What if there were tools to inspect and debug the coroutine state? That's an area we're exploring now.

[+] zellyn|2 years ago|reply
A succinct explanation of how durable coroutines are different (in practice) from Temporal would be useful.
[+] crabmusket|2 years ago|reply
Temporal seems like durable coroutines + distributed execution + some abstractions to help avoid impurity?
[+] ctvo|2 years ago|reply
From a user perspective: how is this different than what Temporal provides?
[+] born-jre|2 years ago|reply
nice, but i had a separate idea.

what if u build a wasm runtime that can save and restore memory with and execution states, sounds much more full proof. or i might have misunderstood this idea :D.

[+] achille-roussel|2 years ago|reply
We tried that actually, and it can work well but you make a different set of trade offs. For example, you can’t get the granularity that durable coroutines give you, you’re bound to snapshot and restore the entire state of the application.

WASM is also not as mature of a tech for server side systems, a lot is left to figure out so the type of applications that you can build with it remains limited.

I’d be excited to see support for something like this get built tho!

[+] infogulch|2 years ago|reply
Why are channels insufficient for this use case?
[+] lelandbatey|2 years ago|reply
A goroutine-and-channel implementation may not work as well for the "durable" goal, while this implementation is focused on being able to perfectly serialize the state of the Coroutines in order to resume them elsewhere (durable).
[+] varispeed|2 years ago|reply
Why not fork Go and implement this directly? (and also removing any telemetry Google might have installed while at it...)
[+] pryz|2 years ago|reply
Forking Go means that you have to maintain a copy. That's a lot of work. Instead, the idea is to implement coroutine as a library and work toward an integration with the Go stblib.
[+] zelly|2 years ago|reply
This is the kind of thing you can only trust the compiler to do. And we already have goroutines.
[+] rweir|2 years ago|reply
very cool - looking forward to seeing the rest.