Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better type support for reusable FaultTolerance instances #1050

Closed
AleksanderBrzozowski opened this issue Sep 24, 2024 · 5 comments · Fixed by #1051
Closed

Better type support for reusable FaultTolerance instances #1050

AleksanderBrzozowski opened this issue Sep 24, 2024 · 5 comments · Fixed by #1051
Assignees
Milestone

Comments

@AleksanderBrzozowski
Copy link

The type support in the fault tolerance instances is mostly used for fallback purposes.

I use programatic API to configure fault tolerance, and it requires ugly hacks to reuse the instance for two methods that return different types:

    private val faultTolerance: FaultTolerance<Uni<Any>> = MutinyFaultTolerance.create<Any>().build()

    fun action1(): Uni<String> = faultTolerance { ... }

    fun action2(): Uni<Int> = faultTolerance { ... }

    @Suppress("UNCHECKED_CAST")
    private operator fun <T> FaultTolerance<Uni<Any>>.invoke(
        block: () -> Uni<T>,
    ): Uni<T> = this.call { block() as Uni<Any> } as Uni<T>

I wonder if there is a better API to support this kind of use case, instead of making this ugly hack.

@Ladicek
Copy link
Contributor

Ladicek commented Sep 24, 2024

Yeah, I wonder as well. I don't see a good way, to be honest. Do you?

@AleksanderBrzozowski
Copy link
Author

One of the possible solutions is to allow sharing the state between these two - the same circuit breaker, rate limit, etc.

For example:

val store = FaultToleranceStore.create()

val ft1 = FaultTolerance.create(...)
  .withStore(store)
val ft2  = FaultTolerance.create(...)
  .withStore(store)

I am not sure if this is the proper way. Other option is to have a method within FaultTolerance that contains this ugly cast ☹️

@Ladicek
Copy link
Contributor

Ladicek commented Sep 24, 2024

Hmm, I guess something like this might work?

FaultTolerance.java:

/**
 * Creates a copy of this {@link FaultTolerance} object containing the same fault tolerance strategies,
 * but guarding an action of a different type. Since the type of the action is only used in fallback,
 * this is usually safe; if this {@code FaultTolerance} object contains a fallback, this method throws
 * an exception.
 * <p>
 * Note that the original type <em>must</em> have the same asynchronous "nature" as the new type. That is,
 * if the original {@code FaultTolerance} object is synchronous, the new type must also be synchronous.
 * If the original {@code FaultTolerance} object is asynchronous and the asynchronous type is
 * {@code CompletionStage<T>}, the new type must be {@code CompletionStage<U>}.
 *
 * @param <U> type of value of the guarded action
 */
<U> FaultTolerance<U> copy();

@AleksanderBrzozowski
Copy link
Author

This looks good to me, maybe there should be a different name than copy(). I was wondering if we can do something better. I guess that the fallback method is problematic, as it can cause troubles when calling copy().

Maybe fallback should be checked inside copy method, and if it exists, the function should throw an exception?

I am also a concerned about other use cases, for example - reuse circuit breaker, but not rate limiter.

I was wondering, maybe it is possible to have API like this:

val cb = CircuitBreaker(...)
val ft1 = FaultTolerance(...).withCircuitBreaker(cb).build()
val ft2 = FaultTolerance(...).withCircuitBreaker(cb).build()

@Ladicek
Copy link
Contributor

Ladicek commented Sep 24, 2024

This looks good to me, maybe there should be a different name than copy().

If you have a suggestion, I'm all ears.

I was wondering if we can do something better. I guess that the fallback method is problematic, as it can cause troubles when calling copy().

Maybe fallback should be checked inside copy method, and if it exists, the function should throw an exception?

That's exactly what the documentation says :-)

I am also a concerned about other use cases, for example - reuse circuit breaker, but not rate limiter.

I was wondering, maybe it is possible to have API like this:

val cb = CircuitBreaker(...)
val ft1 = FaultTolerance(...).withCircuitBreaker(cb).build()
val ft2 = FaultTolerance(...).withCircuitBreaker(cb).build()

I'm afraid this goes directly against the existing design. I'll have to think about that.

@Ladicek Ladicek self-assigned this Sep 30, 2024
@Ladicek Ladicek added this to the 6.5.0 milestone Sep 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants