2

I'm leaning webflux and reactor. Got three test methods as below. "documentOperations.findById" and "documentOperations.delete" are two database operations. I know test1 is bad as the two db operations are placed in one async method. My question is:
Do test2 and test3 have the same impact to system performace? Or in other words, which one is better?

private Mono<ServerResponse> test1(ServerRequest request, Contexts contexts) {
    return request.body(bodyDocumentExtractor)
            .flatMap(doc -> {
                Document document = documentOperations.findById(doc.getId());
                documentOperations.delete(document.getId());
                return ServerResponse.noContent().build();
            });
}

private Mono<ServerResponse> test2(ServerRequest request, Contexts contexts) {
    return request.body(bodyDocumentExtractor)
            .flatMap(doc -> {
                return Mono.just(documentOperations.findById(doc.getId()))
                        .flatMap(document -> {
                            documentOperations.delete(document.getId());
                            return ServerResponse.noContent().build();
                        });
            });
}

private Mono<ServerResponse> test3(ServerRequest request, Contexts contexts) {
    return request.body(bodyDocumentExtractor)
            .flatMap(doc -> {
                return Mono.just(documentOperations.findById(doc.getId()));
            }).flatMap(document -> {
                documentOperations.delete(document.getId());
                return ServerResponse.noContent().build();
            });
}
Toerktumlare
  • 12,548
  • 3
  • 35
  • 54
  • I'm assuming the operations on `documentOperations` interact with some external datasource. Therefore, both of these methods should return their results wrapped in a `Mono`. – Michael McFadyen Oct 30 '20 at 22:30
  • @Michael Xin Better to use a reactive db client when working with Webflux. Whenever you need something to connect to external details (such as dbs, http resources) always use reactive clients. For dbs checkout ReactiveMongoRepository, for http calls checkout WebClient. If you cannot find such reactive client, checkout spring guildelines for working with blocking behaviors. https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-concurrency-model – Nipuna Saranga Oct 31 '20 at 01:44
  • your databases return concrete values which tells me directly that they are blocking. So none of the above are good, they are all bad. – Toerktumlare Oct 31 '20 at 16:41

2 Answers2

3

None of the examples above are good. All your database calls return concrete types which means that they are all blocking calls.

// returns the concrete type
// thread does the call, needs to wait until we get the value (document)
Document document = documentOperations.findById("1");

If it is non blocking it returns a Mono<T> or a Flux<T>.

// Returns a mono, so we know it's not blocking. 
// We can chain on actions with for example flatMap etc.
Mono<Document> document = documentOperations.findById("1");

If you have to use a blocking database like for instance oracle database etc. You need to place this on its entire own thread, so that it doesn't block any of the main worker threads. This can be done with a scheduler. So in this example when a client subscribes it will be placed on a separate thread.

Mono<Document> document = Mono.fromCallable(() -> documentOperations.findById("1"))
    .subscribeOn(Schedulers.boundedElastic());;

So for your example:

private Mono<ServerResponse> test3(ServerRequest request, Contexts contexts) {
    return request.body(bodyDocumentExtractor)
        .flatMap(doc -> Mono.fromCallable(() -> documentOperations.findById(doc.getId()))
        .flatMap(document -> Mono.fromCallable(() -> documentOperations.delete(document.getId()))
                .then(ServerResponse.noContent().build());
        ).subscribeOn(Schedulers.boundedElastic());
}

Reactor documentation - Wrap blocking calls

Toerktumlare
  • 12,548
  • 3
  • 35
  • 54
1

I would expect the code to look something along the lines of the code below based on the assumption that DocumentOperations is reactive.

private Mono<ServerResponse> test3(ServerRequest request, Contexts contexts) {
    return request.body(bodyDocumentExtractor)
            .flatMap(doc -> documentOperations.findById(doc.getId()))
            .flatMap(document -> documentOperations.delete(document.getId()))
            .then(ServerResponse.noContent().build());
}
Michael McFadyen
  • 2,675
  • 11
  • 25