Skip to content

Commit

Permalink
Transform the shouldRetry closure into a recoverFromFailure closu…
Browse files Browse the repository at this point in the history
…re that returns an enumeration.
  • Loading branch information
fumoboy007 committed Jan 4, 2024
1 parent 55663c5 commit 21ba8f7
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 150 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ An important but often overlooked default is the choice of backoff algorithm, wh
### Powerful Flexibility

The API provides several customization points to accommodate any use case:
- Retries can be selectively enabled or disabled for specific error cases by providing a custom `shouldRetry` closure. Retries can also be selectively enabled or disabled for specific code paths by wrapping thrown errors with `Retryable` or `NotRetryable`.
- Retries can be selectively enabled or disabled for specific error cases by providing a custom `recoverFromFailure` closure. Retries can also be selectively enabled or disabled for specific code paths by wrapping thrown errors with `Retryable` or `NotRetryable`.
- The `RetryConfiguration` type encapsulates the retry behavior so that it can be reused across multiple call sites without duplicating code.
- The `Backoff` type represents the choice of algorithm that will be used to determine how long to sleep in between attempts. It has built-in support for common algorithms but can be initialized with a custom `BackoffAlgorithm` implementation if needed.
- The clock that is used to sleep in between attempts can be replaced. For example, one might use a fake `Clock` implementation in automated tests to ensure the tests are deterministic and efficient.
12 changes: 6 additions & 6 deletions Snippets/Advanced Use Cases/RetryableRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ extension MyRequest: RetryableRequest {
with configuration: RetryConfiguration<ClockType>,
@_inheritActorContext @_implicitSelfCapture operation: (Self) async throws -> ReturnType
) async throws -> ReturnType {
// We can override the `shouldRetry` closure to automatically handle errors specific to
// the communication protocol.
let configuration = configuration.withShouldRetry { error in
// We can override the `recoverFromFailure` closure to automatically handle errors
// specific to the communication protocol.
let configuration = configuration.withRecoverFromFailure { error in
switch error {
case is MyTransientCommunicationError:
return true
return .retry

case is MyNonTransientCommunicationError:
return false
return .throw

default:
return configuration.shouldRetry(error)
return configuration.recoverFromFailure(error)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import Retry

try await retry {
try await doSomething()
} shouldRetry: { error in
return error.isRetryable
} recoverFromFailure: { error in
return error.isRetryable ? .retry : .throw
}

extension Error {
Expand Down
4 changes: 3 additions & 1 deletion Snippets/Common Use Cases/ReuseRetryConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import Retry
// snippet.show

extension RetryConfiguration<ContinuousClock> {
static let standard = RetryConfiguration(shouldRetry: { $0.isRetryable })
static let standard = RetryConfiguration(
recoverFromFailure: { $0.isRetryable ? .retry : .throw }
)

static let highTolerance = (
Self.standard
Expand Down
30 changes: 30 additions & 0 deletions Sources/Retry/RecoveryAction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// MIT License
//
// Copyright © 2024 Darren Mo.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

/// The action to take after an attempt fails.
public enum RecoveryAction<ClockType: Clock> {
/// Retries the operation, unless the number of attempts reached ``RetryConfiguration/maxAttempts``.
case retry

/// Throws the error without retrying the operation.
case `throw`
}
11 changes: 6 additions & 5 deletions Sources/Retry/Retry.docc/Retry.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Retries with sensible defaults and powerful flexibility.

### Designed for Swift Concurrency

The ``retry(maxAttempts:backoff:appleLogger:logger:operation:shouldRetry:)`` function is an `async` function that runs the given `async` closure repeatedly until it succeeds or until the failure is no longer retryable. The function sleeps in between attempts while respecting task cancellation.
The ``retry(maxAttempts:backoff:appleLogger:logger:operation:recoverFromFailure:)`` function is an `async` function that runs the given `async` closure repeatedly until it succeeds or until the failure is no longer retryable. The function sleeps in between attempts while respecting task cancellation.

### Sensible Defaults

Expand All @@ -17,7 +17,7 @@ An important but often overlooked default is the choice of backoff algorithm, wh
### Powerful Flexibility

The API provides several customization points to accommodate any use case:
- Retries can be selectively enabled or disabled for specific error cases by providing a custom ``RetryConfiguration/shouldRetry`` closure. Retries can also be selectively enabled or disabled for specific code paths by wrapping thrown errors with ``Retryable`` or ``NotRetryable``.
- Retries can be selectively enabled or disabled for specific error cases by providing a custom ``RetryConfiguration/recoverFromFailure`` closure. Retries can also be selectively enabled or disabled for specific code paths by wrapping thrown errors with ``Retryable`` or ``NotRetryable``.
- The ``RetryConfiguration`` type encapsulates the retry behavior so that it can be reused across multiple call sites without duplicating code.
- The ``Backoff`` type represents the choice of algorithm that will be used to determine how long to sleep in between attempts. It has built-in support for common algorithms but can be initialized with a custom ``BackoffAlgorithm`` implementation if needed.
- The ``RetryConfiguration/clock`` that is used to sleep in between attempts can be replaced. For example, one might use a fake `Clock` implementation in automated tests to ensure the tests are deterministic and efficient.
Expand All @@ -31,14 +31,15 @@ The API provides several customization points to accommodate any use case:

### Retrying Operations

- ``retry(maxAttempts:backoff:appleLogger:logger:operation:shouldRetry:)``
- ``retry(maxAttempts:clock:backoff:appleLogger:logger:operation:shouldRetry:)-2cjan``
- ``retry(maxAttempts:clock:backoff:appleLogger:logger:operation:shouldRetry:)-2aiqm``
- ``retry(maxAttempts:backoff:appleLogger:logger:operation:recoverFromFailure:)``
- ``retry(maxAttempts:clock:backoff:appleLogger:logger:operation:recoverFromFailure:)-6s251``
- ``retry(maxAttempts:clock:backoff:appleLogger:logger:operation:recoverFromFailure:)-2e9va``
- ``retry(with:operation:)``

### Configuring the Retry Behavior

- ``RetryConfiguration``
- ``RecoveryAction``
- ``Backoff``
- ``BackoffAlgorithm``

Expand Down
Loading

0 comments on commit 21ba8f7

Please # to comment.