diff --git a/api/envoy/config/listener/v3/quic_config.proto b/api/envoy/config/listener/v3/quic_config.proto index 6c0a5bd201fc0..c208a58f4a48a 100644 --- a/api/envoy/config/listener/v3/quic_config.proto +++ b/api/envoy/config/listener/v3/quic_config.proto @@ -25,7 +25,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: QUIC listener config] // Configuration specific to the UDP QUIC listener. -// [#next-free-field: 14] +// [#next-free-field: 15] message QuicProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.listener.QuicProtocolOptions"; @@ -99,4 +99,10 @@ message QuicProtocolOptions { // QUIC layer by replying with an empty version negotiation packet to the // client. bool reject_new_connections = 13; + + // Maximum number of QUIC sessions to create per event loop. + // If not specified, the default value is 16. + // This is an equivalent of the TCP listener option + // max_connections_to_accept_per_socket_event. + google.protobuf.UInt32Value max_sessions_per_event_loop = 14 [(validate.rules).uint32 = {gt: 0}]; } diff --git a/changelogs/current.yaml b/changelogs/current.yaml index a08ab336a4c96..522c44c60faae 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -103,5 +103,10 @@ new_features: - area: admin change: | Added ``/memory/tcmalloc`` admin endpoint that provides TCMalloc memory statistics. +- area: quic + change: | + Add a new quic configuration field, 'max_sessions_per_event_loop', to QuicProtocolOptions in Envoy listener. + This allows tuning the maximum number of new QUIC sessions created within a single event loop. + The default value is 16, preserving the previous hardcoded limit. deprecated: diff --git a/source/common/quic/active_quic_listener.cc b/source/common/quic/active_quic_listener.cc index 697e9d29f6739..db1b315c326d5 100644 --- a/source/common/quic/active_quic_listener.cc +++ b/source/common/quic/active_quic_listener.cc @@ -129,6 +129,10 @@ ActiveQuicListener::ActiveQuicListener( udp_save_cmsg_config_.expected_size = save_cmsg_config.expected_size(); } } + + max_sessions_per_event_loop_ = + PROTOBUF_GET_WRAPPED_OR_DEFAULT(listener_config.udpListenerConfig()->config().quic_options(), + max_sessions_per_event_loop, kNumSessionsToCreatePerLoop); } ActiveQuicListener::~ActiveQuicListener() { onListenerShutdown(); } @@ -198,7 +202,7 @@ void ActiveQuicListener::onReadReady() { event_loops_with_buffered_chlo_for_test_++; } - quic_dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerLoop); + quic_dispatcher_->ProcessBufferedChlos(max_sessions_per_event_loop_); // If there were more buffered than the limit, schedule again for the next event loop. if (quic_dispatcher_->HasChlosBuffered()) { diff --git a/source/common/quic/active_quic_listener.h b/source/common/quic/active_quic_listener.h index f478711cb92fd..c354f44ad707b 100644 --- a/source/common/quic/active_quic_listener.h +++ b/source/common/quic/active_quic_listener.h @@ -27,7 +27,7 @@ class ActiveQuicListener : public Envoy::Server::ActiveUdpListenerBase, Logger::Loggable { public: // TODO(bencebeky): Tune this value. - static const size_t kNumSessionsToCreatePerLoop = 16; + static constexpr size_t kNumSessionsToCreatePerLoop = 16; ActiveQuicListener(Runtime::Loader& runtime, uint32_t worker_index, uint32_t concurrency, Event::Dispatcher& dispatcher, Network::UdpConnectionHandler& parent, @@ -102,6 +102,10 @@ class ActiveQuicListener : public Envoy::Server::ActiveUdpListenerBase, // During hot restart, an optional handler for packets that weren't for existing connections. OptRef non_dispatched_udp_packet_handler_; Network::IoHandle::UdpSaveCmsgConfig udp_save_cmsg_config_; + // Maximum number of QUIC sessions to create per event loop. + // This is an equivalent of max_connections_to_accept_per_socket_event for TCP + // listeners. + uint32_t max_sessions_per_event_loop_; }; using ActiveQuicListenerPtr = std::unique_ptr; diff --git a/test/common/quic/active_quic_listener_test.cc b/test/common/quic/active_quic_listener_test.cc index 8adf1488b652b..e44d0468c054b 100644 --- a/test/common/quic/active_quic_listener_test.cc +++ b/test/common/quic/active_quic_listener_test.cc @@ -107,6 +107,10 @@ class ActiveQuicListenerPeer { static bool enabled(ActiveQuicListener& listener) { return listener.enabled_->enabled(); } static Network::Socket& socket(ActiveQuicListener& listener) { return listener.listen_socket_; } + + static uint32_t getMaxSessionsPerEventLoop(ActiveQuicListener& listener) { + return listener.max_sessions_per_event_loop_; + } }; class ActiveQuicListenerFactoryPeer { @@ -675,6 +679,23 @@ TEST_P(ActiveQuicListenerTest, EcnReportingDualStack) { EXPECT_EQ(stats.num_ecn_marks_received.ect1, 1); } +TEST_P(ActiveQuicListenerTest, MaxSessionsPerEventLoopNotConfigured) { + initialize(); + EXPECT_EQ(ActiveQuicListener::kNumSessionsToCreatePerLoop, + ActiveQuicListenerPeer::getMaxSessionsPerEventLoop(*quic_listener_)); +} + +TEST_P(ActiveQuicListenerTest, MaxSessionsPerEventLoopConfigured) { + const uint32_t max_sessions_per_event_loop = 2; + envoy::config::listener::v3::UdpListenerConfig udp_listener_config; + udp_listener_config.mutable_quic_options()->mutable_max_sessions_per_event_loop()->set_value( + max_sessions_per_event_loop); + ON_CALL(udp_listener_config_, config()).WillByDefault(ReturnRef(udp_listener_config)); + initialize(); + EXPECT_EQ(max_sessions_per_event_loop, + ActiveQuicListenerPeer::getMaxSessionsPerEventLoop(*quic_listener_)); +} + class ActiveQuicListenerEmptyFlagConfigTest : public ActiveQuicListenerTest { protected: std::string yamlForQuicConfig() override {