From c06a65c8d46a667036adf70672a53025c7c6304c Mon Sep 17 00:00:00 2001 From: Harold Sun Date: Fri, 20 Mar 2026 06:35:00 +0000 Subject: [PATCH] fix: disable connection pooling to prevent stale connections after SnapStart hyper-util's connection pool uses std::time::Instant (CLOCK_MONOTONIC) to track idle connection age. After Lambda SnapStart restore (or freeze/thaw cycles), CLOCK_MONOTONIC can be inconsistent, causing the pool to reuse dead connections and resulting in IncompleteMessage errors. This is a known upstream issue (hyperium/hyper#3810, rust-lang/rust#79462) with no fix planned in hyper-util or Rust stdlib. Disable connection pooling entirely via pool_max_idle_per_host(0). Since the adapter communicates with localhost, the overhead of creating a new TCP connection per request is negligible (microseconds). Closes #604 --- src/lib.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 60677209..7c79b318 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -520,8 +520,12 @@ impl Adapter { /// Creates a new HTTP Adapter instance. /// /// This function initializes a new HTTP client configured to communicate with - /// your web application. The client uses connection pooling with a 4-second - /// idle timeout for optimal Lambda performance. + /// your web application. When Lambda SnapStart is detected + /// (`AWS_LAMBDA_INITIALIZATION_TYPE=snap-start`), connection pooling is + /// disabled to prevent stale connections after restore, where + /// `CLOCK_MONOTONIC` inconsistencies can cause hyper's pool to reuse dead + /// connections. Otherwise, a 4-second idle timeout is used for connection + /// pooling. /// /// # Arguments /// @@ -546,9 +550,19 @@ impl Adapter { /// let adapter = Adapter::new(&options).expect("Failed to create adapter"); /// ``` pub fn new(options: &AdapterOptions) -> Result, Error> { - let client = Client::builder(hyper_util::rt::TokioExecutor::new()) - .pool_idle_timeout(Duration::from_secs(4)) - .build(HttpConnector::new()); + let mut builder = Client::builder(hyper_util::rt::TokioExecutor::new()); + + // When running under SnapStart, CLOCK_MONOTONIC can be inconsistent after + // restore, causing hyper's pool to reuse dead connections (hyper#3810, + // rust-lang/rust#79462). Disable pooling in that case. For localhost + // communication the overhead of new TCP connections is negligible. + if env::var("AWS_LAMBDA_INITIALIZATION_TYPE").as_deref() == Ok("snap-start") { + builder.pool_max_idle_per_host(0); + } else { + builder.pool_idle_timeout(Duration::from_secs(4)); + } + + let client = builder.build(HttpConnector::new()); let schema = "http";