Skip to content

Commit 4ae0349

Browse files
authored
feat: implement basic retry support in runtime (#328)
1 parent cf91733 commit 4ae0349

File tree

4 files changed

+139
-0
lines changed

4 files changed

+139
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
package aws.sdk.kotlin.runtime.http.retries
7+
8+
import aws.sdk.kotlin.runtime.AwsServiceException
9+
import aws.smithy.kotlin.runtime.ServiceErrorMetadata
10+
import aws.smithy.kotlin.runtime.http.response.HttpResponse
11+
import aws.smithy.kotlin.runtime.retries.RetryDirective
12+
import aws.smithy.kotlin.runtime.retries.RetryErrorType.*
13+
import aws.smithy.kotlin.runtime.retries.impl.StandardRetryPolicy
14+
15+
public object AwsDefaultRetryPolicy : StandardRetryPolicy() {
16+
internal val knownErrorTypes = mapOf(
17+
"BandwidthLimitExceeded" to Throttling,
18+
"EC2ThrottledException" to Throttling,
19+
"IDPCommunicationError" to Timeout,
20+
"LimitExceededException" to Throttling,
21+
"PriorRequestNotComplete" to Throttling,
22+
"ProvisionedThroughputExceededException" to Throttling,
23+
"RequestLimitExceeded" to Throttling,
24+
"RequestThrottled" to Throttling,
25+
"RequestThrottledException" to Throttling,
26+
"RequestTimeout" to Timeout,
27+
"RequestTimeoutException" to Timeout,
28+
"SlowDown" to Throttling,
29+
"ThrottledException" to Throttling,
30+
"Throttling" to Throttling,
31+
"ThrottlingException" to Throttling,
32+
"TooManyRequestsException" to Throttling,
33+
"TransactionInProgressException" to Throttling,
34+
)
35+
36+
internal val knownStatusCodes = mapOf(
37+
500 to Timeout,
38+
502 to Timeout,
39+
503 to Timeout,
40+
504 to Timeout,
41+
)
42+
43+
override fun evaluateOtherExceptions(ex: Throwable): RetryDirective? = when (ex) {
44+
is AwsServiceException -> evaluateAwsServiceException(ex)
45+
else -> null
46+
}
47+
48+
private fun evaluateAwsServiceException(ex: AwsServiceException): RetryDirective? = with(ex.sdkErrorMetadata) {
49+
(knownErrorTypes[errorCode] ?: knownStatusCodes[statusCode])
50+
?.let { RetryDirective.RetryError(it) }
51+
}
52+
53+
private val ServiceErrorMetadata.statusCode: Int?
54+
get() = (protocolResponse as? HttpResponse)?.status?.value
55+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package aws.sdk.kotlin.runtime.http.retries
2+
3+
import aws.sdk.kotlin.runtime.AwsErrorMetadata
4+
import aws.sdk.kotlin.runtime.AwsServiceException
5+
import aws.smithy.kotlin.runtime.ServiceErrorMetadata
6+
import aws.smithy.kotlin.runtime.http.Headers
7+
import aws.smithy.kotlin.runtime.http.HttpBody
8+
import aws.smithy.kotlin.runtime.http.HttpStatusCode
9+
import aws.smithy.kotlin.runtime.http.response.HttpResponse
10+
import aws.smithy.kotlin.runtime.retries.RetryDirective
11+
import kotlin.test.Test
12+
import kotlin.test.assertEquals
13+
14+
class AwsDefaultRetryPolicyTest {
15+
@Test
16+
fun testErrorsByErrorCode() {
17+
AwsDefaultRetryPolicy.knownErrorTypes.forEach { (errorCode, errorType) ->
18+
val ex = AwsServiceException()
19+
ex.sdkErrorMetadata.attributes[AwsErrorMetadata.ErrorCode] = errorCode
20+
val result = AwsDefaultRetryPolicy.evaluate(Result.failure(ex))
21+
assertEquals(RetryDirective.RetryError(errorType), result)
22+
}
23+
}
24+
25+
@Test
26+
fun testErrorsByStatusCode() {
27+
AwsDefaultRetryPolicy.knownStatusCodes.forEach { (statusCode, errorType) ->
28+
val modeledStatusCode = HttpStatusCode.fromValue(statusCode)
29+
val response = HttpResponse(modeledStatusCode, Headers.Empty, HttpBody.Empty)
30+
val ex = AwsServiceException()
31+
ex.sdkErrorMetadata.attributes[ServiceErrorMetadata.ProtocolResponse] = response
32+
val result = AwsDefaultRetryPolicy.evaluate(Result.failure(ex))
33+
assertEquals(RetryDirective.RetryError(errorType), result)
34+
}
35+
}
36+
}

docs/design/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ These designs extend or augment the [Smithy Kotlin Designs](https://github.com/a
99
## Detailed sub-designs
1010

1111
* [Endpoint resolution](endpoint-resolution.md)
12+
* [SDK-specific Retries](retries.md)

docs/design/retries.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# SDK-specific Retry Design
2+
3+
* **Type**: Design
4+
* **Author(s)**: Ian Botsford
5+
6+
# Abstract
7+
8+
The AWS SDK for Kotlin uses a specialization of the generalized
9+
[**smithy-kotlin** Retry Design](https://github.com/awslabs/smithy-kotlin/blob/main/docs/design/retries.md). This
10+
document covers those specializations (but does not re-hash the generalized design).
11+
12+
# SDK implementation
13+
14+
The SDK uses the following customizations/specializations over the generalized
15+
[**smithy-kotlin** Retry Design](https://github.com/awslabs/smithy-kotlin/blob/main/docs/design/retries.md):
16+
17+
## Retry policy
18+
19+
The generalized `StandardRetryPolicy` is subclassed to provide support for information only available in AWS-specific
20+
exception types:
21+
22+
```kotlin
23+
object AwsDefaultRetryPolicy : StandardRetryPolicy() {
24+
internal val knownErrorTypes = mapOf(
25+
"BandwidthLimitExceeded" to Throttling,
26+
"RequestTimeoutException" to Timeout,
27+
"TooManyRequestsException" to Throttling,
28+
29+
)
30+
31+
override fun evaluateOtherExceptions(ex: Throwable): RetryDirective? = when (ex) {
32+
is AwsServiceException -> evaluateAwsServiceException(ex)
33+
else -> null
34+
}
35+
36+
private fun evaluateAwsServiceException(ex: AwsServiceException): RetryDirective? = with(ex.sdkErrorMetadata) {
37+
knownErrorTypes[errorCode]?.let { RetryDirective.RetryError(it) }
38+
}
39+
}
40+
```
41+
42+
This policy utilizes the error code provided in the exception to derive a retry directive based on a known list. This
43+
list may grow/change over time.
44+
45+
# Revision history
46+
47+
* 9/27/2021 - Created

0 commit comments

Comments
 (0)