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

quic: make server connection defer sending packets during processing incoming packets #22796

Merged
merged 40 commits into from
Sep 2, 2022

Conversation

danzh2010
Copy link
Contributor

Commit Message: enable defer sending in QuicConnection::ProcessUdpPacket() on server side. This bundles packet writes for each connection at the end of the event loop instead of each packet read. It is for improving CPU cost of sendmsg() for QUIC listener, especially with UDP GSO batch writing.

Also change how closing connections are deferred during L4 filters creation. Without this change, the connection close is deferred till handling all types of delayed close in QuicSession::OnCanWrite() which ProcessUdpPacket() would call into upon handling the 1st incoming packet. With this change, connection close caused by L4 filter creation is handled right before calling ProcessUdpPacket() because OnCanWrite() is deferred.

Risk Level: medium, behavior change of quic write pipeline
Testing: existing tests
Docs Changes: N/A
Release Notes: N/A
Platform Specific Features: N/A
Runtime guard: envoy_reloadable_features_quic_defer_send_in_response_to_packet

Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
@danzh2010 danzh2010 changed the title Defersending quic: make server connection defer sending packets during processing incoming packets Aug 22, 2022
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
@RyanTheOptimist RyanTheOptimist self-assigned this Aug 24, 2022
@RyanTheOptimist
Copy link
Contributor

Looks like //test/integration:quic_protocol_integration_test is failing. Is that expected?

Copy link
Contributor

@RyanTheOptimist RyanTheOptimist left a comment

Choose a reason for hiding this comment

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

I might be missing it, but do we have test which verifies the intended behavior for this change? Specifically that this "bundles packet writes for each connection at the end of the event loop instead of each packet read. " I was expecting to see a test were multiple packets were read and the flag controlled when packets were sent?

source/common/quic/envoy_quic_server_connection.cc Outdated Show resolved Hide resolved
if (close_type_during_initialize_.has_value()) {
close(close_type_during_initialize_.value());
close_type_during_initialize_ = absl::nullopt;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think there's any need for a feature flag to control this part of the change? Presumably you're confident that it's a safe change? Or should we guard it with the same flag?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's guarded by close_type_during_initialize_. If the current feature flag is on, close_type_during_initialize_ wont' have value here, as OnCanWrite() happens outside of ProcessUdpPacket().

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think I understand. maybeApplyDelayClosePolicy() is called from a couple of different places. Before this PR, it's possible for maybeApplyDelayClosePolicy() to actually call close() if not in delayed close (on line 150). But with this PR, if we're not in delayed close then maybeApplyDelayClosePolicy() will not call close().

But I guess you're saying that before this PR, close_type_during_initialize_ will never have a value here. Do I have that right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

maybeApplyDelayClosePolicy() is only called in OnCanWrite() which was called at the end of ProcessUdpPacket() and various other places before this PR. But in this PR, we check close_type_during_initialize_ at the beginning of ProcessUdpPacket() and close connection there. So OnCanWrite() won't be called at all in ProcessUdpPacket(). This check only takes effect at processing the 1st packet, close_type_during_initialize_ will always be nullptr afterwards.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, I see. Thanks for the explanation.

@danzh2010
Copy link
Contributor Author

I might be missing it, but do we have test which verifies the intended behavior for this change? Specifically that this "bundles packet writes for each connection at the end of the event loop instead of each packet read. " I was expecting to see a test were multiple packets were read and the flag controlled when packets were sent?

I modified test/common/quic/active_quic_listener_test.cc to check that send alarm is armed after receiving CHLO.

@danzh2010
Copy link
Contributor Author

Looks like //test/integration:quic_protocol_integration_test is failing. Is that expected?

Nope, I'm looking into it. They are all upstream disconnect tests, but it's weird that they fail only in windows build.

Signed-off-by: Dan Zhang <danzh@google.com>
@RyanTheOptimist
Copy link
Contributor

I might be missing it, but do we have test which verifies the intended behavior for this change? Specifically that this "bundles packet writes for each connection at the end of the event loop instead of each packet read. " I was expecting to see a test were multiple packets were read and the flag controlled when packets were sent?

I modified test/common/quic/active_quic_listener_test.cc to check that send alarm is armed after receiving CHLO.

nod And presumably the send alarm being set leads to the behavior mentioned in the PR description. But it's not obvious to me that this is the case. (Though I certainly believe you that it is!) Would it be hard to have a test which actually waited to send something until multiple packets are received? For example, the existing test in active_quic_listener_test.cc doesn't seem to send any packets after receiving the CHLO. Is there a way to make it do that?

Signed-off-by: Dan Zhang <danzh@google.com>
if (close_type_during_initialize_.has_value()) {
close(close_type_during_initialize_.value());
close_type_during_initialize_ = absl::nullopt;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, I see. Thanks for the explanation.

@@ -97,6 +99,7 @@ void QuicFilterManagerConnectionImpl::close(Network::ConnectionCloseType type) {
} else {
delayed_close_state_ = DelayedCloseState::CloseAfterFlush;
}
std::cerr << "========= danzh: delay close\n";
Copy link
Contributor

Choose a reason for hiding this comment

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

Presumably you're still debugging the CI failures?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is the only way I could think of to debug windows.

Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
@RyanTheOptimist
Copy link
Contributor

/wait

Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
@danzh2010
Copy link
Contributor Author

nod And presumably the send alarm being set leads to the behavior mentioned in the PR description. But it's not obvious to me that this is the case. (Though I certainly believe you that it is!) Would it be hard to have a test which actually waited to send something until multiple packets are received? For example, the existing test in active_quic_listener_test.cc doesn't seem to send any packets after receiving the CHLO. Is there a way to make it do that?

It was harder than I expected. CHLO doesn't always elicit sending packet due to packet coalescing, so there might be no behavioral change in this test with and without defer sending. But we are not testing QUICHE code, so to make the test more obvious, I used the getter of defer_send_in_response_to_packets_ to check its value in the test.

RyanTheOptimist
RyanTheOptimist previously approved these changes Sep 1, 2022
Copy link
Contributor

@RyanTheOptimist RyanTheOptimist left a comment

Choose a reason for hiding this comment

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

LGTM modulo a TODO

if (defer_send_) {
// Defer sending while processing UDP packets till the end of the current event loop to optimize
// UDP GSO sendmsg efficiency. But this optimization causes some test failures under Windows,
// and Windows doesn't support GSO, do not apply this optimization on Windows.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Mind filing a github issue to track this and adding a TODO to get to the bottom of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Signed-off-by: Dan Zhang <danzh@google.com>
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.

None yet

3 participants