If I'm managing a library with both `suspend` and ...
# coroutines
d
If I'm managing a library with both
suspend
and non suspend versions of an api (based on Lettuce Redis client's blocking and non blocking api), and I'd rather manage all the code in either one and adapt to the other, is there a major advantage to just keep them separate -- or is it better to manage the suspend version and use runBlocking as a bridge, or maybe just manage the blocking version and use
with(<http://Dispatchers.IO|Dispatchers.IO>)
to make it into the suspend version?
s
I think it depends on how this Redis client is actually implemented. If their "non-blocking" client is just a wrapper around the blocking one, you might as well not use it and just call it via the IO dispatcher instead. On the other hand, if it's using true non-blocking IO under the hood, then there's an actual benefit to writing a proper suspending adapter for it.
A quick Google suggests it's a true non blocking client, so I'd recommend doing as much as you can with suspending functions and only blocking at the call site when you absolutely need to
d
Does it make sense to manage a separate api for blocking calls then? Is calling runBlocking with a suspend version adding significant overhead?
s
There's probably not much difference at all between using runBlocking and writing a blocking implementation that uses the blocking functions provided by the client itself 👍
☝️ 1
Both approaches will end up using the calling thread to run any code before and after the request, and then blocking the thread while the request is actually in flight
d
And you're saying that the overhead would probably be negligable?
s
Yes
👍🏼 1
j
I would avoid calling runBlocking from within a library project. Can lead to unexpected nested runBlockings in the client. Chatter I've seen is that nested runBlockings can be bad
💯 2
d
Yeah, good point. Although it's a bit frustrating to have to manage two sets of almost the same code and have to write the same tests for both. I wish there would be some way out of this... I only really considered this in the first place because anyone using coroutines would just use the non blocking api and wouldn't nest the scopes, but I admit that there could always be funny cases that people actually do what's unexpected.
g
What kind issue with nested runBlocking? Just curious about it
But I still would probably avoid runBlocking on library level and if such API exposed, I would expects that I use native blocking under the hood I would check if anyone actually using blocking API, because having wrappers for 2 apis look indeed as overkill, if nobody using it anyway
s
Here's one way things can go wrong when using runBlocking inside an existing coroutine: https://betterprogramming.pub/how-i-fell-in-kotlins-runblocking-deadlock-trap-and-how-you-can-avoid-it-db9e7c4909f1
👌🏼 1
👍 1
d
Yeah, well, we use both.. most of our microservice frameworks support coroutines, but some (like Keycloak when writing for their SPI...) don't. So I guess it's back to maintaining both then... 🙈, thanks!
g
If it an internal library, I think it's also could be a meaningful tradeoff to use runBlocking, if it on the right area of the code (so it's unlikely will be used by coroutines code, but let's say blocking service implementation). After all, blocking main thread could cause deadlock without coroutines too, it's just a nature of mixing blocking with callbacks
d
I just published it, but yeah, maybe in internal libraries it might be a better situation, although Sam has a good point that there's no indication in the function's declaration or anywhere else, so it might be something easy to forget, or when others in the company are using it, they might not be as careful.
g
Yes, but for a public library it's fine to have coroutines only API, for internal, to support existing code, migrate to runBlocking in some places could be not the worst thing, considering that coroutines API forces everyone to use it
This is why I say that coroutine-bases library shouldn't provide blocking API which is just wrap of suspend functions with runBlocking