1

Given the following reactive code:

operation1()
    .then(operation2())
    .onErrorMap(e -> revertOperation1().thenReturn(e).block())
    .then(operation3())
    .onErrorResume(e -> Mono.empty());

where each operation returns Mono<Void>.

Errors are propagated downstream, meaning that if either operation1() or operation2() fail then both onErrorMap(...) and onErrorResume(...) are executed.

How can I make it so that when an error occurs that is not propagated downstream?

More precisely:

  • if operation1() fails, neither onErrorMap(...) nor onErrorResume(...) should be executed;
  • if operation2() fails, only onErrorMap(...) should be executed but not onErrorResume(...).

onErrorStop exists, but that seems to work only in combination with onErrorContinue.

I could use only one onError at the end of the chain, but I don't know how to identify which operation returned the error.

Using spring-boot-starter-webflux.

Marco Lackovic
  • 6,077
  • 7
  • 55
  • 56
  • 2
    put your error handling inside the `then` operator like `.then(operation2().onErrorMap())`, this way it is only applied to the operation2 and do the same for operation3. – Martin Tarjányi Mar 17 '21 at 18:16
  • I tried that but doesn't seem to work as intended: `operation2` and `3` are still being executed when `operation1` fails. According to [the documentation](https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#then-reactor.core.publisher.Mono-) in `then(Mono other)` the _error signal is replayed in the resulting Mono_ – Marco Lackovic Mar 18 '21 at 09:32
  • `operation1().then(operation2().onErrorMap(...)).then(operation3().onErrorResume(...))` - yes, this way the failure of operation1 should prevent operation 2 and 3 from being executed. If that's not the case, then you should share what is happening inside operation1 because then something is likely wrong there. – Martin Tarjányi Mar 18 '21 at 09:36
  • 1
    although, I have an idea, you might have a misunderstanding of the flow, check out this answer: https://stackoverflow.com/questions/57870706/whats-the-point-of-switchifempty-getting-evaluated-eagerly/57877616#57877616 - TLDR. you might want to wrap operation2 and operation3 with `Mono.defer()` if there is something eager going on in them – Martin Tarjányi Mar 18 '21 at 09:38
  • All operations are `WebClient` requests that return `Mono` and may throw a `WebClientResponseException` in case of errors. What I am trying to do is _error recovery_: some failed critical operations should be reverted while others should be just logged. For testing purposes, using `Mono operation1() { return Mono.error(new RuntimeException()); }`, in your suggested example both `operation2` and `operation3` are executed. – Marco Lackovic Mar 18 '21 at 15:25
  • no, they are assembled, but not subscribed/executed, why do you think they are executed? – Martin Tarjányi Mar 18 '21 at 15:41
  • Using `System.out.println("x"); return Mono.empty();` as a body of `operation2` and `3` then two `x`-es are printed out – Marco Lackovic Mar 18 '21 at 15:48
  • the stackoverflow answer that I linked before has the answer for you – Martin Tarjányi Mar 18 '21 at 15:50
  • Your solution worked in the end, thanks. Would be good to have it as an answer. I was deceived by a false negative Junit test: I was using _Mockito_ to `verify` that `operation2` was `never()` called when `operation1` failed and it was telling me that it was called. I am guessing that's because _Mockito_ only verify it at _assembly time_. – Marco Lackovic Mar 19 '21 at 17:04

0 Answers0