Skip to content

Commit

Permalink
Add a RetryableRequest protocol that adds safe retry methods to a c…
Browse files Browse the repository at this point in the history
…onforming request type.
  • Loading branch information
fumoboy007 committed Jan 3, 2024
1 parent 71ebf57 commit bd3f43e
Show file tree
Hide file tree
Showing 6 changed files with 445 additions and 0 deletions.
64 changes: 64 additions & 0 deletions Snippets/Advanced Use Cases/RetryableRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Conform a request type to ``RetryableRequest`` to add safe retry methods to the request type.

// snippet.hide

import Retry

// snippet.show

extension MyRequest: RetryableRequest {
var isIdempotent: Bool {
// ...
// snippet.hide
return true
// snippet.show
}

func unsafeRetryIgnoringIdempotency<ClockType, ReturnType>(
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
switch error {
case is MyTransientCommunicationError:
return true

case is MyNonTransientCommunicationError:
return false

default:
return configuration.shouldRetry(error)
}
}

return try await Retry.retry(with: configuration) {
return try await operation(self)
}
}
}

// snippet.hide

let myRequest = MyRequest()

// snippet.show

try await myRequest.retry { request in
try await perform(request)
}

// snippet.hide

struct MyRequest {
}

enum MyTransientCommunicationError: Error {
}

enum MyNonTransientCommunicationError: Error {
}

func perform(_ request: MyRequest) async throws {
}
4 changes: 4 additions & 0 deletions Sources/Retry/Retry.docc/Advanced Use Cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
## Implementing a Custom Backoff Algorithm

@Snippet(path: "swift-retry/Snippets/Advanced Use Cases/CustomBackoffAlgorithm")

## Adding Safe Retry Methods to a Request Type

@Snippet(path: "swift-retry/Snippets/Advanced Use Cases/RetryableRequest")
4 changes: 4 additions & 0 deletions Sources/Retry/Retry.docc/Retry.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ The API provides several customization points to accommodate any use case:

- ``Retryable``
- ``NotRetryable``

### Safely Retrying Requests

- ``RetryableRequest``
8 changes: 8 additions & 0 deletions Sources/Retry/Retry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import OSLog
/// will not be called if the error is ``Retryable`` or ``NotRetryable``.
///
/// - SeeAlso: ``retry(with:operation:)``
/// - SeeAlso: ``RetryableRequest``
public func retry<ReturnType>(
maxAttempts: Int? = 3,
backoff: Backoff<ContinuousClock> = .default(baseDelay: .seconds(1), maxDelay: .seconds(20)),
Expand Down Expand Up @@ -84,6 +85,7 @@ public func retry<ReturnType>(
/// will not be called if the error is ``Retryable`` or ``NotRetryable``.
///
/// - SeeAlso: ``retry(with:operation:)``
/// - SeeAlso: ``RetryableRequest``
public func retry<ClockType, ReturnType>(
maxAttempts: Int? = 3,
clock: ClockType,
Expand Down Expand Up @@ -125,6 +127,7 @@ public func retry<ClockType, ReturnType>(
/// will not be called if the error is ``Retryable`` or ``NotRetryable``.
///
/// - SeeAlso: ``retry(with:operation:)``
/// - SeeAlso: ``RetryableRequest``
public func retry<ClockType, ReturnType>(
maxAttempts: Int? = 3,
clock: ClockType,
Expand Down Expand Up @@ -163,6 +166,7 @@ public func retry<ClockType, ReturnType>(
/// will not be called if the error is ``Retryable`` or ``NotRetryable``.
///
/// - SeeAlso: ``retry(with:operation:)``
/// - SeeAlso: ``RetryableRequest``
public func retry<ReturnType>(
maxAttempts: Int? = 3,
backoff: Backoff<ContinuousClock> = .default(baseDelay: .seconds(1), maxDelay: .seconds(20)),
Expand Down Expand Up @@ -197,6 +201,7 @@ public func retry<ReturnType>(
/// will not be called if the error is ``Retryable`` or ``NotRetryable``.
///
/// - SeeAlso: ``retry(with:operation:)``
/// - SeeAlso: ``RetryableRequest``
public func retry<ClockType, ReturnType>(
maxAttempts: Int? = 3,
clock: ClockType,
Expand Down Expand Up @@ -234,6 +239,7 @@ public func retry<ClockType, ReturnType>(
/// will not be called if the error is ``Retryable`` or ``NotRetryable``.
///
/// - SeeAlso: ``retry(with:operation:)``
/// - SeeAlso: ``RetryableRequest``
public func retry<ClockType, ReturnType>(
maxAttempts: Int? = 3,
clock: ClockType,
Expand Down Expand Up @@ -266,6 +272,8 @@ public func retry<ClockType, ReturnType>(
///
/// - Note: The function will log messages using the `debug` log level to ``RetryConfiguration/logger``
/// (and/or ``RetryConfiguration/appleLogger`` on Apple platforms).
///
/// - SeeAlso: ``RetryableRequest``
public func retry<ClockType, ReturnType>(
with configuration: RetryConfiguration<ClockType>,
@_inheritActorContext @_implicitSelfCapture operation: () async throws -> ReturnType
Expand Down
Loading

0 comments on commit bd3f43e

Please sign in to comment.