cats-retry
cats-retry copied to clipboard
Create version for exponential backoff with max capped delay
Similar to akka i wanna have ability to create exponential backoff retry policy with max capped delay just for keep logical delay, not like 10 minutes. I can create PR by myself if you don't mind :)
I'm not sure what you mean by "logical delay". Do you have a link to the Akka docs for this feature? Or some sample code to show what it looks like in Akka?
In akka we have this functionality:
RestartSource.onFailuresWithBackoff(
minBackoff = 3.seconds,
maxBackoff = 30.seconds,
randomFactor = 0.2,
maxRestarts = -1
)(() => YourStream)
When we migrate to fs2 i wrote this policy for reproduce this behavior in cats-retry:
def exponentialRandomBackoff[M[_] : Applicative](
minBackoff: FiniteDuration,
maxBackoff: FiniteDuration,
randomFactor: Double,
maxRestarts: Int
): RetryPolicy[M] =
RetryPolicy.liftWithShow[M]({
status =>
if (maxRestarts == -1 || status.retriesSoFar < maxRestarts) {
val delay =
if (status.previousDelay.exists(_ >= maxBackoff))
maxBackoff
else {
//Copy-paste from RetryPolicies.safeMultiply
val durationNanos = BigInt(minBackoff.toNanos)
val resultNanos = durationNanos * BigInt(Math.pow(2, status.retriesSoFar.toDouble).toLong)
val safeResultNanos = resultNanos min BigInt(Long.MaxValue)
val duration_ = (FiniteDuration(safeResultNanos.toLong, TimeUnit.NANOSECONDS) *
(1.0 + Random.nextDouble() * randomFactor)) min maxBackoff
FiniteDuration(duration_.toNanos, TimeUnit.NANOSECONDS)
}
DelayAndRetry(delay)
} else
GiveUp
}, show"exponentialRandomBackoff(minBackoff=$minBackoff, maxBackoff=$maxBackoff, randomFactor=$randomFactor, maxRestarts=$maxRestarts)")
Also i reference path of this code issue in #188
I rewrite this example with built-in functions:
def exponentialRandomBackoff[M[_] : Applicative](
minBackoff: FiniteDuration,
maxBackoff: FiniteDuration,
randomFactor: Double,
maxRestarts: Int
): RetryPolicy[M] =
RetryPolicy
.withShow[M](
{ status =>
RetryPolicies.exponentialBackoff[M](minBackoff).decideNextRetry(status).map {
case GiveUp => GiveUp
case DelayAndRetry(delay) =>
val nextDelay = delay * (1.0 + Random.nextDouble() * randomFactor)
DelayAndRetry(FiniteDuration(nextDelay.toNanos, TimeUnit.NANOSECONDS))
}
},
show"exponentialRandomBackoff(minBackoff=$minBackoff, randomFactor=$randomFactor)"
)
.meet(RetryPolicies.constantDelay(maxBackoff))
.join(
if (maxRestarts == -1)
RetryPolicies.constantDelay[M](Duration.Zero)
else
RetryPolicies.limitRetries[M](maxRestarts)
)
So i think that we can put something like this in lib would be helpful for devs who migrate from akka-streams to fs2.