Skip to content

ConnectionPool: per-peer backlog blocks unrelated-peer dispatch (bounded head-of-line stall) #2

Description

@cbenhagen

Tracking issue, follow-up to the deadlock PR in #1. Not a blocker. The fix bounds the wedge to one connect_timeout per slow peer, but the underlying dispatch pattern remains: the pool main loop is the only dispatcher for all peers, so each cleared backlog still costs every other peer one stall window.

Actor::handle_msg awaits entry.tx.send(msg) into a per-connection mpsc(PER_CONNECTION_INBOX = 100). When concurrency for one slow-to-connect peer exceeds 100 (the P_pending shape from #1: a peer whose endpoint.connect doesn't fail fast), that send parks for up to one connect_timeout window. The connection actor is suspended in connect_timeout and isn't draining its inbox; the actor's eventual rx close is what unblocks the pool. While parked, no other peer's RequestRef is dispatched. Worst case across N distinct slow peers each over the cap: roughly N × connect_timeout dispatch latency for an unrelated request.

Hits realistic workloads where unreachable peers don't fail fast: iroh-docs against many providers behind unstable NAT, or a startup-burst against a stale candidate list.

Mitigations (all deferred from the deadlock PR):

  • try_send on conn_tx in dispatch. Adds a Backpressure error variant for callers.
  • Failure caching with TTL. Sidesteps dispatch for known-unreachable peers; needs an invalidation story.
  • Per-peer concurrency cap at get_or_connect entry. Admission instead of absorption.
  • Per-peer dispatch tasks. Largest restructure, cleanest end state.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions