Skip to content

Keep pooled HTTP/2 connections alive when their dialer exits - #939

Merged
benoitc merged 2 commits into
masterfrom
fix/h2-shared-conn-lifetime
Sep 21, 2026
Merged

benoitc merged 2 commits into
masterfrom
fix/h2-shared-conn-lifetime

Conversation

@benoitc

@benoitc benoitc commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Replaces #937. Thanks to @smartinio for reporting this, tracing the cause, and the per-stream tracking this builds on.

A pooled HTTP/2 connection stayed owned by the caller that dialed it. When that caller exited, the connection stopped and every other caller's request on it failed with {error, closed}.

#937 fixed it by making the pool the owner, which then needed synchronous registration with deadlines, retiring busy connections and polling for reuse. This takes a smaller route: a shared connection has no owner and stays under hackney_conn_sup. Each stream monitors its own caller and is reset if that caller dies. The connection closes itself once it has had no open stream for the pool timeout. The pool stays a registry, and registration is still a cast.

It also fixes a slot leak: unregistering a checked-out HTTP/2 connection dropped the pool's monitor on it, so its per-host slot was never released when it stopped.

Connections that are not pooled keep their owner. A streaming upload still blocks its shared connection (streaming_body); moving uploads to per-stream state is a separate change.

A pooled HTTP/2 connection stayed owned by the caller that dialed it, so
when that caller exited the connection stopped and every other caller's
request on it failed with {error, closed}.

A shared connection now has no owner. hackney shares it before
registering it with the pool, each stream monitors its own caller and is
reset if that caller dies, and the connection closes itself once it has
had no open stream for the pool timeout. The pool stays a registry and
releases the per-host slot when the connection stops. Connections that
are not pooled keep their owner.

Unregistering a checked-out HTTP/2 connection also dropped the pool's
monitor on it, so its per-host slot was never released; it now keeps
the monitor.

Reported and diagnosed by @smartinio in #937, whose per-stream tracking
this builds on.
The handler answered the stream the client had just reset. On slow
runners that response crossed the RST_STREAM, and h2 drops a header
block for a reset stream without decoding it, so the HPACK tables fell
out of sync and the next response on the connection failed to decode.
That is an h2 issue of its own; this test only needs the stream reset.
@benoitc
benoitc merged commit ac1772d into master Sep 21, 2026
11 of 12 checks passed
@benoitc
benoitc deleted the fix/h2-shared-conn-lifetime branch September 21, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant