Ultranium
Ultranium

Reputation: 372

Implement retry logic with Mutiny

I'm just learning Mutiny and I need to implement retry logic.

I have this code:

fun main() {
    getResult()
        .onFailure().invoke { t -> println("Got error: $t") }
        .onFailure().retry().atMost(2)
        .subscribe().with(
            { result -> println(result) },
            { t -> t.printStackTrace() }
        )
}

fun getResult(): Uni<String?> {
    println("Preparing result...")

    return Uni.createFrom().failure(Exception("Some error happened"))
}

So, the getResult() is a function that may misbehave and needs to be called multiple times on failure.

When I run this program, this is what's happening:

Preparing result...
Got error: java.lang.Exception: Some error happened
Got error: java.lang.Exception: Some error happened
Got error: java.lang.Exception: Some error happened
java.lang.Exception: Some error happened
    at MainKt.getResult(Main.kt:16)
    at MainKt.main(Main.kt:4)

Obiously, the getResult() function is called only once, while the onFailure() stages actually executed three times.

Is there anything that Mutiny could help me to execute getResult() function on each failure? I sure can implement this with a simple loop, but I feel like Mutiny should already have something like this.

Unfortunately, I didn't find anything suitable in the docs.

Upvotes: 1

Views: 1204

Answers (2)

Ultranium
Ultranium

Reputation: 372

So, the right solution for this is actually using the Uni.deferred() method like this:

fun main() {
    Uni.createFrom().deferred { getResult() }
        .onFailure().invoke { t -> println("Got error: $t") }
        .onFailure().retry().atMost(2)
        .subscribe().with(
            { result -> println(result) },
            { t -> t.printStackTrace() }
        )
}

Thanks to Boris the Spider, who suggested to use the deferred(), and to Clement, who clarified its use with null values.

Initially, I misinterpreted the deferred() documentation thinking it's not allowed to return a null value, but actually it's OK for a Supplier to return a Uni of null:

Uni.createFrom.deferred { Uni.createFrom().nullItem() }

What the docs really are prohibiting is returning a null instead of a Uni:

Uni.createFrom().deferred { null }

Upvotes: 2

Clement
Clement

Reputation: 3222

Your Uni in getResult is created with an "immediate" item, which is cached and never computed again.

Use Uni.createFrom().failure(() -> Exception("Some error happened"))

In this case, it's a supplier, so it won't be cached but called on every attempt.

Upvotes: 2

Related Questions