> Another example is a server process that spaw...
# coroutines
v
Another example is a server process that spawns several children jobs and needs to supervise their execution, tracking their failures and restarting just those children jobs that had failed.
I cannot get my head around how to restart a children (say, an
actor
) from inside a
CoroutineExceptionHandler
šŸ˜ž What I really need is just restarting strategy a-la Akka.
Copy code
fun CoroutineScope.worker(ctx: CoroutineContext) = actor<Int>(ctx) {
    for (msg in channel) {
        println("got $msg")
        if (msg == 2) throw Exception("it's $msg and we are failing")
    }
}

GlobalScope.launch {
    val supervisor = SupervisorJob()
    var actor: SendChannel<Int>?

    val handler = CoroutineExceptionHandler { _, _ ->
        {
            actor = worker(coroutineContext + supervisor + handler) // error: handler is not defined
        }
    }

    actor = worker(coroutineContext + supervisor + handler)

    for (msg in 1..5)
        actor?.send(msg)
}
e
It is not hard:
1. Donā€™t pass
ctx
into your
worker
function. It is bad style (now). Just define it as extension on
CoroutineScope
2. Restart the
worker
from handler like this:
Copy code
val handler = CoroutineExceptionHandler { context, error ->
        CoroutineScope(context).worker()
    }
Akka-style auto-restart is NOT a great idea for a language with mutable state. Indeed, it works perfectly in Erlang (that does not have any mutable state), but in Akka it just leads to tons of boilerplate and to just bad code in overall (you have to wrap every actor into a class, and you have to pass parameters to actors without type-safety, and you have to trust that it actually works)
v
Well, Akka Typed, which is close to release afaik, does not use mutable state, does not require actors to be classes and it's fully typed.
e
It is indeed much better, yet still too much boiler-plate for my taste.
Weā€™ll be tacking design for more fully-featured actors after 1.0 release. There are a few ideas on where we can make it better. With (encapsulated) actors it does make sense to add some kind of auto-restart support, even though Iā€™m not a fan ā€” this kind of logic seems to be very project-specific. If you bake it into the library then you end up with too many configuration params. If it takes a few lines of code to implement restart, then it better be code
In kotlin you can literally write restart code with one lineā€¦.
Copy code
val restart = CoroutineExceptionHandler { context, _ -> CoroutineScope(context).worker() }
We can add a ā€œhelperā€ to make into something like:
Copy code
val restart = CoroutineRestarter { worker() }
Does it really need further improvement?
v
I agree, too much ceremony. but Iā€™m afraid if one were gonna implement features that it provides, he would write a lot of code, and the result would be way less reliable. I can pick kotlin now for an app iā€™m working on and will miss certain features soon (like supervising, clustering, persistence, out of the box monitoring and logging, a lot of integration adapters like amqp, mongo, kafka, etc, etc) Simple kotlin apps are indeed very clean and enjoy to write, but what I will do when I need it be production ready?
e
We donā€™t have any plans on writing a distributing programming framework. That is somebody elseā€™s bread. We donā€™t plan to write any kind of framework for that matter. We are just providing core scaffolding and utilities.
Anyone can take Kotlin coroutines and go write a framework. Iā€™m sure weā€™ll actually see multiple competing frameworks based on coroutines quite soon.
(Iā€™m personally not a fan of frameworks at allā€¦ Iā€™d never use something as magical as Akka for my own project)
v
I was there: I used a CML .NET port (channels, selective communication, IVar, MVar, streams etc.) for a number of applications and 1. it's very powerful 2. it's very hard to reason about the code structure, the actual paths data flies 3. it's not reliable, i.e. worker jobs can just die silently and it's error prone to check that all code is wrapped with try catch. As a result, apps just stopped on prod, which is the least thing you want to have. Now I use akka (streams mostly) instead, where I have explicit, reliable (i.e. supervised by default + restart strategies, etc.), asynchronous data transformation. That is all on .NET. Now I need to write an app on JVM and I'm not sure what to use: akka typed looks good and familiar, but the app cries for shared synchronous channels (work distribution), which is quite hard to achieve with actors. That's why I'm looking at Kotlin coroutines, which are simple, but not reliable.
j
just curious, what parts are not reliable?