Cody Mikol
04/28/2021, 12:21 AMrunBlocking
lambda have a negative impact on its behavior?
Given the scenario that I want a method capable of taking in a list of strings and then resolving something via HTTP I seem to have a two options.
1. Make the method suspended, this requires callers to be suspended as well
suspend fun parRequireStrings(resolvers: List<String>): ValidatedNel<Error, List<String>> = properties
.parTraverseValidated(Semigroup.nonEmptyList()) { requireString(it).toValidatedNel() }
2 Make the method block so the callers don’t have to be suspended
fun parRequireStrings(resolvers: List<String>): ValidatedNel<Error, List<String>> = runBlocking {
properties.parTraverseValidated(Semigroup.nonEmptyList()) { requireString(it).toValidatedNel() }
}
The second option seems more desirable as you can call it outside of suspended functions, are there any drawbacks to this approach?simon.vergauwen
04/28/2021, 8:04 AMsuspend
.
In the second snippet, you're 100% going to be blocking the thread from where this is called (which can cause deadlocks, crashes on Android if called from main, etc).
It's not advised to call runBlocking
like this in places, but instead, you should try to call it only once on the edge of your program.
From the docs "It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in main
functions and in tests."
So I'd say the first option is most desirable, besides the problems of runBlocking
it's the only function that correctly models its behavior and is referentially transparent.simon.vergauwen
04/28/2021, 8:06 AMscope.launch {
val res = parRequireString(listOf(....))
render(res)
}
Cody Mikol
04/28/2021, 1:48 PMrunBlocking
would just await the results of that suspension. Does this mean in the case of blocking parTraverseEither
all parallel benefits are lost? I have a situation where I have a large batch of work to do and thought I could leverage this to run them in parallel.
I’m currently working in a large application that uses the older blocking version of Spring Web REST controllers.simon.vergauwen
04/28/2021, 3:03 PMDoes this mean in the case of blockingYes and no 😄 The problem is that a server runs the incoming request on a thread pool, whenever you run intoall parallel benefits are lost?parTraverseEither
runBlocking
it blocks one of those threads until the runBlocking
is finished.
If the pool is size 8, and you get 8 incoming requests that all run into runBlocking
then the server will not be able to handle any more incoming requests. So that can heavily degrade the throughput of a server.
In contrast, using suspend
will not block any thread but jump between threads and that'll will allow other incoming request to be accepted will the other request are running some work in parallel in the background.
So to bridge between suspend
and the Java world you should utilize one of the Java features that have the same capabilities.
With Spring Web REST controllers that would be CompletableFuture
, so you can use the KotlinX Coroutines JDK8 support to bridge.
https://github.com/Kotlin/kotlinx.coroutines/tree/master/integration/kotlinx-coroutines-jdk8
future {
properties.parTraverseValidated(Semigroup.nonEmptyList()) { requireString(it).toValidatedNel() }
}
If you're using Spring WebFlux you could use the Reactor integration instead: https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive/kotlinx-coroutines-reactor