quinn-proto: add TransportConfig::max_rtt to bound RTT samples#2723
quinn-proto: add TransportConfig::max_rtt to bound RTT samples#2723mjansson wants to merge 1 commit into
Conversation
Every RTT sample feeds directly into the smoothed RTT estimator with no upper bound. When the host is momentarily unable to process incoming packets in a timely fashion -- for example an async runtime starved of CPU, or packets left sitting unread in the socket receive buffer -- an ack can be handled long after it arrived, yielding a spuriously large RTT sample. That single sample poisons the smoothed estimate, which in turn inflates the PTO, the time-based loss detection threshold and the idle timeout, and can leave a connection unable to recover. Add a TransportConfig::max_rtt option that clamps each raw sample before it enters the estimator. It defaults to 2 seconds, comfortably above any legitimate internet RTT, so existing behaviour is unchanged in practice. The clamping is covered by unit tests. Signed-off-by: Mattias Jansson <mattias.jansson@epicgames.com>
|
Linter failure is preexisting and not related to this change as far as I can tell |
|
Can you elaborate on "unable to recover"? It's not obvious to me why the spurious estimate wouldn't get averaged right back down with following traffic. Even if the connection is lost immediately after such an event, surely even an inflated PTO should still resolve in a reasonable amount of time? There should be a hard upper bound at which the connection becomes lost before the delayed ACK is processed anyway. |
|
I'm digging up my old notes here, we had this issue ~4 months ago but due to being in crunch to get Lore released we never go around to putting up the upstream PR. The problem we saw is when the spurious sample is not followed by clean samples. No returning ACKs → no new RTT samples → nothing that average the estimate back down. The estimator is pinned at the poisoned value precisely during the window where recovery has to happen. The timers that are supposed to bound and repair the connection are all keyed off that poisoned estimate, including the idle timeout. In
The effective idle timeout is Loss detection is coupled the same way with Concretely, baseline RTT
And since receiving any packet resets the idle timer to |
The hard upper bound I was referring to was the duration of delay after which the connection times out before the delayed ACK can be processed at all.
I don't follow. If the peer is still communicating, why shouldn't the connection remain open? |
Motivation
Every RTT sample feeds straight into the smoothed RTT estimator with no upper bound. When the host is momentarily unable to process incoming packets in a timely fashion — e.g. an async runtime starved of CPU, or packets sitting unread in the socket receive buffer — an ack can be processed long after it arrived, producing a spuriously large RTT sample. That single sample poisons the smoothed estimate, which inflates the PTO, the time-based loss-detection threshold and the idle timeout, and can leave a connection unable to recover.
We observed exactly this under runtime starvation and now configure a 2s bound on both client and server connections.
Summary
TransportConfig::max_rttoption that clamps each raw RTT sample before it enters the estimator (RttEstimatorgains amaxbound).rtt_estimator_clamps_samples_above_max,rtt_estimator_keeps_samples_below_max).