Skip to content

Send window update on STREAM_DATA_BLOCKED rx? #2183

@mxinden

Description

@mxinden

If I understand the code below correctly, Quiche currently ignores incoming StreamDataBlocked frames, i.e. ignores if a sender signals that it is blocked on the provided stream data flow control window:

frame::Frame::StreamDataBlocked { .. } => (),

If that is correct, have you considered sending a stream flow control update instead? More specifically call FlowControl::max_data_next and send a window update:

/// Returns the new max_data limit.
pub fn max_data_next(&self) -> u64 {
self.consumed + self.window
}

Maybe additionally call FlowControl::autotune_window in case the BDP is larger than the stream window.


As a sender Firefox's QUIC stack will send a STREAM_DATA_BLOCKED when attempting to send more than the granted stream window allows. I.e. it will send notify the receiver ahead of time that it will be blocked soon.

https://github.com/mozilla/neqo/blob/98b69aabc1852b65242d59c5775e3b167e8f2a8d/neqo-transport/src/send_stream.rs#L1218-L1242

As a receiver Firefox's QUIC stack will send a flow control update when it receives a STREAM_DATA_BLOCKED:

https://github.com/mozilla/neqo/blob/98b69aabc1852b65242d59c5775e3b167e8f2a8d/neqo-transport/src/recv_stream.rs#L758-L765

Before sending it will auto-tune the window:

https://github.com/mozilla/neqo/blob/98b69aabc1852b65242d59c5775e3b167e8f2a8d/neqo-transport/src/fc.rs#L382


This showed up while benchmarking Firefox's QUIC stack using h3.speed.cloudflare.com.

Throughout a transfer, not just during the initial BDP auto tuning ramp up, Firefox is repeatedly blocked on the stream flow control limit. Example:

$ RUST_LOG=neqo_bin=info cargo run --bin neqo-client -- -m=POST --stats --upload-size=100000000 https://h3.speed.cloudflare.com/__up?measId=XXX

6.193 INFO stats for Client ...
  rx: 14582 drop 3 dup 0 saved 3
  tx: 73061 lost 172 lateack 0 ptoack 0 unackdrop 0
  pmtud: 6 sent 3 acked 3 lost 0 change 1500 iface_mtu 1470 pmtu
  resumed: false
  frames rx:
    crypto 6 done 1 token 0 close 0
    ack 14567 (max 73051) ping 569 padding 0
    stream 5 reset 0 stop 0
    max: stream 0 data 14 stream_data 24
    blocked: stream 0 data 0 stream_data 0
    datagram 0
    ncid 0 rcid 0 pchallenge 0 presponse 0
    ack_frequency 0
  frames tx:
    crypto 4 done 0 token 0 close 1
    ack 590 (max 14842) ping 7 padding 6
    stream 72950 reset 0 stop 0
    max: stream 1 data 0 stream_data 0
    blocked: stream 0 data 0 stream_data 21
    datagram 0
    ncid 0 rcid 0 pchallenge 0 presponse 0
    ack_frequency 0
  ecn:
    tx:
      Initial Count({NotEct: 4, Ect1: 0, Ect0: 0, Ce: 0})
      Handshake Count({NotEct: 2, Ect1: 0, Ect0: 0, Ce: 0})
      Short Count({NotEct: 73036, Ect1: 0, Ect0: 19, Ce: 0})
    acked:
    rx:
      Initial Count({NotEct: 4, Ect1: 0, Ect0: 0, Ce: 0})
      Handshake Count({NotEct: 3, Ect1: 0, Ect0: 0, Ce: 0})
      Short Count({NotEct: 14572, Ect1: 0, Ect0: 0, Ce: 0})
    path validation outcomes: ValidationCount({Capable: 0, NotCapable(BlackHole): 0, NotCapable(Bleaching): 1, NotCapable(ReceivedUnsentECT1): 0})
    mark transitions:
  dscp: Cs0: 14585 

Note the blocked: stream 0 data 0 stream_data 21, which means that Firefox has been blocked 21 times on stream flow control. Also note the low number of stream window updates sent by Cloudflare max: stream 0 data 14 stream_data 24, i.e. 24.


Caught by @larseggert 🙏
Related: #1384
Side note: Thank you for h3.speed.cloudflare.com! Very helpful.

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