Skip to content

fix: bound the peer relay connection cache with LRU eviction - #207

Open
thexeos wants to merge 1 commit into
cloudflare:draft-ietf-moq-transport-14from
thexeos:fix/bound-remote-connection-cache-draft-14
Open

fix: bound the peer relay connection cache with LRU eviction#207
thexeos wants to merge 1 commit into
cloudflare:draft-ietf-moq-transport-14from
thexeos:fix/bound-remote-connection-cache-draft-14

Conversation

@thexeos

@thexeos thexeos commented Aug 13, 2026

Copy link
Copy Markdown

Backport of #202 to draft-14. On this branch the knob rides the existing RelayTuning, so no public signature changes.

RemoteManager keeps one cached connection per (url, addr) peer and only drops an entry when that connection dies, so a relay that talks to many peers over its lifetime accumulates pooled QUIC connections without limit, each with its own keep-alive, for the life of the process. This adds a configurable capacity (default 256, 0 disables) with least-recently-used eviction, refreshing recency on cache hits as well as insertions. Eviction only reclaims connections that nothing is using — every caller holds a use guard while it is subscribed, publishing or forwarding over a connection, and an entry with a live guard (or a slot lock held by an in-flight connect) is skipped rather than closed, so a full pool of busy peers stays over capacity instead of cutting off a live subscription. No new dependencies; the LRU is a linear scan over a few hundred entries, run only when a new connection is added.

RemoteManager cached one connection per (url, addr) peer and only ever
dropped an entry when that connection died, so a relay that talks to many
peers over its lifetime accumulated pooled QUIC connections without limit —
each with its own keep-alive — for the life of the process.

Give the pool a configurable capacity (RelayTuning::max_remote_connections,
default 256, 0 disables) with least-recently-used eviction. Recency is
refreshed on cache hits as well as insertion, and eviction only reclaims
entries nothing is using: a subscriber holds a use guard for as long as its
upstream subscription lives, and an entry with a live guard — or whose slot
lock is held by an in-flight connect or teardown — is skipped rather than
closed, so the pool may sit over capacity instead of cutting off a live
subscription. Candidate slots are probed with try_lock while the pool lock
is held, so the existing pool/slot lock order is unchanged and no lock is
held across an await.
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