Skip to content

Smithys RecordingClient does not work for DynamoDB ClientΒ #1310

@CaptainJiNX

Description

@CaptainJiNX

Describe the bug

Using the example from https://github.com/awslabs/aws-sdk-rust/blob/main/sdk/aws-smithy-http-client/src/test_util/dvr/record.rs but with dynamodb client (for instance just a list tables command) makes the recording hang. It works for S3, but not for DynamoDB. After the request times out and all recorded events are dumped to a file, the file contains only the request event.

Regression Issue

  • Select this option if this issue appears to be a regression.

Expected Behavior

I expect the request to succeed, and the request and response events should be present in the dump file.

Current Behavior

The request never completes when using the recording client.

Reproduction Steps

Run the following rust code:

main.rs

use aws_config::{BehaviorVersion, Region};
use aws_smithy_async::rt::sleep::default_async_sleep;
use aws_smithy_http_client::test_util::dvr::RecordingClient;
use aws_smithy_runtime::client::http::hyper_014::default_connector;
use aws_smithy_runtime_api::client::http::HttpConnectorSettingsBuilder;

#[tokio::main]
async fn main() {
    let settings = HttpConnectorSettingsBuilder::default().build();
    let http_client = default_connector(&settings, default_async_sleep()).unwrap();
    let http_client = RecordingClient::new(http_client);

    let config = aws_config::load_defaults(BehaviorVersion::latest()).await;

    let ddb_config = aws_sdk_dynamodb::Config::from(&config)
        .to_builder()
        .http_client(http_client.clone())
        .region(Region::new("eu-west-1"))
        .build();

    let ddb_client = aws_sdk_dynamodb::Client::from_conf(ddb_config);

    let response = ddb_client.list_tables().send().await.unwrap();

    println!("Response: {:#?}", response);

    http_client.dump_to_file("recording.json").unwrap();
}

cargo.toml

[package]
name = "recorder-test"
version = "0.1.0"
edition = "2024"

[dependencies]
aws-config = "1.8.0"
aws-sdk-dynamodb = "1.80.0"
aws-smithy-async = "1.2.5"
aws-smithy-http-client = { version = "1.0.5", features = ["test-util"] }
aws-smithy-runtime = { version = "1.8.3" }
aws-smithy-runtime-api = "1.8.1"
tokio = { version = "1.45.1", features = ["macros", "rt-multi-thread"] }

Possible Solution

It seems to be related to the complex task and channel handling in the recorder. If we clone the request body, it seems to work as expected.

diff --git a/sdk/aws-smithy-http-client/src/test_util/dvr/record.rs b/sdk/aws-smithy-http-client/src/test_util/dvr/record.rs
index 51759dfeba3..d7f900dfac0 100644
--- a/sdk/aws-smithy-http-client/src/test_util/dvr/record.rs
+++ b/sdk/aws-smithy-http-client/src/test_util/dvr/record.rs
@@ -131,8 +131,16 @@ fn record_body(
     direction: Direction,
     event_bus: Arc<Mutex<Vec<Event>>>,
 ) -> JoinHandle<()> {
-    let (sender, output_body) = crate::test_util::body::channel_body();
-    let real_body = std::mem::replace(body, output_body);
+    // For cloneable bodies (like small JSON payloads), record from a clone
+    // to avoid disrupting HTTP semantics. This fixes issues with services
+    // like DynamoDB that are sensitive to body handling.
+    let (real_body, sender) = if let Some(cloned_body) = body.try_clone() {
+        (cloned_body, None)
+    } else {
+        let (sender, output_body) = crate::test_util::body::channel_body();
+        (std::mem::replace(body, output_body), Some(sender))
+    };
+
     tokio::spawn(async move {
         let mut real_body = real_body;
         let mut sender = sender;
@@ -147,18 +155,22 @@ fn record_body(
                             direction,
                         },
                     });
-                    // This happens if the real connection is closed during recording.
-                    // Need to think more carefully if this is the correct thing to log in this
-                    // case.
-                    if sender.send_data(data).await.is_err() {
-                        event_bus.lock().unwrap().push(Event {
-                            connection_id: event_id,
-                            action: Action::Eof {
-                                direction: direction.opposite(),
-                                ok: false,
-                            },
-                        })
-                    };
+
+                    if let Some(ref mut sender) = sender {
+                        // This happens if the real connection is closed during recording.
+                        // Need to think more carefully if this is the correct thing to log in this
+                        // case.
+                        if sender.send_data(data).await.is_err() {
+                            event_bus.lock().unwrap().push(Event {
+                                connection_id: event_id,
+                                action: Action::Eof {
+                                    direction: direction.opposite(),
+                                    ok: false,
+                                },
+                            });
+                            break;
+                        }
+                    }
                 }
                 None => {
                     event_bus.lock().unwrap().push(Event {
@@ -168,7 +180,9 @@ fn record_body(
                             direction,
                         },
                     });
-                    drop(sender);
+                    if let Some(sender) = sender {
+                        drop(sender);
+                    }
                     break;
                 }
                 Some(Err(_err)) => {
@@ -179,7 +193,9 @@ fn record_body(
                             direction,
                         },
                     });
-                    sender.abort();
+                    if let Some(sender) = sender {
+                        sender.abort();
+                    }
                     break;
                 }
             }

Additional Information/Context

No response

Version

aws-config = "1.8.0"
aws-sdk-dynamodb = "1.80.0"
aws-smithy-async = "1.2.5"
aws-smithy-http-client = { version = "1.0.5", features = ["test-util"] }
aws-smithy-runtime = { version = "1.8.3" }
aws-smithy-runtime-api = "1.8.1"

Environment details (OS name and version, etc.)

MacOS Sonoma 14.6.1, Apple M2 Pro

Logs

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis issue is a bug.p2This is a standard priority issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions