You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
lifecycle: a ConnectAdmission decision cannot be revised once served, so a host cannot hold queued dials behind a push-back (follow-up to #1532) #1664
ConnectAdmission::delay is consulted once per run-loop attempt, and the delay it returns is served to the end and followed by connect() without asking the policy again.
A host that paces many clients against one shared budget books each attempt's slot at that moment, so a cooldown the host learns while those clients sleep - one client drawing a temporary ban, a throttled pair-code request - cannot reach any dial already holding a slot.
After a network blip, which is exactly when a queue of reconnects forms, the queue drains at the host's stagger rate into the server that just pushed back.
This is the follow-up to #1532: the admission point exists now, but a decision cannot be revised once it is made.
Evidence
Rev 9e24e4c0; the same code is on main at d804acae.
src/client/lifecycle.rs:899-915 (run's loop): admission.delay() (:903) is called once per attempt, and its result goes straight to wait_for_connect_admission.
src/client/lifecycle.rs:1121-1152 (wait_for_connect_admission): while the delay is served, can_dial() re-checks only shutdown, is_running, pause and pause_generation; when the sleep ends it returns can_dial() (:1147) and the loop proceeds to the attempt. Nothing re-enters the policy between the sleep and the dial.
src/types/connect_admission.rs:5-9: the trait's single synchronous fn delay(&self) -> Duration is documented as consulted once per attempt; "Zero proceeds without sleeping or consulting again".
Why it matters
The synchronous, non-blocking callback is the right shape for the common case and should stay.
What a host pacing a fleet needs on top is one more look at the policy before the dial actually happens, so a decision made seconds ago can be revised by what the host has learned since.
The one lever that exists today is pause()/resume(), which abandons an outstanding reservation and obtains a fresh decision on resume - but it is the host's own pause, which also tears down a live connection, so using it to revise pacing would conflate an operator's intent with a pacing decision and requires the host to track which clients are mid-wait.
Suggested fix
Either re-consult the policy after serving a non-zero delay - a policy that now answers zero proceeds, one that answers non-zero extends the wait - or add an optional second check consulted after the delay (a cheap "still clear to dial?"), defaulting to "proceed" so existing policies are unaffected.
Activity