Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

package aws.sdk.kotlin.runtime.http.retries

import aws.sdk.kotlin.runtime.AwsServiceException
import aws.smithy.kotlin.runtime.ServiceErrorMetadata
import aws.smithy.kotlin.runtime.http.response.HttpResponse
import aws.smithy.kotlin.runtime.retries.RetryDirective
import aws.smithy.kotlin.runtime.retries.RetryErrorType.*
import aws.smithy.kotlin.runtime.retries.impl.StandardRetryPolicy

public object AwsDefaultRetryPolicy : StandardRetryPolicy() {
internal val knownErrorTypes = mapOf(
"BandwidthLimitExceeded" to Throttling,
"EC2ThrottledException" to Throttling,
"IDPCommunicationError" to Timeout,
"LimitExceededException" to Throttling,
"PriorRequestNotComplete" to Throttling,
"ProvisionedThroughputExceededException" to Throttling,
"RequestLimitExceeded" to Throttling,
"RequestThrottled" to Throttling,
"RequestThrottledException" to Throttling,
"RequestTimeout" to Timeout,
"RequestTimeoutException" to Timeout,
"SlowDown" to Throttling,
"ThrottledException" to Throttling,
"Throttling" to Throttling,
"ThrottlingException" to Throttling,
"TooManyRequestsException" to Throttling,
"TransactionInProgressException" to Throttling,
)

internal val knownStatusCodes = mapOf(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correctness

Aren't these Transient errors?

Copy link
Contributor

@aajtodd aajtodd Oct 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also don't we need to define error codes for RequestTimeout and RequestTimeoutException as Transient error codes?
Nevermind, saw your other comment. We apparently call these Timeout

The known status codes still looks incorrect though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right you are, they should be Timeout.

500 to Timeout,
502 to Timeout,
503 to Timeout,
504 to Timeout,
)

override fun evaluateOtherExceptions(ex: Throwable): RetryDirective? = when (ex) {
is AwsServiceException -> evaluateAwsServiceException(ex)
else -> null
}

private fun evaluateAwsServiceException(ex: AwsServiceException): RetryDirective? = with(ex.sdkErrorMetadata) {
(knownErrorTypes[errorCode] ?: knownStatusCodes[statusCode])
?.let { RetryDirective.RetryError(it) }
}

private val ServiceErrorMetadata.statusCode: Int?
get() = (protocolResponse as? HttpResponse)?.status?.value
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package aws.sdk.kotlin.runtime.http.retries

import aws.sdk.kotlin.runtime.AwsErrorMetadata
import aws.sdk.kotlin.runtime.AwsServiceException
import aws.smithy.kotlin.runtime.ServiceErrorMetadata
import aws.smithy.kotlin.runtime.http.Headers
import aws.smithy.kotlin.runtime.http.HttpBody
import aws.smithy.kotlin.runtime.http.HttpStatusCode
import aws.smithy.kotlin.runtime.http.response.HttpResponse
import aws.smithy.kotlin.runtime.retries.RetryDirective
import kotlin.test.Test
import kotlin.test.assertEquals

class AwsDefaultRetryPolicyTest {
@Test
fun testErrorsByErrorCode() {
AwsDefaultRetryPolicy.knownErrorTypes.forEach { (errorCode, errorType) ->
val ex = AwsServiceException()
ex.sdkErrorMetadata.attributes[AwsErrorMetadata.ErrorCode] = errorCode
val result = AwsDefaultRetryPolicy.evaluate(Result.failure(ex))
assertEquals(RetryDirective.RetryError(errorType), result)
}
}

@Test
fun testErrorsByStatusCode() {
AwsDefaultRetryPolicy.knownStatusCodes.forEach { (statusCode, errorType) ->
val modeledStatusCode = HttpStatusCode.fromValue(statusCode)
val response = HttpResponse(modeledStatusCode, Headers.Empty, HttpBody.Empty)
val ex = AwsServiceException()
ex.sdkErrorMetadata.attributes[ServiceErrorMetadata.ProtocolResponse] = response
val result = AwsDefaultRetryPolicy.evaluate(Result.failure(ex))
assertEquals(RetryDirective.RetryError(errorType), result)
}
}
}
1 change: 1 addition & 0 deletions docs/design/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ These designs extend or augment the [Smithy Kotlin Designs](https://github.com/a
## Detailed sub-designs

* [Endpoint resolution](endpoint-resolution.md)
* [SDK-specific Retries](retries.md)
47 changes: 47 additions & 0 deletions docs/design/retries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# SDK-specific Retry Design

* **Type**: Design
* **Author(s)**: Ian Botsford

# Abstract

The AWS SDK for Kotlin uses a specialization of the generalized
[**smithy-kotlin** Retry Design](https://github.com/awslabs/smithy-kotlin/blob/main/docs/design/retries.md). This
document covers those specializations (but does not re-hash the generalized design).

# SDK implementation

The SDK uses the following customizations/specializations over the generalized
[**smithy-kotlin** Retry Design](https://github.com/awslabs/smithy-kotlin/blob/main/docs/design/retries.md):

## Retry policy

The generalized `StandardRetryPolicy` is subclassed to provide support for information only available in AWS-specific
exception types:

```kotlin
object AwsDefaultRetryPolicy : StandardRetryPolicy() {
internal val knownErrorTypes = mapOf(
"BandwidthLimitExceeded" to Throttling,
"RequestTimeoutException" to Timeout,
"TooManyRequestsException" to Throttling,
)

override fun evaluateOtherExceptions(ex: Throwable): RetryDirective? = when (ex) {
is AwsServiceException -> evaluateAwsServiceException(ex)
else -> null
}

private fun evaluateAwsServiceException(ex: AwsServiceException): RetryDirective? = with(ex.sdkErrorMetadata) {
knownErrorTypes[errorCode]?.let { RetryDirective.RetryError(it) }
}
}
```

This policy utilizes the error code provided in the exception to derive a retry directive based on a known list. This
list may grow/change over time.

# Revision history

* 9/27/2021 - Created