7

I'm working on a project which is using Kotlin, Spring Boot, Hibernate (all on latest version) and I would like to make it reactive with WebFlux framework from Spring.

Problem is that I can't use ReactiveCrudRepository because web app have to use Oracle database and therefore Hibernate. So I couldn't figured out a way how to use non blocking access to Oracle SQL database (only free frameworks).

My question is:

Is it possible to use this like that:

  • Casual CrudRepository which is blocking
  • Service which use corountines and returns everything as Mono

Service example code:

fun getAllLanguages(): Mono<Collection<ProgrammingLanguage>> = async { repository.findAll() }.asMono()

Afterwards there will be controller with:

fun listProgrammingLanguagesReactive() = mono(Unconfined) {
    service.also { logger.info { "requesting list of programming languages" } }
            .getAllLanguages()
            .also { logger.info { "responding with list of programming languages" } }
}

This approach works but I'm not sure whether it will work all the time and whether this is not terrible practice and so on.

Lukas Forst
  • 802
  • 1
  • 8
  • 25

1 Answers1

8

The problem with synchronous blocking API is that there will be a thread blocked for each API call. There is simply no way around it, coroutines or not.

Your approach is as good as any for providing asynchronous adapter to blocking API.

However, please consider following:

You may want to confine async { repository.findAll() } and similar blocking calls to a dedicated fixed ThreadPool/Dispatcher. While coroutines are cheap, remember, that repository.findAll() blocks actual underlying thread and you don't want to exhaust all thread in the CommonPool (which is used by async by default).

This is a useful practice, as you're limiting the number of threads/simultaneous blocking calls. If your fixed pool gets exhausted at some point, then incoming requests will be suspended, without blocking threads, until there are available threads in the pool to process them.

Aivean
  • 10,692
  • 25
  • 39
  • Thank you! So basically it is ok as long as I don't have so much these blocking calls OR I would confine these requests to another Thread Pool, right? – Lukas Forst May 27 '18 at 19:16
  • @Delfi, basically, yes. Your API ensures that client threads (those that call `listProgrammingLanguagesReactive`) won't be blocked, but without dedicated fixed pool for blocking API calls the number of threads in your application may not be restricted (depending on the implementation of `CommonPool`). And restricting the number of threads is the main reason to use async paradigm in the first place. – Aivean May 27 '18 at 20:46
  • @Aivean Does this solution still works if we need to do multiple repository calls in the same transaction using `@Transactional` – jivimberg May 31 '18 at 16:07
  • 1
    @jivimberg, it will, but only if your `async {}` is invoked before `@Transactional`. In the example above this means that `repository.findAll()` is marked as `@Transactional`. So, behind the scenes, your transaction will be performed in a regular blocking manner, while you'll still have `async` facade. However, the limitation is that you cannot join multiple async calls into the same transaction. In order to do so, you'll have to add another transactional method to the `repository` (your blocking transactional service). – Aivean May 31 '18 at 16:51