From c710c72968b53680dc17c1bfd3f1d1d1373d0a41 Mon Sep 17 00:00:00 2001 From: Aaron Todd Date: Mon, 20 Nov 2023 16:32:55 -0500 Subject: [PATCH] refactor: make AWS retry policy inheritable (#1122) --- .../4cbc20ed-789f-4040-b81c-328ecd3d0700.json | 8 +++ aws-runtime/aws-http/api/aws-http.api | 10 ++- .../http/retries/AwsDefaultRetryPolicy.kt | 55 ---------------- .../runtime/http/retries/AwsRetryPolicy.kt | 62 +++++++++++++++++++ ...tryPolicyTest.kt => AwsRetryPolicyTest.kt} | 10 +-- .../aws/sdk/kotlin/codegen/AwsRuntimeTypes.kt | 2 +- .../codegen/AwsServiceConfigIntegration.kt | 4 +- 7 files changed, 86 insertions(+), 65 deletions(-) create mode 100644 .changes/4cbc20ed-789f-4040-b81c-328ecd3d0700.json delete mode 100644 aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/retries/AwsDefaultRetryPolicy.kt create mode 100644 aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/retries/AwsRetryPolicy.kt rename aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/retries/{AwsDefaultRetryPolicyTest.kt => AwsRetryPolicyTest.kt} (78%) diff --git a/.changes/4cbc20ed-789f-4040-b81c-328ecd3d0700.json b/.changes/4cbc20ed-789f-4040-b81c-328ecd3d0700.json new file mode 100644 index 00000000000..baa5bd3b768 --- /dev/null +++ b/.changes/4cbc20ed-789f-4040-b81c-328ecd3d0700.json @@ -0,0 +1,8 @@ +{ + "id": "4cbc20ed-789f-4040-b81c-328ecd3d0700", + "type": "feature", + "description": "Make the AWS retry policy inheritable", + "issues": [ + "awslabs/aws-sdk-kotlin#1055" + ] +} \ No newline at end of file diff --git a/aws-runtime/aws-http/api/aws-http.api b/aws-runtime/aws-http/api/aws-http.api index bc37aa1159a..3e15d87841f 100644 --- a/aws-runtime/aws-http/api/aws-http.api +++ b/aws-runtime/aws-http/api/aws-http.api @@ -49,7 +49,13 @@ public final class aws/sdk/kotlin/runtime/http/operation/CustomUserAgentMetadata public static final fun getCustomUserAgentMetadata (Laws/smithy/kotlin/runtime/operation/ExecutionContext;)Laws/sdk/kotlin/runtime/http/operation/CustomUserAgentMetadata; } -public final class aws/sdk/kotlin/runtime/http/retries/AwsDefaultRetryPolicy : aws/smithy/kotlin/runtime/retries/policy/StandardRetryPolicy { - public static final field INSTANCE Laws/sdk/kotlin/runtime/http/retries/AwsDefaultRetryPolicy; +public class aws/sdk/kotlin/runtime/http/retries/AwsRetryPolicy : aws/smithy/kotlin/runtime/retries/policy/StandardRetryPolicy { + public static final field Companion Laws/sdk/kotlin/runtime/http/retries/AwsRetryPolicy$Companion; + public fun ()V + protected fun evaluateSpecificExceptions (Ljava/lang/Throwable;)Laws/smithy/kotlin/runtime/retries/policy/RetryDirective; +} + +public final class aws/sdk/kotlin/runtime/http/retries/AwsRetryPolicy$Companion { + public final fun getDefault ()Laws/sdk/kotlin/runtime/http/retries/AwsRetryPolicy; } diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/retries/AwsDefaultRetryPolicy.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/retries/AwsDefaultRetryPolicy.kt deleted file mode 100644 index d4726e22151..00000000000 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/retries/AwsDefaultRetryPolicy.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.smithy.kotlin.runtime.ServiceErrorMetadata -import aws.smithy.kotlin.runtime.ServiceException -import aws.smithy.kotlin.runtime.http.response.HttpResponse -import aws.smithy.kotlin.runtime.retries.policy.RetryDirective -import aws.smithy.kotlin.runtime.retries.policy.RetryErrorType.* -import aws.smithy.kotlin.runtime.retries.policy.StandardRetryPolicy - -public object AwsDefaultRetryPolicy : StandardRetryPolicy() { - internal val knownErrorTypes = mapOf( - "BandwidthLimitExceeded" to Throttling, - "EC2ThrottledException" to Throttling, - "IDPCommunicationError" to Transient, - "LimitExceededException" to Throttling, - "PriorRequestNotComplete" to Throttling, - "ProvisionedThroughputExceededException" to Throttling, - "RequestLimitExceeded" to Throttling, - "RequestThrottled" to Throttling, - "RequestThrottledException" to Throttling, - "RequestTimeout" to Transient, - "RequestTimeoutException" to Transient, - "SlowDown" to Throttling, - "ThrottledException" to Throttling, - "Throttling" to Throttling, - "ThrottlingException" to Throttling, - "TooManyRequestsException" to Throttling, - "TransactionInProgressException" to Throttling, - ) - - internal val knownStatusCodes = mapOf( - 500 to Transient, - 502 to Transient, - 503 to Transient, - 504 to Transient, - ) - - override fun evaluateSpecificExceptions(ex: Throwable): RetryDirective? = when (ex) { - is ServiceException -> evaluateServiceException(ex) - else -> null - } - - private fun evaluateServiceException(ex: ServiceException): RetryDirective? = with(ex.sdkErrorMetadata) { - (knownErrorTypes[errorCode] ?: knownStatusCodes[statusCode]) - ?.let { RetryDirective.RetryError(it) } - } - - private val ServiceErrorMetadata.statusCode: Int? - get() = (protocolResponse as? HttpResponse)?.status?.value -} diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/retries/AwsRetryPolicy.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/retries/AwsRetryPolicy.kt new file mode 100644 index 00000000000..807cb9018aa --- /dev/null +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/retries/AwsRetryPolicy.kt @@ -0,0 +1,62 @@ +/* + * 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.smithy.kotlin.runtime.ServiceErrorMetadata +import aws.smithy.kotlin.runtime.ServiceException +import aws.smithy.kotlin.runtime.http.response.HttpResponse +import aws.smithy.kotlin.runtime.retries.policy.RetryDirective +import aws.smithy.kotlin.runtime.retries.policy.RetryErrorType.* +import aws.smithy.kotlin.runtime.retries.policy.StandardRetryPolicy + +public open class AwsRetryPolicy : StandardRetryPolicy() { + public companion object { + /** + * The default [aws.smithy.kotlin.runtime.retries.policy.RetryPolicy] used by AWS service clients + */ + public val Default: AwsRetryPolicy = AwsRetryPolicy() + + internal val knownErrorTypes = mapOf( + "BandwidthLimitExceeded" to Throttling, + "EC2ThrottledException" to Throttling, + "IDPCommunicationError" to Transient, + "LimitExceededException" to Throttling, + "PriorRequestNotComplete" to Throttling, + "ProvisionedThroughputExceededException" to Throttling, + "RequestLimitExceeded" to Throttling, + "RequestThrottled" to Throttling, + "RequestThrottledException" to Throttling, + "RequestTimeout" to Transient, + "RequestTimeoutException" to Transient, + "SlowDown" to Throttling, + "ThrottledException" to Throttling, + "Throttling" to Throttling, + "ThrottlingException" to Throttling, + "TooManyRequestsException" to Throttling, + "TransactionInProgressException" to Throttling, + ) + + internal val knownStatusCodes = mapOf( + 500 to Transient, + 502 to Transient, + 503 to Transient, + 504 to Transient, + ) + } + + override fun evaluateSpecificExceptions(ex: Throwable): RetryDirective? = when (ex) { + is ServiceException -> evaluateServiceException(ex) + else -> null + } + + private fun evaluateServiceException(ex: ServiceException): RetryDirective? = with(ex.sdkErrorMetadata) { + (knownErrorTypes[errorCode] ?: knownStatusCodes[statusCode]) + ?.let { RetryDirective.RetryError(it) } + } + + private val ServiceErrorMetadata.statusCode: Int? + get() = (protocolResponse as? HttpResponse)?.status?.value +} diff --git a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/retries/AwsDefaultRetryPolicyTest.kt b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/retries/AwsRetryPolicyTest.kt similarity index 78% rename from aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/retries/AwsDefaultRetryPolicyTest.kt rename to aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/retries/AwsRetryPolicyTest.kt index 01a588ec7d3..06f2cdfffc0 100644 --- a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/retries/AwsDefaultRetryPolicyTest.kt +++ b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/retries/AwsRetryPolicyTest.kt @@ -14,25 +14,25 @@ import aws.smithy.kotlin.runtime.retries.policy.RetryDirective import kotlin.test.Test import kotlin.test.assertEquals -class AwsDefaultRetryPolicyTest { +class AwsRetryPolicyTest { @Test fun testErrorsByErrorCode() { - AwsDefaultRetryPolicy.knownErrorTypes.forEach { (errorCode, errorType) -> + AwsRetryPolicy.knownErrorTypes.forEach { (errorCode, errorType) -> val ex = ServiceException() ex.sdkErrorMetadata.attributes[ServiceErrorMetadata.ErrorCode] = errorCode - val result = AwsDefaultRetryPolicy.evaluate(Result.failure(ex)) + val result = AwsRetryPolicy.Default.evaluate(Result.failure(ex)) assertEquals(RetryDirective.RetryError(errorType), result) } } @Test fun testErrorsByStatusCode() { - AwsDefaultRetryPolicy.knownStatusCodes.forEach { (statusCode, errorType) -> + AwsRetryPolicy.knownStatusCodes.forEach { (statusCode, errorType) -> val modeledStatusCode = HttpStatusCode.fromValue(statusCode) val response = HttpResponse(modeledStatusCode, Headers.Empty, HttpBody.Empty) val ex = ServiceException() ex.sdkErrorMetadata.attributes[ServiceErrorMetadata.ProtocolResponse] = response - val result = AwsDefaultRetryPolicy.evaluate(Result.failure(ex)) + val result = AwsRetryPolicy.Default.evaluate(Result.failure(ex)) assertEquals(RetryDirective.RetryError(errorType), result) } } diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsRuntimeTypes.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsRuntimeTypes.kt index e6b138ff481..4385020b467 100644 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsRuntimeTypes.kt +++ b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsRuntimeTypes.kt @@ -63,7 +63,7 @@ object AwsRuntimeTypes { } object Retries { - val AwsDefaultRetryPolicy = symbol("AwsDefaultRetryPolicy", "retries") + val AwsRetryPolicy = symbol("AwsRetryPolicy", "retries") } object Middleware { diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsServiceConfigIntegration.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsServiceConfigIntegration.kt index 1f9ca20f798..750f1f65342 100644 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsServiceConfigIntegration.kt +++ b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsServiceConfigIntegration.kt @@ -121,8 +121,8 @@ class AwsServiceConfigIntegration : KotlinIntegration { .RetryPolicy .toBuilder() .apply { - propertyType = ConfigPropertyType.RequiredWithDefault("AwsDefaultRetryPolicy") - additionalImports = listOf(AwsRuntimeTypes.Http.Retries.AwsDefaultRetryPolicy) + propertyType = ConfigPropertyType.RequiredWithDefault("AwsRetryPolicy.Default") + additionalImports = listOf(AwsRuntimeTypes.Http.Retries.AwsRetryPolicy) } .build()