Last active
December 6, 2022 19:23
-
-
Save gmk57/77f25792f95b60b8eab4d9f0a7d1b38e to your computer and use it in GitHub Desktop.
Coroutine-based solution for retries with delay
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Using
Result
as a return type requires special compiler flag, please add tobuild.gradle
:kotlinOptions.freeCompilerArgs += ["-Xallow-result-return-type"]
You can replace Timber with your favorite logger or ditch it completely.