I have a Ktor http endpoint in which I want to tri...
# ktor
k
I have a Ktor http endpoint in which I want to trigger some async side effect that I do not want the caller to wait for. When I attended the talk "*Coroutines and Structured Concurrency in Ktor"* by @simon.vergauwen at KotlinConf I saw an example of how to launch a coroutine on the
call
property which as far as I understand does what I want:
Copy code
call.launch(Dispatchers.IO) {
  runCatching {
    // some "long running" side effect doing updates in the db that should keep running after the response has been sent back to the client.
  }.onFailure { error ->
    // catch to avoid failing the parent coroutine and log properly
  }
}
Is this considered "best practice" for my task? What about launching the coroutine in a new scope not related to the call/route scope:
Copy code
CoroutineScope(Dispatchers.IO).launch {
  // same as above
}
What is considered best practice?
p
It's definitely not best practice to create anonymous scopes. If you want management of those side-effects to be entirely outside the scope of the ktor request/application then create and manage a scope at the process level that has correct cleanup and management, and use that to launch your side effects
m
we have a
serviceScope
that we pass around with DI. When the container is getting drained (e.g. a new release rolls out) the scope is canceled, triggering shutdown of things like queue listeners and graceful closure of long-living connections like websockets etc
s
@Kjartan like @phldavies mentioned it's bad practice to create anonymous
CoroutineScope
. TL;DR never do create an custom scope, nor use
GlobalScope
. Especially with Ktor its not needed. There is 2
CoroutineScope
available to you in Ktor. 1. The
Application : CoroutienScope
scope which works like @madisp described, it's available when the application starts and gets cancelled and awaited when the engines or JVM exit. Instead of creating a
serviceScope
I pass around the
Application
but I encapsulate it as
CoroutineScope
to my service layer as also discussed in the Ktor talk. The recording is online btw,

https://www.youtube.com/watch?v=JxTIZAEos8Y

. 2. The
RoutingCall : CoroutineScope
which is available as the
call
property in any
RoutingHandler
.
p
you can also reach the
application
from
call
and do
call.application.launch { ... }
☝️ 1
k
Thanks for the feedback guys! 🙂
I have a follow-up question. As the http endpoint should have a "side effect" I am invoking a service using the
Application : CoroutineScope
as it makes more sense to me than using the
RoutingCall : CoroutineScope
scope:
Copy code
call.application.launch {
  runCatching {
    sideEffectService.performLongRunningTask()
  }.onFailure { error ->
    // some error handling
  }
}
All coroutines created within `performLongRunningTask()`will then be a children of the
Application : CoroutineScope
and to my understanding will be awaited if the applications shuts down while this operation is running. However it is a bit unclear to me why I would want to pass around the
Application : CoroutineScope
or some
serviceScope
? I suppose this is only relevant if if I want to create coroutines that should not be tied to the current "parent"
CoroutineScope
? So in my example above I am already using the
Application : CoroutineScope
and it would not make sense if that also was injected as a private variable in the
sideEffectService
, Unless I would have a specific reason for wanting to create some other side effect that should not be related to the "outer" CoroutineContext. Or are there other reasons for injecting a CoroutineScope?
s
I suppose this is only relevant if if I want to create coroutines that should not be tied to the current "parent"
CoroutineScope
?
I'm not entirely sure what you mean, but
Application
is the parent of the
ApplicationCall : CoroutineScope
. Yes, injecting the
CoroutineScope
into your service layer, or relying on it directly like you're doing is only needed when you want to create new coroutines. Or in other words parallel executing code. You normally always want to prefer simple
suspend
functions otherwise, or regular functions for non-suspending code of course.
So in my example above I am already using the
Application : CoroutineScope
and it would not make sense if that also was injected as a private variable in the
sideEffectService
, Unless I would have a specific reason for wanting to create some other side effect that should not be related to the "outer" CoroutineContext. Or are there other reasons for injecting a CoroutineScope?
Great question! Your use-case and usage is perfect. It's a maybe bit subjective, but some prefer injecting into the class itself. This can be desired when you want to encapsulate the behavior of launching, to make the function available to Java code, ... ? In your snippet the service seems to exposes a simple
suspend fun
, which makes the complexity of the service lower imo, and defers the decision how the function runs to the user. Your approach also significantly lowers the testing complexity, since no injecting of context is required