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.
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");
}