Search code examples
javaspringspring-webfluxreactor

Spring Webflux - Chained Mono gets resolved even though the first Mono responds with an Error Signal


Here's my code:

public Mono<Foo> doSomething(String fooId, String barId) {

    Mono<Foo> firstResult = firstServiceCall(fooId, barId);
    Mono<List<Baz>> secondResult = secondServiceCall(fooId);

    return firstResult.flatMap(secondResult::thenReturn);
}

private Mono<Foo> firstServiceCall(String fooId, String barId) {
    return fooRepo
            .findByFooIdAndBarId(fooId, barId)
            .switchIfEmpty(Mono.error(new ResponseStatusException(NOT_FOUND)))
            .filter(Foo::isSomething)
            .switchIfEmpty(Mono.error(new ResponseStatusException(UNPROCESSABLE_ENTITY)))
            .doOnNext(foo -> foo.setSomething(false))
            .flatMap(fooRepo::save);
}

private Mono<List<Baz>> secondServiceCall(String fooId) {
    var filter = new BazSearchFilter();
    filter.setFooId(fooId);
    return bazRepo
            .findAllByFilter(filter)
            .doOnNext(baz -> baz.setStatus(BazStatus.SOMETHING))
            .collectList()
            .flatMapMany(bazRepo::saveAll)
            .collectList();
}

For some reason, the doSomethingMethod always calls the secondServiceCall method despite an error signal being propagated from the firstServiceCall method (the NOT_FOUND or UNPROCESSABLE_ENTITY scenarios).

I would expect the secondServiceCall not to go off since I'm using flatMap, but maybe I'm missing something.

Does anyone know how to fix this?


Solution

  • You should just rewrite doSomething(...) method as follows:

    public Mono<Foo> doSomething(String fooId, String barId) {
    
        Mono<Foo> firstResult = firstServiceCall(fooId, barId);
    
        return firstResult.doOnNext(foo -> secondServiceCall(fooId));
    }
    

    or as follows:

    public Mono<Foo> doSomething(String fooId, String barId) {
    
        Mono<Foo> firstResult = firstServiceCall(fooId, barId);
        Mono<List<Baz>> secondResult = Mono.defer(() -> secondServiceCall(fooId));
    
        return firstResult.flatMap(secondResult::thenReturn);
    }
    

    It is very interesting that this method also works as expected:

    public Mono<Foo> doSomething(String fooId, String barId) {
    
        Mono<Foo> firstResult = firstServiceCall(fooId, barId);
    
        return firstResult.flatMap(foo -> secondServiceCall(fooId).thenReturn(foo));
    }
    

    definitely a lambda does the trick