Skip to content

transport: per-stream send priority API + priority-aware drain#243

Merged
ch4r10t33r merged 1 commit into
masterfrom
feat/191-stream-priority
Jul 10, 2026
Merged

transport: per-stream send priority API + priority-aware drain#243
ch4r10t33r merged 1 commit into
masterfrom
feat/191-stream-priority

Conversation

@ch4r10t33r

Copy link
Copy Markdown
Collaborator

Closes #191. Last open implementation item from the #138 gap tracker (with #192/BBR deferred as backlog).

Summary

quinn SendStream::set_priority(i32) equivalent:

  • Server.setStreamPriority(conn, stream_id, priority) / Client.setStreamPriority(stream_id, priority) — default 0, higher drains first, 0 clears.
  • Priority-aware drain: drainPendingStreamSends (both sides) serves strictly descending priority tiers. Within a tier the walk keeps arrival order, one chunk per entry per pass — equal-priority streams round-robin exactly as today. With no priorities set there is exactly one tier and the order is byte-identical to the current FIFO.
  • fix(io): reserve per-conn pending-send headroom for priority (gossip) streams #236 compatibility: the binary priority_stream_ids set becomes an i32 map; markStreamPriority/unmarkStreamPriority stay as shims (set 1 / clear). The pending-send byte-budget headroom keys on priority > 0 — same semantics as before; negative priorities order below default without claiming the gossip headroom.

Semantics notes

  • Strict tiers match quinn's scheduler (priority DESC, recency ASC); a persistently-full high tier can starve lower tiers — that is the documented quinn behavior too, and the per-drive send budget bounds it.
  • Shared caps unchanged: per-drive send budget + per-call cap span all tiers; CC/pacer block aborts the whole drain as before; entries FC-blocked in a higher tier are not double-sent on lower passes (tier filter skips them).
  • Tier selection is O(entries) per tier — O(entries × distinct priorities) per drain call. libp2p's use is two classes (control vs bulk).

Tests

  • Priority set/clear, budget headroom for positive vs negative priority.
  • nextPriorityTierBelow descending tier walk (5 → 0 → −2 → null).
  • Loopback ordering proof: with the per-drive budget capped to one send, the drain picks the high-priority entry over an earlier-arrived low-priority one; shim-clear restores default.
  • 299/300 unit tests pass (1 pre-existing skip).

Local interop: handshake green; transfer/multiplexing flake locally on clean master too (NS3-on-macOS environment) — the CI interop gate is authoritative here.

quinn SendStream::set_priority equivalent. The pending-send drain now
serves strictly descending priority tiers; within a tier the walk keeps
arrival order and emits one chunk per entry per pass, so equal-priority
streams round-robin exactly as before.

- ConnState: `priority_stream_ids` (binary void-set from #236) becomes
  `stream_priorities: AutoHashMapUnmanaged(u64, i32)`. New helpers:
  `streamPriority(sid)` (0 default) and `nextPriorityTierBelow(bound)`
  (the tier selector — O(entries) per tier, O(entries × distinct
  priorities) per drain call; libp2p uses two classes in practice).

- Public API: `Server.setStreamPriority(conn, sid, i32)` /
  `Client.setStreamPriority(sid, i32)`. Priority 0 clears the entry.
  Existing `markStreamPriority` / `unmarkStreamPriority` become shims
  (set 1 / clear) — embedder-compatible, and the #236 byte-budget
  headroom now keys on `priority > 0`, preserving its semantics
  (negative priority orders below default WITHOUT the headroom).

- drainPendingStreamSends (server + client): outer tier loop wraps the
  existing walk with a tier filter. All existing gates unchanged —
  per-drive send budget and per-call cap are shared across tiers
  (`break :outer`), CC/pacer block still aborts the whole drain,
  FC-blocked entries in a higher tier are skipped on lower passes by
  the filter (no double-send). With no priorities set there is exactly
  one tier (0) and the walk order is identical to before.

Tests: priority set/clear + budget headroom (positive vs negative),
tier-selector descending walk, and a loopback proof that a
one-send-budget drain picks the high-priority entry over an
earlier-arrived low-priority one, then shim-clear restores default.
299/300 pass (1 pre-existing skip). Local interop: handshake green;
transfer/multiplexing flake locally on clean master too (NS3-on-macOS
environment, not this change) — CI interop gate is authoritative.
@ch4r10t33r ch4r10t33r merged commit 52c1c96 into master Jul 10, 2026
9 of 10 checks passed
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.

No stream priority API (no equivalent of quinn's SendStream::set_priority)

1 participant