Created
August 11, 2022 21:18
-
-
Save BigZaphod/50ccb254a6e7047ff5693a4381a3be37 to your computer and use it in GitHub Desktop.
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
// | |
// Created by Sean Heber on 8/11/22. | |
// | |
import Foundation | |
enum ExponentialBackoffError : Error { | |
case retryLimitExceeded | |
} | |
/// Runs the `operation` until it succeeds. | |
/// Success here is defined as not throwing, so if `operation` throws any errors, this function swallows them, waits a bit, and runs the `operation` again until either we reach the maximum attempts allowed or the task is cancelled. | |
func retryWithExponentialBackoff<Result>(base: Double = 0.25, maxInterval: Double = 60, maxAttempts: Int? = nil, operation: () async throws -> Result) async throws -> Result { | |
var attempt = 0 | |
while maxAttempts == nil || attempt < maxAttempts! { | |
try Task.checkCancellation() | |
do { | |
return try await operation() | |
} catch { | |
// errors from the operation are ignored! | |
} | |
// This uses an exponential backoff with jitter (hence the randomness). | |
let sleep = base * Double(pow(Double(2), Double(attempt))) | |
let seconds = Double.random(in: 0...min(maxInterval, sleep)) | |
try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000)) | |
attempt += 1 | |
} | |
throw ExponentialBackoffError.retryLimitExceeded | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And of course the
onFailure
function could also be madeasync
there, too, and who knows what crazy possibilities that might unlock.