Skip to content

Instantly share code, notes, and snippets.

@OKatrych
Created March 28, 2024 13:25
Show Gist options
  • Save OKatrych/62a15692f8e757c89c64448fbbfdb62f to your computer and use it in GitHub Desktop.
Save OKatrych/62a15692f8e757c89c64448fbbfdb62f to your computer and use it in GitHub Desktop.
Flow exponential backoff
fun <T> Flow<T>.retryWithPolicy(
retryPolicy: RetryPolicy,
predicate: suspend FlowCollector<T>.(cause: Throwable) -> Boolean
): Flow<T> {
var currentDelay = retryPolicy.delayMillis
val delayFactor = retryPolicy.delayFactor
return retryWhen { cause, attempt ->
if (predicate(cause) && attempt < retryPolicy.numRetries) {
delay(currentDelay)
currentDelay = (currentDelay * delayFactor).coerceAtMost(retryPolicy.maxDelayMillis)
return@retryWhen true
} else {
return@retryWhen false
}
}
}
/**
* Retry policy with exponential backoff.
*
* delayFactor is used to multiply delayMillis to increase the delay for the next retry.
*
* For instance, given a policy with numRetries of 4, delayMillis of 400ms and delayFactor of 2:
* - first retry: effective delayMillis will be 400
* - second retry: effective delayMillis will be 800
* - third retry: effective delayMillis will be 1600
* - forth retry: effective delayMillis will be 3200
*
* If no exponential backoff is desired, set delayFactor to 1
*/
interface RetryPolicy {
val numRetries: Long
val delayMillis: Long
val delayFactor: Long
val maxDelayMillis: Long
}
data class DefaultRetryPolicy(
override val numRetries: Long = Long.MAX_VALUE,
override val delayMillis: Long = 400,
override val delayFactor: Long = 2,
override val maxDelayMillis: Long = 10_000,
) : RetryPolicy
/**
* Useful for testing
*/
data class NoRetryPolicy(
override val numRetries: Long = 0,
override val delayMillis: Long = 0,
override val delayFactor: Long = 0,
override val maxDelayMillis: Long = 0,
) : RetryPolicy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment