Signal-iOS/SignalServiceKit/Util/OWSOperation.swift

46 lines
2.1 KiB
Swift

//
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
public enum ExponentialBackoff {
public enum Defaults {
public static let minAverageBackoff: TimeInterval = 2
public static let maxAverageBackoff: TimeInterval = .infinity
}
}
public enum OWSOperation {
/// Computes an exponential retry delay with jitter.
///
/// The "jitter" is 25% of the base value. For example, if `failureCount` is
/// `4`, the base value is `16`, the jitter is ±4, and the value that's
/// returned is in the range [12, 20]. (Jitter helps avoid resource
/// contention, mitigate thundering herds, and spread out request spikes.)
///
/// - Parameters:
/// - failureCount: The number of failures that have occurred/the number
/// of times we've backed off. This is the "n" in the "2^n" exponential
/// backoff formula used when computing the base value.
///
/// - maxAverageBackoff: The maximum base value (i.e., the largest value
/// used before computing the jitter). This method may return a value up
/// to 25% larger than `maxAverageBackoff`, but over many invocations, the
/// average of the returned values will be `maxAverageBackoff`. (For
/// example, if you want "one retry per day", specify `.day`. Some retries
/// will happen after only 18 hours (not 24 hours), but those will be
/// balanced by retries that wait 30 hours.)
public static func retryIntervalForExponentialBackoff(
failureCount: some FixedWidthInteger,
minAverageBackoff: TimeInterval = ExponentialBackoff.Defaults.minAverageBackoff,
maxAverageBackoff: TimeInterval = ExponentialBackoff.Defaults.maxAverageBackoff,
) -> TimeInterval {
let averageBackoff = min(maxAverageBackoff, pow(2, Double(failureCount)) * minAverageBackoff / 2)
return averageBackoff * Double.random(in: 0.75..<1.25)
}
public static func formattedNs(_ nanoseconds: UInt64) -> String {
return String(format: "%.1f", Double(nanoseconds) / Double(NSEC_PER_SEC))
}
}