Skip to content

Instantly share code, notes, and snippets.

@gmk57
Last active December 6, 2022 19:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gmk57/77f25792f95b60b8eab4d9f0a7d1b38e to your computer and use it in GitHub Desktop.
Save gmk57/77f25792f95b60b8eab4d9f0a7d1b38e to your computer and use it in GitHub Desktop.
Coroutine-based solution for retries with delay
/**
* Analogue of [runCatching] with retry, delay & logging added.
* Re-throws CancellationException to avoid the need to explicitly skip it in [onFailure].
* Intervals are calculated since previous failure.
*/
@ExperimentalTime
suspend fun <T> runRetrying(
times: Int,
interval: Duration,
name: String,
block: suspend () -> T
): Result<T> {
var count = 0
while (true) {
try {
return Result.success(block())
} catch (e: Throwable) {
if (e is CancellationException) throw e
if (++count >= times) {
return Result.failure(e)
} else {
Timber.w("$name error ($count): $e")
delay(interval)
}
}
}
}
/**
* Variant of [runRetrying] with intervals calculated since previous start to get monotonous attempts.
* Useful when failure may take a long and unpredictable time, e.g. network errors/timeouts.
*/
@ExperimentalTime
suspend fun <T> runRetryingExact(
times: Int,
interval: Duration,
name: String,
block: suspend () -> T
): Result<T> {
var count = 0
while (true) {
val startTime = TimeSource.Monotonic.markNow()
try {
return Result.success(block())
} catch (e: Throwable) {
if (e is CancellationException) throw e
if (++count >= times) {
return Result.failure(e)
} else {
Timber.w("$name error ($count): $e")
delay((startTime + interval).remaining())
}
}
}
}
/** Returns the amount of time remaining until this mark (opposite of [TimeMark.elapsedNow]) */
@ExperimentalTime
fun TimeMark.remaining(): Duration = -elapsedNow()
@gmk57
Copy link
Author

gmk57 commented Mar 22, 2021

Using Result as a return type requires special compiler flag, please add to build.gradle:
kotlinOptions.freeCompilerArgs += ["-Xallow-result-return-type"]

You can replace Timber with your favorite logger or ditch it completely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment