Mono::zip doesn't short-circuit for some kind of 'Mono' objects

Viewed 45

I try to zip two Monos into one, like in the following example.

final Mono<String> mono1 = ...;
final Mono<String> mono2 = ...;

final Mono<String> result = Mono.zip(mono1, mono2)
    .map(args -> args.getT1() + ":" + args.getT1());

My expectation now is, that mono2 is not evaluated if mono1 is an error-Mono, but this is not always the case. It seems that mono2 is evaluated, even in the case of an error, if it is the result of a Mono::flatMap operation. The following test will reproduce this behaviour.

@Test
public void shortCircuitZip() {
    final var count = new AtomicInteger();

    // Short-circuting in zip if first is an error-mono.
    final var mono1 = Mono.zip(
            Mono.<String>error(new IllegalArgumentException("Illegal")),
            Mono.fromSupplier(() -> {
                count.incrementAndGet();
                return "RESULT";
            })
        )
        .map(args -> "%s:%s".formatted(args.getT1(), args.getT2()));

    assertThatThrownBy(mono1::block).isInstanceOf(IllegalArgumentException.class);
    assertThat(count.get()).isEqualTo(0);

    // If the second mono has a 'flatMap' in it, the zip is no longer
    // short-circuting.
    final var mono2 = Mono.zip(
            Mono.<String>error(new IllegalArgumentException("Illegal")),
            Mono.fromSupplier(() -> {
                    count.incrementAndGet();
                    return "RESULT";
                })
                .flatMap(v -> Mono.just(v))
        )
        .map(args -> "%s:%s".formatted(args.getT1(), args.getT2()));

    assertThatThrownBy(mono2::block).isInstanceOf(IllegalArgumentException.class);
    // This assertion fails with a "flatMapped" second mono.
    assertThat(count.get()).isEqualTo(0);
}

The question is, whether this behaviour is intentional or a bug in the reactor library?

I'm using the following reactor version: io.projectreactor:reactor-core:3.4.19

0 Answers
Related