0

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?

Andrea Damiani
  • 611
  • 1
  • 6
  • 20

1 Answers1

1

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

Alex Shavlovsky
  • 321
  • 3
  • 8
  • That makes sense! Could you give an explanation on why my solution doesn't work as I expected? – Andrea Damiani Jun 24 '20 at 20:23
  • I think that in your code `Mono> secondResult` is eagerly evaluated because it depends on the `String fooId` parameter of the `doSomething` method and does not depends on the result of the `firstServiceCall(...)` so the second service gets called even before a flatMap transformation occurs. See also [what does Mono.defer() do?](https://stackoverflow.com/a/55972232/13794675) – Alex Shavlovsky Jun 24 '20 at 22:47
  • 1
    I see. I'm still new to Webflux/Reactor so I did not know about defer. That is very interesting. Btw I tried your second solution with defer and Intellij highlights the "defer" word and says that the value is never used as a publisher. Although I tested it and it works perfectly. That's weird :D – Andrea Damiani Jun 25 '20 at 08:05