Why do we need non-blocking db connectors if we can just wrap the standard blocking query result in a reactive stream? Are Mongo, Redis, etc non-blocking connectors actually streaming data from the datasource, or fetch data in a blocking fashion, then stream from in-memory? An example of querying Mongo:
public interface ItemReactiveRepository extends ReactiveMongoRepository<Item,String> {
//
}
...
Flux<Item> itemsFlux = itemReactiveRepository.findAll();
and
public interface ItemRepository extends MongoRepository<User, String> {
//
}
...
List<Item> itemsList = itemRepository.findAll();
Flux<Items> itemsFlux = Flux.fromIterable(itemsList );
If someone can take a time to explain, or throw in a link I’d be grateful
2
Answers
Short answer is scalability.
In the blocking world (JDBC, JPA, etc.) if you want to execute many database queries concurrently, then you’ll need as many threads as many concurrent database queries you want to execute. This works fine until a certain point. However, threads are not free: they require memory, and context switching costs CPU time. So the more concurrency you have, the more your system struggles.
Here comes non-blocking IO (R2DBC, Reactive Mongo Driver, etc.) and reactive which lets you get rid of this thread-per-connection model and lets you achieve the same concurrency by using a fixed, low number of threads (usually equals to number of CPU cores). This model provides much higher scalability.
If you just wrapped your blocking code into a reactive stream, you wouldn’t get rid of the thread-per-connection model, you’d just hide the problem and would end up with the same scalability issues.
Reactive code isn’t a case of just “using different objects”. It’s a fundamentally different way of writing code to ensure that a thread is never blocking (or waiting) for something else to happen. It’s either actively busy, doing some processing, or it’s ready and waiting. The objects are just a side-effect of writing code in that style.
We could, theoretically, write that code using existing Java objects and just using “callbacks”. However, while this sort of approach works ok for buttons being clicked on a UI, you’d quickly end up in “callback hell” if you tried to scale that approach. This is the reason we have frameworks that provide objects such as
Flux
andMono
, giving us the ability to manage non-blocking code in a much more sensible, and more powerful way.These objects also provide utility methods for taking “standard” objects and wrapping them (such as
fromIterable()
orjust()
.) These are useful in some scenarios, but can be misused – your second example is just that; a case of what some refer to as “imposter” reactive code. It’s not “reactive” or non-blocking at all, it just wraps blocking method calls in reactive objects to make them appear to be reactive. This is bad, as it means people using your methods, assuming that they’re reactive because they look reactive, will actually end up calling blocking code on a non-blocking thread. That will either crash the entire implementation or slow it down horrendously, and may then mean tools such as Blockhound need to be brought in to track down what’s going on.Put simply, it’s impossible to take blocking code and make it non-blocking – so if you think you’ve somehow done that, you’re mistaken. It has to be written from the ground up (unfortunately, and that’s why it’s taking so long for full reactive stacks on Java to become viable.) The other way around however is trivial – you can block on any reactive call you want to.