Search code examples
javaquarkusmutinyexponential-backoff

Uni subscription not running when using retry policy


I have encountered some weird behavior while using Mutiny along Quarkus.

My problem is that I am trying to wrap an existing method into a Uni and I want this method to be retried a certain number of times and if they all fail, I want my failure subscription to be called, but is not.

In order to better understand this, I wrote a test for it:

@Test
void mutinySubscriptionNotCalledAfterRetry() {
    final AtomicBoolean executed = new AtomicBoolean(false);
    Uni.createFrom().item(this::error)
       .onFailure()
       .retry()
       .withBackOff(Duration.ofSeconds(1)).withJitter(0.2)
       .atMost(5)
       .subscribe()
       .with(success -> fail(),
             failure -> executed.set(true));
    assertTrue(executed.get()); // Failing statement
}

private boolean error() {
    throw new RuntimeException();
}

The thing is that the failure subscription is never ran, and I don't know whether I am failing to understand something, but this seems to be a valid usecase according to Clement's playground:

https://gist.github.com/cescoffier/e9abce907a1c3d05d70bea3dae6dc3d5

Can anybody shed some light on this?

Thanks a lot in advance.


Solution

  • The use case is perfectly valid, but your test is not written correctly. Such kinds of retries are asynchronous. So, the failure callback (failure -> execute(set(true));) is not yet called when you execute the assertion. You need to wait until it's called. In your case, it will take ~ 35 seconds.

    To wait, you can use Awaitility and do something like: await().until(() -> executed.get());. You can also use io.smallrye.mutiny.helpers.test.UniAssertSubscriber:

    @Test
    void mutinySubscriptionNotCalledAfterRetry() {
      UniAssertSubscriber<Boolean> subscriber = 
        Uni.createFrom().item(this::error)              
           .onFailure().retry()
              .withBackOff(Duration.ofSeconds(1)).withJitter(0.2)
              .atMost(5)
           .subscribe().withSubscriber(new UniAssertSubscriber<>());
    
      subscriber.awaitFailure(Duration.ofSeconds(35));
    }
    
    private boolean error() {
        throw new RuntimeException("boom");
    }