Skip to content

Commit 847ed0b

Browse files
authored
Fix the S3 alternate runtime retries test in orchestrator mode (#2825)
This PR fixes the S3 `alternative-async-runtime` retry tests. The retry backoff time was being included in the operation attempt timeout, which I think is undesirable as it makes retry config much harder to get right since the backoff times are not predictable (they have randomness incorporated into them). The overall operation timeout is the only one that needs to incorporate backoff times. In addition, this PR also: - Updates READMEs for the `aws-smithy-runtime-api` and `aws-smithy-runtime` crates - Adds top-level crate docs to describe features to `aws-smithy-runtime` - Copies `capture_test_logs` into `aws-smithy-runtime` so that it can be used (just about) everywhere instead of just in `aws-config` - Adds service/operation name to the tracing `invoke` span so it's possible to tell which operation the events are for - Makes the `Debug` impl for `Identity` useful - Adds a ton of trace/debug spans and events to the orchestrator - Fixes an issue in `aws-smithy-runtime` where a huge amount of the orchestrator tests weren't being compiled due to a removed feature flag ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
1 parent 386ded8 commit 847ed0b

File tree

23 files changed

+318
-118
lines changed

23 files changed

+318
-118
lines changed

aws/rust-runtime/aws-config/src/test_case.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ pub(crate) struct Metadata {
136136
name: String,
137137
}
138138

139+
// TODO(enableNewSmithyRuntimeCleanup): Replace Tee, capture_test_logs, and Rx with
140+
// the implementations added to aws_smithy_runtime::test_util::capture_test_logs
139141
struct Tee<W> {
140142
buf: Arc<Mutex<Vec<u8>>>,
141143
quiet: bool,

aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,13 @@
77
mod test {
88
use aws_sdk_dynamodb::config::{Credentials, Region, SharedAsyncSleep};
99
use aws_sdk_dynamodb::{config::retry::RetryConfig, error::ProvideErrorMetadata};
10-
use aws_smithy_async::rt::sleep::TokioSleep;
1110
use aws_smithy_async::test_util::instant_time_and_sleep;
1211
use aws_smithy_async::time::SharedTimeSource;
13-
use aws_smithy_async::time::SystemTimeSource;
1412
use aws_smithy_client::test_connection::TestConnection;
1513
use aws_smithy_http::body::SdkBody;
1614
use aws_smithy_runtime::client::retries::RetryPartition;
1715
use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse};
18-
use aws_smithy_types::timeout::TimeoutConfigBuilder;
19-
use std::time::{Duration, Instant, SystemTime};
16+
use std::time::{Duration, SystemTime};
2017

2118
fn req() -> HttpRequest {
2219
http::Request::builder()
@@ -111,15 +108,16 @@ mod test {
111108
}
112109

113110
#[tokio::test]
114-
async fn test_adaptive_retries_with_throttling_errors_times_out() {
115-
tracing_subscriber::fmt::init();
111+
async fn test_adaptive_retries_with_throttling_errors() {
112+
let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH);
113+
116114
let events = vec![
117115
// First operation
118-
(req(), err()),
116+
(req(), throttling_err()),
117+
(req(), throttling_err()),
119118
(req(), ok()),
120119
// Second operation
121120
(req(), err()),
122-
(req(), throttling_err()),
123121
(req(), ok()),
124122
];
125123

@@ -130,44 +128,31 @@ mod test {
130128
.retry_config(
131129
RetryConfig::adaptive()
132130
.with_max_attempts(4)
133-
.with_initial_backoff(Duration::from_millis(50))
134131
.with_use_static_exponential_base(true),
135132
)
136-
.timeout_config(
137-
TimeoutConfigBuilder::new()
138-
.operation_attempt_timeout(Duration::from_millis(100))
139-
.build(),
140-
)
141-
.time_source(SharedTimeSource::new(SystemTimeSource::new()))
142-
.sleep_impl(SharedAsyncSleep::new(TokioSleep::new()))
143-
.http_connector(conn.clone())
133+
.time_source(SharedTimeSource::new(time_source))
134+
.sleep_impl(SharedAsyncSleep::new(sleep_impl.clone()))
144135
.retry_partition(RetryPartition::new(
145-
"test_adaptive_retries_with_throttling_errors_times_out",
136+
"test_adaptive_retries_with_throttling_errors",
146137
))
138+
.http_connector(conn.clone())
147139
.build();
148-
149140
let expected_table_names = vec!["Test".to_owned()];
150-
let start = Instant::now();
151141

152142
// We create a new client each time to ensure that the cross-client retry state is working.
153143
let client = aws_sdk_dynamodb::Client::from_conf(config.clone());
154144
let res = client.list_tables().send().await.unwrap();
145+
assert_eq!(sleep_impl.total_duration(), Duration::from_secs(40));
155146
assert_eq!(res.table_names(), Some(expected_table_names.as_slice()));
156147
// Three requests should have been made, two failing & one success
157-
assert_eq!(conn.requests().len(), 2);
148+
assert_eq!(conn.requests().len(), 3);
158149

159-
let client = aws_sdk_dynamodb::Client::from_conf(config);
160-
let err = client.list_tables().send().await.unwrap_err();
161-
assert_eq!(err.to_string(), "request has timed out".to_owned());
162-
// two requests should have been made, both failing (plus previous requests)
163-
assert_eq!(conn.requests().len(), 2 + 2);
164-
165-
let since = start.elapsed();
166-
// At least 300 milliseconds must pass:
167-
// - 50ms for the first retry on attempt 1
168-
// - 50ms for the second retry on attempt 3
169-
// - 100ms for the throttling delay triggered by attempt 4, which required a delay longer than the attempt timeout.
170-
// - 100ms for the 5th attempt, which would have succeeded, but required a delay longer than the attempt timeout.
171-
assert!(since.as_secs_f64() > 0.3);
150+
let client = aws_sdk_dynamodb::Client::from_conf(config.clone());
151+
let res = client.list_tables().send().await.unwrap();
152+
assert!(Duration::from_secs(48) < sleep_impl.total_duration());
153+
assert!(Duration::from_secs(49) > sleep_impl.total_duration());
154+
assert_eq!(res.table_names(), Some(expected_table_names.as_slice()));
155+
// Two requests should have been made, one failing & one success (plus previous requests)
156+
assert_eq!(conn.requests().len(), 5);
172157
}
173158
}

aws/sdk/integration-tests/s3/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features
2121
aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] }
2222
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
2323
aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test" }
24+
aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["test-util"] }
2425
aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" }
2526
aws-types = { path = "../../build/aws-sdk/sdk/aws-types" }
2627
bytes = "1"

aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ use aws_smithy_types::timeout::TimeoutConfig;
2020
use std::fmt::Debug;
2121
use std::time::{Duration, Instant};
2222

23+
#[cfg(aws_sdk_orchestrator_mode)]
24+
use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs;
25+
2326
#[derive(Debug)]
2427
struct SmolSleep;
2528

@@ -33,6 +36,9 @@ impl AsyncSleep for SmolSleep {
3336

3437
#[test]
3538
fn test_smol_runtime_timeouts() {
39+
#[cfg(aws_sdk_orchestrator_mode)]
40+
let _guard = capture_test_logs();
41+
3642
if let Err(err) = smol::block_on(async { timeout_test(SharedAsyncSleep::new(SmolSleep)).await })
3743
{
3844
println!("{err}");
@@ -42,6 +48,9 @@ fn test_smol_runtime_timeouts() {
4248

4349
#[test]
4450
fn test_smol_runtime_retry() {
51+
#[cfg(aws_sdk_orchestrator_mode)]
52+
let _guard = capture_test_logs();
53+
4554
if let Err(err) = smol::block_on(async { retry_test(SharedAsyncSleep::new(SmolSleep)).await }) {
4655
println!("{err}");
4756
panic!();
@@ -59,6 +68,9 @@ impl AsyncSleep for AsyncStdSleep {
5968

6069
#[test]
6170
fn test_async_std_runtime_timeouts() {
71+
#[cfg(aws_sdk_orchestrator_mode)]
72+
let _guard = capture_test_logs();
73+
6274
if let Err(err) = async_std::task::block_on(async {
6375
timeout_test(SharedAsyncSleep::new(AsyncStdSleep)).await
6476
}) {
@@ -69,6 +81,9 @@ fn test_async_std_runtime_timeouts() {
6981

7082
#[test]
7183
fn test_async_std_runtime_retry() {
84+
#[cfg(aws_sdk_orchestrator_mode)]
85+
let _guard = capture_test_logs();
86+
7287
if let Err(err) =
7388
async_std::task::block_on(async { retry_test(SharedAsyncSleep::new(AsyncStdSleep)).await })
7489
{
@@ -137,7 +152,7 @@ async fn retry_test(sleep_impl: SharedAsyncSleep) -> Result<(), Box<dyn std::err
137152
.region(Region::new("us-east-2"))
138153
.http_connector(conn.clone())
139154
.credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests()))
140-
.retry_config(RetryConfig::standard())
155+
.retry_config(RetryConfig::standard().with_max_attempts(3))
141156
.timeout_config(
142157
TimeoutConfig::builder()
143158
.operation_attempt_timeout(Duration::from_secs_f64(0.1))

codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable
1818
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
1919
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
2020
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
21+
import software.amazon.smithy.rust.codegen.core.util.sdkId
2122

2223
class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenContext) : ConfigCustomization() {
2324
private val runtimeConfig = codegenContext.runtimeConfig
@@ -369,10 +370,10 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon
369370
if (runtimeMode.defaultToOrchestrator) {
370371
rustTemplate(
371372
"""
372-
let retry_partition = layer.load::<#{RetryPartition}>().cloned().unwrap_or_else(|| #{RetryPartition}::new("${codegenContext.serviceShape.id.name}"));
373+
let retry_partition = layer.load::<#{RetryPartition}>().cloned().unwrap_or_else(|| #{RetryPartition}::new("${codegenContext.serviceShape.sdkId()}"));
373374
let retry_config = layer.load::<#{RetryConfig}>().cloned().unwrap_or_else(#{RetryConfig}::disabled);
374375
if retry_config.has_retry() {
375-
#{debug}!("creating retry strategy with partition '{}'", retry_partition);
376+
#{debug}!("using retry strategy with partition '{}'", retry_partition);
376377
}
377378
378379
if retry_config.mode() == #{RetryMode}::Adaptive {

codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.pre
2727
import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations
2828
import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator
2929
import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol
30+
import software.amazon.smithy.rust.codegen.core.util.dq
3031
import software.amazon.smithy.rust.codegen.core.util.inputShape
3132
import software.amazon.smithy.rust.codegen.core.util.outputShape
33+
import software.amazon.smithy.rust.codegen.core.util.sdkId
3234

3335
open class OperationGenerator(
3436
private val codegenContext: ClientCodegenContext,
@@ -142,7 +144,13 @@ open class OperationGenerator(
142144
stop_point: #{StopPoint},
143145
) -> #{Result}<#{InterceptorContext}, #{SdkError}<#{Error}, #{HttpResponse}>> {
144146
let input = #{TypedBox}::new(input).erase();
145-
#{invoke_with_stop_point}(input, runtime_plugins, stop_point).await
147+
#{invoke_with_stop_point}(
148+
${codegenContext.serviceShape.sdkId().dq()},
149+
${operationName.dq()},
150+
input,
151+
runtime_plugins,
152+
stop_point
153+
).await
146154
}
147155
148156
pub(crate) fn operation_runtime_plugins(

codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class OperationRuntimePluginGenerator(
6464
fn config(&self) -> #{Option}<#{FrozenLayer}> {
6565
let mut cfg = #{Layer}::new(${operationShape.id.name.dq()});
6666
use #{ConfigBagAccessors} as _;
67+
6768
cfg.set_request_serializer(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer));
6869
cfg.set_response_deserializer(#{DynResponseDeserializer}::new(${operationStructName}ResponseDeserializer));
6970

codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol
77

8-
import software.amazon.smithy.aws.traits.ServiceTrait
98
import software.amazon.smithy.model.shapes.BlobShape
109
import software.amazon.smithy.model.shapes.OperationShape
1110
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
@@ -31,9 +30,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation
3130
import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol
3231
import software.amazon.smithy.rust.codegen.core.util.dq
3332
import software.amazon.smithy.rust.codegen.core.util.findStreamingMember
34-
import software.amazon.smithy.rust.codegen.core.util.getTrait
3533
import software.amazon.smithy.rust.codegen.core.util.inputShape
3634
import software.amazon.smithy.rust.codegen.core.util.letIf
35+
import software.amazon.smithy.rust.codegen.core.util.sdkId
3736

3837
// TODO(enableNewSmithyRuntimeCleanup): Delete this class when cleaning up `enableNewSmithyRuntime`
3938
/** Generates the `make_operation` function on input structs */
@@ -53,9 +52,7 @@ open class MakeOperationGenerator(
5352
private val defaultClassifier = RuntimeType.smithyHttp(runtimeConfig)
5453
.resolve("retry::DefaultResponseRetryClassifier")
5554

56-
private val sdkId =
57-
codegenContext.serviceShape.getTrait<ServiceTrait>()?.sdkId?.lowercase()?.replace(" ", "")
58-
?: codegenContext.serviceShape.id.getName(codegenContext.serviceShape)
55+
private val sdkId = codegenContext.serviceShape.sdkId()
5956

6057
private val codegenScope = arrayOf(
6158
*preludeScope,

codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package software.amazon.smithy.rust.codegen.core.util
77

8+
import software.amazon.smithy.aws.traits.ServiceTrait
89
import software.amazon.smithy.codegen.core.CodegenException
910
import software.amazon.smithy.model.Model
1011
import software.amazon.smithy.model.shapes.BooleanShape
@@ -146,3 +147,7 @@ fun String.shapeId() = ShapeId.from(this)
146147

147148
/** Returns the service name, or a default value if the service doesn't have a title trait */
148149
fun ServiceShape.serviceNameOrDefault(default: String) = getTrait<TitleTrait>()?.value ?: default
150+
151+
/** Returns the SDK ID of the given service shape */
152+
fun ServiceShape.sdkId(): String =
153+
getTrait<ServiceTrait>()?.sdkId?.lowercase()?.replace(" ", "") ?: id.getName(this)

rust-runtime/aws-smithy-runtime-api/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# aws-smithy-retries
1+
# aws-smithy-runtime-api
22

33
**This crate is UNSTABLE! All internal and external interfaces are subject to change without notice.**
44

5-
Smithy runtime types.
5+
Lightweight crate with traits and types necessary to configure runtime logic in the `aws-smithy-runtime` crate.
66

77
<!-- anchor_start:footer -->
88
This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly.

0 commit comments

Comments
 (0)