-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add request retries implementation (#34)
- Loading branch information
Showing
12 changed files
with
717 additions
and
38 deletions.
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Copyright © 2023 Heroic Labs | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/// A protocol defining a random number generator. | ||
protocol Random { | ||
/// Generates a random double in the range `(0.0, 1.0]` | ||
/// - Returns; A random double between 0.0 (inclusive) and 1.0 (exclusive). | ||
func next() -> Double | ||
|
||
/** | ||
Generates a random double within the specified range. | ||
- Parameter range: The half-open range within which the random number is generated. Lower bound is inclusive, and upper bound is exclusive. | ||
- Returns: A random double within the specified range. | ||
*/ | ||
func next(in range: Range<Double>) -> Double | ||
} | ||
|
||
/// A custom implementation of the `Random` protocol providing a random number generator with a seed. | ||
public final class NakamaRandomGenerator: Random { | ||
private var seed: UInt64 | ||
|
||
/** | ||
Initialize the instance with the given `seed`. | ||
- Parameter seed: A string used to seed the random number generator. | ||
*/ | ||
public init(seed: String) { | ||
var hasher = Hasher() | ||
seed.hash(into: &hasher) | ||
let truncated = UInt32(truncatingIfNeeded: hasher.finalize()) | ||
|
||
srand48(Int(truncated)) | ||
self.seed = UInt64(truncated) | ||
} | ||
|
||
public func next() -> Double { | ||
return drand48() | ||
} | ||
|
||
public func next(in range: Range<Double>) -> Double { | ||
return range.lowerBound + ((range.upperBound - range.lowerBound) * drand48()) | ||
} | ||
|
||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* Copyright © 2023 Heroic Labs | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/// Represents a single retry attempt. | ||
public final class Retry | ||
{ | ||
/// The delay (milliseconds) in the request retry attributable to the exponential backoff algorithm. | ||
let exponentialBackoff: Int | ||
|
||
/// The delay (milliseconds) in the request retry attributable to the jitter algorithm. | ||
let jitterBackoff: Int | ||
|
||
public init(exponentialBackoff: Int, jitterBackoff: Int) { | ||
self.exponentialBackoff = exponentialBackoff | ||
self.jitterBackoff = jitterBackoff | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
* Copyright © 2023 Heroic Labs | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/** | ||
A configuration for controlling retriable requests. | ||
|
||
Configurations can be assigned to the `Client` on a request-by-request basis via `RequestConfiguration`. | ||
|
||
It can also be assigned on a global basis using `GlobalRetryConfiguration`. | ||
|
||
Configurations passed via the `RequestConfiguration` parameter take precedence over the global configuration. | ||
*/ | ||
public final class RetryConfiguration { | ||
/** | ||
The base delay (milliseconds) used to calculate the time before making another request attempt. | ||
This base will be raised to N, where N is the number of retry attempts. | ||
*/ | ||
var baseDelay: Int | ||
|
||
/// The maximum number of attempts to make before cancelling the request task. | ||
var maxRetries: Int | ||
|
||
/// The jitter algorithm used to apply randomness to the retry delay. Defaults to `FullJitter` | ||
var jitter: Jitter | ||
|
||
/// A closure that is invoked before a new retry attempt is made. | ||
var retryListener: RetryListener | ||
|
||
public init(baseDelayMs: Int, maxRetries: Int, jitter: Jitter? = nil, retryListener: RetryListener? = nil) { | ||
self.baseDelay = baseDelayMs | ||
self.maxRetries = maxRetries | ||
self.jitter = jitter ?? { retries, delayMs, random in | ||
return RetryJitter.fullJitter(retries: retries, retryDelay: delayMs, random: random) | ||
} | ||
self.retryListener = retryListener ?? { retriesCount, retry in | ||
|
||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
* Copyright © 2023 Heroic Labs | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/// Represents a history of retry attempts. | ||
public final class RetryHistory | ||
{ | ||
/// The configuration for retry behavior. | ||
var configuration: RetryConfiguration? | ||
|
||
/// An array containing individual retry attempts. | ||
var retries: [Retry] | ||
|
||
/// A seeded random number generator. | ||
var random: NakamaRandomGenerator | ||
|
||
/// Initializes a `RetryHistory` instance with a given token and retry configuration. | ||
/// | ||
/// Typically called with the Nakama authentication methods using the id or token as a seed for the `RNG`. | ||
/// - Parameters: | ||
/// - token: The id or authentication token used to seed the random number generator. | ||
/// - configuration: The configuration specifying retry behavior. | ||
public init(token: String, configuration: RetryConfiguration) { | ||
self.configuration = configuration | ||
self.retries = [] | ||
self.random = NakamaRandomGenerator(seed: token) | ||
} | ||
|
||
/// A convenience initializer that creates a `RetryHistory` instance using an existing session. | ||
/// | ||
/// Typically called with other Nakama methods after obtaining an authentication token. | ||
/// The auth token will be used as a seed for the random generator. | ||
/// | ||
/// - Parameters: | ||
/// - session: The authenticated session providing the token. | ||
/// - configuration: The configuration specifying retry behavior. | ||
public convenience init(session: Session, configuration: RetryConfiguration) { | ||
self.init(token: session.token, configuration: configuration) | ||
} | ||
} |
Oops, something went wrong.