Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for WebTransport SendOrder to neqo (issue #1432) #1437

Closed
wants to merge 25 commits into from

Conversation

jesup
Copy link
Member

@jesup jesup commented May 18, 2023

No attempt at fairness within a sendorder (or for unordered streams) is attempted.

A few typos are corrected as well

…s within a sendorder (or for unordered streams) is attempted
@jesup
Copy link
Member Author

jesup commented May 19, 2023

I had added the custom Ord impl to switch the order, but didn't actually modify it to switch the order... :-)
Added logging for sendorder changes

@KershawChang
Copy link
Collaborator

I think we should have some tests in neqo as well.
It should be easier to test this in neqo than writing a wpt test.

neqo-http3/src/connection.rs Outdated Show resolved Hide resolved
neqo-http3/src/connection_client.rs Outdated Show resolved Hide resolved
neqo-transport/src/connection/mod.rs Outdated Show resolved Hide resolved
neqo-transport/src/streams.rs Outdated Show resolved Hide resolved
Comment on lines 157 to 160
assert_eq!(
out.as_dgram_ref().is_some(),
(d_num + 1) % usize::try_from(DEFAULT_ACK_PACKET_TOLERANCE + 1).unwrap() == 0
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not really testing for ACKs, so maybe you don't need to worry about checking this.

Copy link
Member Author

@jesup jesup May 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cut-and-paste from the next test, I can remove that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though if I make it a function I might want to keep it

neqo-transport/src/connection/tests/stream.rs Outdated Show resolved Hide resolved
neqo-transport/src/connection/tests/stream.rs Outdated Show resolved Hide resolved
neqo-transport/src/streams.rs Outdated Show resolved Hide resolved
neqo-transport/src/connection/tests/stream.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@KershawChang KershawChang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed there are some failed tests, so I think this patch still has more to do.

neqo-http3/src/connection.rs Outdated Show resolved Hide resolved
neqo-http3/src/connection_client.rs Outdated Show resolved Hide resolved
neqo-transport/src/send_stream.rs Outdated Show resolved Hide resolved
neqo-transport/src/streams.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@KershawChang KershawChang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, but I think this still needs @martinthomson 's approval.

neqo-transport/src/send_stream.rs Outdated Show resolved Hide resolved
neqo-transport/src/connection/tests/stream.rs Outdated Show resolved Hide resolved
neqo-http3/src/connection.rs Outdated Show resolved Hide resolved
// This avoids the complexity of trying to hold references to the
// Streams which are owned by the IndexMap.
sendordered: BTreeMap<SendOrder, HashSet<StreamId>>,
regular: Vec<StreamId>, // streams with no SendOrder set, sorted in stream_id order
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider iterating map instead? You would need to mark any stream that has a sendorder somehow (i.e., skip if self.sendorder.is_some()), but then you save the overhead of maintaining this collection.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly, but: In stream-per-frame (and perhaps most instances where sendorder is used), all or almost all the streams will have sendorders, and so we'll iterate over map but ignore almost or all the iterations. That seems a higher overhead (every time we're sending packets) than the maintenance overhead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Having a count of the number of unordered streams would allow us to add .take(count) when iterating, which would shortcut very nicely. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand your suggestion here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If, rather than maintaining a collection of streams that are unordered, you merely kept a count, then in the two cases we think most likely:

  1. All streams are unordered. You iterate over the main IndexMap directly, but .take(x) where x = self.map.len(), means that it is a full iteration.
  2. All streams are ordered. You iterate over the main IndexMap, but .take(0), which turns into a no-op.

The advantage is that you don't have an indirection for the first iteration; you iterate the collection directly. In the second, you short-circuit that iteration (because iterators are lazy and .take(0) doesn't hit the underlying iterator at all. It's only when you have a mix that you end up paying extra, but that's not a situation we think likely. If there are a few streams that are unordered, then you'll have a few to track, but maybe you can count the number of unordered streams with unsent data so that you have to hit the collection less often.

neqo-transport/src/send_stream.rs Outdated Show resolved Hide resolved
neqo-transport/src/send_stream.rs Outdated Show resolved Hide resolved
neqo-transport/src/send_stream.rs Outdated Show resolved Hide resolved
neqo-transport/src/send_stream.rs Outdated Show resolved Hide resolved
neqo-transport/src/streams.rs Outdated Show resolved Hide resolved
neqo-transport/src/connection/tests/stream.rs Outdated Show resolved Hide resolved
Comment on lines 489 to 491
fn set_sendorder(&mut self, _conn: &mut Connection, _sendorder: Option<SendOrder>) {
// Not relevant on session
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should let this pass through. We will simply not call it in gecko (who knows, maybe this makes sense at some point).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass through to where?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am suggesting that you change the send order, rather than do nothing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sessions don't have a sendorder to set, so I'm still confused by your suggestion. Unless you mean "set the sendorder of all streams to N", which would be unhelpful and there's no reason for it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I'm just suggesting that you change the session stream's sendorder. Yes, it doesn't make much sense, but a no-op is far more surprising than a function that does the expected thing, even if it shouldn't be used.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a debug assertion here?
I assume this function should not be called at all.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this wouldn't work:

self.borrow_mut().control_stream_send.set_sendorder(conn, sendorder)

neqo-http3/src/connection_client.rs Outdated Show resolved Hide resolved
@jesup
Copy link
Member Author

jesup commented May 29, 2023

Updated for almost all the comments; will look at using Vec instead of HashSet for each sendordered group tomorrow

@KershawChang
Copy link
Collaborator

@jesup please also fix all format and clippy errors. Thanks.

Comment on lines 489 to 491
fn set_sendorder(&mut self, _conn: &mut Connection, _sendorder: Option<SendOrder>) {
// Not relevant on session
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this wouldn't work:

self.borrow_mut().control_stream_send.set_sendorder(conn, sendorder)

/// # Errors
/// Error my occure during sending data, e.g. protocol error, etc.
/// Error my occur during sending data, e.g. protocol error, etc.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Error my occur during sending data, e.g. protocol error, etc.
/// Errors might occur during sending data, e.g., protocol error.

@@ -271,6 +271,10 @@ impl SendStream for SendMessage {
self.stream.has_buffered_data()
}

fn set_sendorder(&mut self, _conn: &mut Connection, _sendorder: Option<SendOrder>) {
// Not relevant for SendMessage
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again. self.stream.set_sendorder(conn, sendorder) seems workable here, even if we don't end up using it. (Yes, this requires an additional step to implement that on BufferedStream, but it's a trivial thing.)

Yes, we're building for webtransport, but this type has a direct relationship with an underlying stream and a random noop is a surprise I'd prefer to avoid.

stream_id: StreamId,
}

#[cfg(test)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[cfg(test)]

Comment on lines 34 to 36
if self.sendorder.is_some() && other.sendorder.is_some() {
// We want reverse order (high to low) when both values are specified.
other.sendorder.cmp(&self.sendorder)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternative (I keep coming up with these, sorry) might be better:

Suggested change
if self.sendorder.is_some() && other.sendorder.is_some() {
// We want reverse order (high to low) when both values are specified.
other.sendorder.cmp(&self.sendorder)
if let (Some(s), Some(o)) = (&self.sendorder, &other.sendorder) {
// We want reverse order (high to low) when both values are specified.
o.cmp(s)

Comment on lines 434 to 436
self.send.insert(
new_id, stream,
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really what cargo fmt produces? I'm surprised that it's breaking across lines like this.

Comment on lines 2949 to 2950
/// # Errors
/// None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// # Errors
/// None

Comment on lines 998 to 1000
/// Set the stream SendOrder.
/// # Errors
/// Returns InvalidStreamId if the stream id doesn't exist
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clippy will complain here.

Suggested change
/// Set the stream SendOrder.
/// # Errors
/// Returns InvalidStreamId if the stream id doesn't exist
/// Set the stream `SendOrder`.
/// # Errors
/// `InvalidStreamId` if the stream id doesn't exist.

@martinthomson martinthomson linked an issue Jun 8, 2023 that may be closed by this pull request
@jesup
Copy link
Member Author

jesup commented Aug 30, 2023

This was landed as part of PR #1443

@jesup jesup closed this Aug 30, 2023
@jesup jesup deleted the sendorder branch August 30, 2023 20:01
@jesup jesup restored the sendorder branch September 14, 2023 13:25
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.

Support SendOrder for WebTransportStream
4 participants