Skip to content

Repository files navigation

Keepalive Mismatch Lab

Every request returns 200 OK. The transport fails 4,800 times.

This interactive lab models an HTTP/1.1 idle-timeout mismatch. An upstream shortens its timeout from 60 seconds to 30 seconds while a client pool still keeps sockets for 60 seconds. Hot and warm routes stay inside the new boundary; a sparse route wakes after 45 seconds and finds that the other side already discarded its connection.

The deterministic simulation and HTTP service are executable CoffeeScript. A dependency-free browser workbench compares the hidden cost of retries with three lifetime policies.

Default result

The model runs 500 idempotent GETs per second for 600 seconds. At second 120, the upstream timeout changes from 60 seconds to 30 seconds. Two percent of traffic belongs to a sparse 45-second connection cohort.

Strategy HTTP success Transport success Resets Connection opens Sparse p99 Background probes
Retry mask 100% 98.43% 4,800 4,800 49 ms 0
Fleet-wide short TTL 100% 100% 0 48,000 32 ms 0
Heartbeat every socket 100% 100% 0 0 8 ms 30,000
Route-aware lifetime 100% 100% 0 4,800 32 ms 0

The logical-request metric says all four strategies are identical. The attempt ledger shows very different operational costs:

  • Retry mask: preserves every response, but sparse requests pay reset detection, retry backoff, and a new handshake.
  • Fleet-wide short TTL: prevents stale reuse by churning healthy warm connections too.
  • Heartbeat: removes request-path failures at the cost of continual background traffic across the idle fleet.
  • Route-aware lifetime: applies early eviction only where idle age crosses the new upstream boundary.

Why telemetry matters

An HTTP status describes the logical request. ECONNRESET belongs to one attempt on one persistent connection. The causal record is:

trace_id
+ request_attempt
+ connection_id
+ connection_age_ms
+ idle_age_ms
+ client_idle_timeout_ms
+ upstream_idle_timeout_ms
+ socket_error

Keeping those fields together lets telemetry.sh expose a retry that otherwise looks like ordinary latency, isolate the sparse route, identify the timeout boundary, and compare request-path recovery with connection churn or heartbeat traffic.

This differs from connection-pool saturation: the pool has enough capacity. The failure is disagreement about how long an idle socket remains reusable.

Run it

Requirements: Node.js 20 or newer and npm.

npm install
make run

Open http://localhost:8080.

Run the CoffeeScript model directly:

npm run model

Override model inputs through environment variables:

REQUESTS_PER_SECOND=1000 \
SPARSE_ROUTE_PERCENT=5 \
UPSTREAM_TIMEOUT_AFTER_SECONDS=20 \
SPARSE_GAP_SECONDS=35 \
npm run model

Or use the container:

docker build -t keepalive-mismatch-lab .
docker run --rm -p 8080:8080 keepalive-mismatch-lab

Verify it

The checks cover deterministic results, boundary and relational clamping, safe and ineffective heartbeat policies, CoffeeScript execution, JavaScript syntax, invalid HTTP input, security headers, the API contract, and responsive page structure.

npm install
make check
make container-test

GitHub Actions runs both the native and production-container suites.

Standards context

RFC 9112, section 9.5 explains that persistent-connection timeout lengths are not prescribed and that a client, server, or proxy may close the transport at any time—including while one peer considers it idle and the other has begun a request. Its retry section says implementations need to anticipate asynchronous closes.

RFC 9110, section 9.2.2 defines idempotent methods and permits automatic recovery after a connection failure. It cautions against automatically retrying non-idempotent methods unless the client can establish idempotent semantics or know the original request was not applied. This lab therefore models GET requests only.

Model boundaries

This is a deterministic teaching model, not a network benchmark. Route gaps represent connection cohorts rather than a specific HTTP client implementation. Real close detection, pool eviction, TCP behavior, proxy configuration, request replay, and handshake costs vary.

The lab opens no workload sockets; it safely models the lifecycle and telemetry curves instead of disrupting the host network.

Built to demonstrate why request and transport evidence belong together with telemetry.sh.

About

Interactive CoffeeScript lab exposing stale keepalive reuse, masked retries, and HTTP idle-timeout mismatches.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages