Spring Web Server using coroutines

I am creating a web server that perform heavy IO ops to databases. I have so far used Project Reactor/WebFlux but I recently started looking into coroutines. I am need some guidance with best practises.

My app consist of three layers: Rest Controller → Business Logic → DAO. This is how I am thinking of doing:

//Controller
@GetMapping
suspend fun getData() = service.getData()

//Business Layer
suspend fun getData(): Data = coroutineScope(Dispatchers.IO) {
    val def1 = async { repo.getData1() }
    val def2 = async { repo.getData1() }

    return def1.await() + def2.await()
}

//Repository/DAO
suspend fun getData1() = ...await()
suspend fun getData2() = ...await()

So my questions are… Is this a good practise(i e the “correct” way to do it)? Do I really need to repeat coroutineScope(Dispatchers.IO) on every function in the business layer or do Spring do some magic for us?

1 Like

I think you jump into Dispatchers.IO too high/early. Dispatchers.IO should be really reserved for blocking operations only, should be placed close to IO. Business layer often perform more than just calling the model, it runs some CPU calculations and then it should use Dispatchers.Default instead. Even DAO itself sometimes needs to perform some CPU-intensive transformations.

Moreover, business layer does not really know how DAO works and therefore it should not assume it should jump to Dispatchers.IO . DAO could use memory to store data, it could use asynchronous IO, etc. DAO should know if/when jump to which dispatcher.

Also, you need to understand that using Dispatchers.IO does not magically make blocking IO highly performant. Internally, it just spawns a separate thread per call and blocks on it (but it re-uses threads). Often this is enough, but if you say you perform heavy IO then you should look for libraries that are coroutine-friendly and have suspend API, not blocking. Or for libraries with asynchronous API and then wrap it into suspend API by yourself. In both cases, you should then not jump into Dispatchers.IO at all (yeah, did I say business logic does not know whether model requires Dispatchers.IO or not?).

2 Likes

Thanks for the info. So I guess I should avoid setting dispatchers and use the default one. I can’t tell for sure, but I am using Spring Web Flux and RD2BC with a Kotlin extension and I think(and hope) that the framework is doing things on the right dispatchers.

You suggested libraries that are more coroutines friends. Any suggestions? After some googling, I would say Spring (Boot) is not that friendly. Vertx and Ktor are for example better suited.

Dispatchers.IO is mainly for blocking IO operations. R2DBC uses non-blocking IO so I see no reason for you to switch to Dispatchers.IO at all.

I also do not see why you would consider Spring to be not coroutine friendly, when you are using the reactive stack (Web Flux, R2DBC etc.).

1 Like