Skip to content

Commit 282be2a

Browse files
committed
fix: treat a zero pool idle timeout as no pooling at all
AWS_LWA_POOL_IDLE_TIMEOUT_SECONDS=0 asks for no keep-alive, but passing Duration::ZERO to hyper does not deliver it: expiry is decided lazily at checkout via saturating_duration_since(idle_at) > timeout, and hyper skips spawning its idle-eviction task entirely for a zero timeout. The socket is therefore parked after the response and only dropped when the next checkout evicts it — a file descriptor held open for a connection no request may reuse. build_client now maps a zero idle timeout onto the same pool_max_idle_per_host(0) that Pooling::Disabled uses, so the connection is dropped as the response completes. Documented in the README and guide env-var tables, which did not mention the value before.
1 parent 9484cef commit 282be2a

4 files changed

Lines changed: 29 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@
2222
consumers keep the last published release (`1.0.0-rc1`) unchanged.
2323
- Add `AWS_LWA_POOL_IDLE_TIMEOUT_SECONDS` to configure the idle keep-alive
2424
(fractional seconds allowed, e.g. `0.5`) of the adapter's HTTP connection to your
25-
app. Default: 4 seconds. A value that is set but unusable falls back to the
26-
default and logs a warning.
25+
app. Default: 4 seconds. `0` turns connection reuse off entirely rather than relying
26+
on expiry, so every request opens a fresh connection. A value that is set but
27+
unusable falls back to the default and logs a warning.
2728
- Add `AWS_LWA_READINESS_CHECK_TIMEOUT_SECONDS` to bound the readiness check
2829
(fractional seconds allowed, e.g. `0.5`), applied to both the initial cold-start
2930
readiness wait and the post-SnapStart-restore readiness check. When set and the app

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ The readiness check port/path and traffic port can be configured using environme
6969
| AWS_LWA_LAMBDA_RUNTIME_API_PROXY | overwrites `AWS_LAMBDA_RUNTIME_API` to allow proxying request | None |
7070
| AWS_LWA_SNAPSTART_BEFORE_CHECKPOINT_PATH | inner-app path the adapter POSTs to before a SnapStart snapshot (drain resources) | None |
7171
| AWS_LWA_SNAPSTART_AFTER_RESTORE_PATH | inner-app path the adapter POSTs to after a SnapStart restore (reconnect/reseed) | None |
72-
| AWS_LWA_POOL_IDLE_TIMEOUT_SECONDS | idle keep-alive (seconds) for the adapter's connection to your app | "4" |
72+
| AWS_LWA_POOL_IDLE_TIMEOUT_SECONDS | idle keep-alive (seconds, fractional allowed) for the adapter's connection to your app; `0` turns connection reuse off entirely | "4" |
7373
| AWS_LWA_READINESS_CHECK_TIMEOUT_SECONDS | seconds (fractional allowed, e.g. 0.5) to wait for the app to report ready (cold-start init and after a SnapStart restore); on expiry the adapter FAILS (init fails and the runtime never starts; a restore fails) rather than serving. Unset, 0, or negative all mean wait indefinitely (a set-but-<=0 or malformed value is ignored with a warning). async_init keeps its own ~9.8s bound | unset / <=0 (unbounded) |
7474

7575
> **Deprecation Notice:** The following non-namespaced environment variables are deprecated and will be removed in version 2.0:

docs/guide/src/configuration/environment-variables.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ All configuration is done through environment variables, set either in your Dock
2121
| `AWS_LWA_LAMBDA_RUNTIME_API_PROXY` | Proxy URL for Lambda Runtime API requests | None |
2222
| `AWS_LWA_SNAPSTART_BEFORE_CHECKPOINT_PATH` | Inner-app path the adapter POSTs to before a SnapStart snapshot | None |
2323
| `AWS_LWA_SNAPSTART_AFTER_RESTORE_PATH` | Inner-app path the adapter POSTs to after a SnapStart restore | None |
24-
| `AWS_LWA_POOL_IDLE_TIMEOUT_SECONDS` | Idle keep-alive (seconds) for the adapter's connection to your app | `4` |
24+
| `AWS_LWA_POOL_IDLE_TIMEOUT_SECONDS` | Idle keep-alive (seconds, fractional allowed, e.g. `0.5`) for the adapter's connection to your app. `0` turns connection reuse off entirely, so every request opens a fresh connection. A set-but-unusable value falls back to the default with a `warn!`. | `4` |
2525
| `AWS_LWA_READINESS_CHECK_TIMEOUT_SECONDS` | Seconds (fractional allowed, e.g. `0.5`) the adapter waits for the app to report ready (cold-start init **and** after a SnapStart restore). On expiry the adapter **fails** rather than serving: cold-start init fails (the runtime never starts) and a restore fails. Unset, `0`, or a negative value all mean **wait indefinitely** (no bound); a set-but-`<= 0` or malformed value is ignored with a `warn!`. The `async_init` path keeps its own ~9.8s bound (non-fatal) and is unaffected. | unset / `<= 0` (unbounded) |
2626

2727
## Deprecated Variables

src/lib.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -827,16 +827,20 @@ pub struct Adapter<C, B> {
827827
///
828828
/// [`Pooling::Disabled`] sets `pool_max_idle_per_host(0)`, turning hyper's pool off
829829
/// outright so reuse is impossible by construction. A zero `idle_timeout` is NOT a
830-
/// substitute: expiry is decided by `saturating_duration_since(idle_at) > timeout`,
831-
/// which reads as not-expired when the clock has not advanced (`ZERO > ZERO` is
832-
/// false) — the very condition hyper#3810 / rust-lang/rust#79462 describe.
830+
/// substitute for that, so it is mapped onto the same setting rather than trusted to
831+
/// expire entries: hyper decides expiry with
832+
/// `saturating_duration_since(idle_at) > timeout`, which reads as not-expired when the
833+
/// clock has not advanced (`ZERO > ZERO` is false) — the very condition hyper#3810 /
834+
/// rust-lang/rust#79462 describe — and it skips spawning the idle-eviction task
835+
/// altogether for a zero timeout, so on its own zero would park a socket that no
836+
/// request may reuse until the next checkout evicts it.
833837
///
834838
/// Reads no environment: the caller decides, so the post-restore rebuild cannot
835839
/// inherit the pre-snapshot restriction.
836840
fn build_client(idle_timeout: Duration, pooling: Pooling) -> Client<HttpConnector, Body> {
837841
let mut builder = Client::builder(hyper_util::rt::TokioExecutor::new());
838842
builder.pool_idle_timeout(idle_timeout);
839-
if pooling == Pooling::Disabled {
843+
if pooling == Pooling::Disabled || idle_timeout.is_zero() {
840844
builder.pool_max_idle_per_host(0);
841845
}
842846
builder.build(HttpConnector::new())
@@ -1748,7 +1752,8 @@ mod tests {
17481752
assert_eq!(pool_idle_timeout_from_env(), Duration::from_secs(30));
17491753
assert_eq!(AdapterOptions::default().pool_idle_timeout, Duration::from_secs(30));
17501754

1751-
// Zero is honored (disables idle keep-alive by timeout).
1755+
// Zero is honored; `build_client` turns the pool off for it, so no connection
1756+
// is kept alive at all (see `test_zero_idle_timeout_disables_pooling`).
17521757
std::env::set_var(ENV_POOL_IDLE_TIMEOUT_SECONDS, "0");
17531758
assert_eq!(pool_idle_timeout_from_env(), Duration::from_secs(0));
17541759

@@ -1931,6 +1936,20 @@ mod tests {
19311936
);
19321937
}
19331938

1939+
/// `AWS_LWA_POOL_IDLE_TIMEOUT_SECONDS=0` asks for no keep-alive at all, so
1940+
/// `build_client` must turn the pool off for it. Left to hyper's expiry the socket
1941+
/// would instead be parked until the next checkout evicts it — a file descriptor
1942+
/// held open for a connection nothing may reuse.
1943+
#[tokio::test]
1944+
async fn test_zero_idle_timeout_disables_pooling() {
1945+
let client = build_client(Duration::ZERO, Pooling::Enabled);
1946+
let retained = connection_retained_after_request(&client).await;
1947+
assert!(
1948+
!retained,
1949+
"a zero idle timeout must DROP the connection, not park it in the idle pool"
1950+
);
1951+
}
1952+
19341953
/// `AWS_LWA_ASYNC_INIT` must be ignored under SnapStart and Provisioned
19351954
/// Concurrency, where its premise does not hold.
19361955
///

0 commit comments

Comments
 (0)