kartoffelsup
11/07/2022, 6:58 PMval exponentialBackoff: Schedule<Throwable, Double> = Schedule.exponential(TimeUnit.SECONDS.toNanos(2L).toDouble())
val retrySchedule = exponentialBackoff.jittered()
.whileOutput {
TimeUnit.NANOSECONDS.toSecods(it.toLong()) < 10.0
}
.untilInput {
when (it) {
is ApiException -> it.statusCode in 400 until 500
...
else -> false
}
the above never stops on the whileOutput condition (using this at work and don't have the code handy, it looks at least similar to above :D)simon.vergauwen
11/08/2022, 7:20 PMkartoffelsup
11/08/2022, 7:25 PMkartoffelsup
11/08/2022, 10:16 PMkartoffelsup
11/09/2022, 10:05 AMcheck
predicatekartoffelsup
11/10/2022, 5:08 PMsimon.vergauwen
11/11/2022, 9:00 AMkartoffelsup
11/12/2022, 5:22 PMkartoffelsup
11/13/2022, 2:45 PMcheck
predicate function. Is that intentional or am I using it wrong?
val exponentialBackoff: Schedule<Throwable, Double> = Schedule.exponential(TimeUnit.MILLISECONDS.toNanos(500L).toDouble())
val retrySchedule: Schedule<Throwable, Double> = exponentialBackoff.jittered(suspend { 5.0 })
.whileOutput { del ->
println(TimeUnit.NANOSECONDS.toMillis(del.toLong()))
true
}
val start: Instant by lazy { Instant.now() }
val result: Either<Throwable, Unit> = retrySchedule.retryOrElseEither({
println("Elapsed: ${Duration.between(start, Instant.now())}")
throw RetryableException("f")
}) { t: Throwable, _ -> t }
result.fold(
ifLeft = { println("Error: $it") },
ifRight = { println("Success") }
)
#################################
Elapsed: PT0.000016289S
1000
Elapsed: PT5.019987402S
2000
Elapsed: PT15.02116669S
4000
kartoffelsup
11/15/2022, 1:36 PMsimon.vergauwen
11/15/2022, 1:47 PMretry
into either
blocks, and it will retry Either.Left.bind
as well as exceptions. We're actually not entirely happy with the APIs of Schedule
, but finding a better API that matches the same functionality is tricky.
fun main() = runBlocking<Unit> {
either {
Schedule.recurs<Throwable>(3).retry {
println("Attempting")
raise<Int>("failure")
}
}.also(::println)
}
Attempting
Attempting
Attempting
Attempting
Either.Left(failure)
kartoffelsup
11/15/2022, 3:26 PMsimon.vergauwen
11/15/2022, 3:27 PMraise
here which is Arrow 2.x.x.
shift
or bind
works exactly the same 😉kartoffelsup
11/16/2022, 10:53 AMsimon.vergauwen
11/16/2022, 11:50 AMimport arrow.core.Either
import arrow.core.continuations.either
import arrow.fx.coroutines.Schedule
import arrow.fx.coroutines.retry
import kotlinx.coroutines.runBlocking
fun main() = runBlocking<Unit> {
either {
Schedule.recurs<Throwable>(3).retry {
println("Attempting")
Either.Left("failure").bind()
}
}.also(::println)
}
simon.vergauwen
11/16/2022, 11:51 AMkartoffelsup
11/16/2022, 12:02 PMcheck
condition had to be extended to return true for ShiftCancelledException
^^simon.vergauwen
11/16/2022, 12:09 PMRaise/EffectScope
based code.kartoffelsup
11/16/2022, 12:11 PMsimon.vergauwen
11/16/2022, 12:12 PMsimon.vergauwen
11/16/2022, 12:12 PMkartoffelsup
11/16/2022, 12:13 PMkartoffelsup
11/16/2022, 12:14 PMsimon.vergauwen
11/16/2022, 12:30 PMkartoffelsup
11/16/2022, 12:33 PMsimon.vergauwen
11/16/2022, 12:38 PMOutput
it typically not relevant. As long as you have good ways of configuring a MAX timeout.
Similarly to logging, there can be other alternatives, for example (looking at my current WIP).
repetition(
2.seconds
Policy.exponential().max(10.seconds)
).retry { index ->
Either.Left("failure")
.tapLeft { println(it) }
.bind()
}
simon.vergauwen
11/16/2022, 12:38 PMThrowable
but that could for example be a lambda passed to retry
.kartoffelsup
11/16/2022, 1:28 PMsimon.vergauwen
11/16/2022, 1:30 PMrepetition
is here the singleton value. I guess the Predicate
could also fit in there.. The same could not be done with for a predicate with E
since you only know that at the call-site.