Skip to content

Commit 6c6f620

Browse files
danzh2010danzh1989
andauthored
quic: fix defer logging of request in Access log (#37643)
Commit Message: defer the logging till either last byte ACKed or QuicStream::OnSoonToBeDestroyed() called. Additional Description: OnSoonToBeDestroyed() is newly added to be called before stream object destruction. This is a better place than QuicStatsGatherer destructor to flush the log if the last byte hasn't been fully ACKed for whatever reason. Risk Level: low, behind default-false feature. Testing: integration tests passed Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A Runtime guard: guarded by existing runtime guard `envoy.reloadable_features.quic_defer_logging_to_ack_listener` Fixes #29930 for real --------- Signed-off-by: Dan Zhang <[email protected]> Co-authored-by: Dan Zhang <[email protected]>
1 parent 19dcf98 commit 6c6f620

File tree

5 files changed

+44
-5
lines changed

5 files changed

+44
-5
lines changed

source/common/quic/envoy_quic_server_stream.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,12 @@ void EnvoyQuicServerStream::OnClose() {
421421
return;
422422
}
423423
clearWatermarkBuffer();
424+
if (stats_gatherer_->notify_ack_listener_before_soon_to_be_destroyed()) {
425+
// Either stats_gatherer_ will do deferred logging upon receiving the last
426+
// ACK, or OnSoonToBeDestroyed() will catch all the cases where the stream
427+
// is destroyed without receiving the last ACK.
428+
return;
429+
}
424430
if (!stats_gatherer_->loggingDone()) {
425431
stats_gatherer_->maybeDoDeferredLog(/* record_ack_timing */ false);
426432
}
@@ -539,5 +545,16 @@ void EnvoyQuicServerStream::useCapsuleProtocol() {
539545

540546
void EnvoyQuicServerStream::OnInvalidHeaders() { onStreamError(absl::nullopt); }
541547

548+
void EnvoyQuicServerStream::OnSoonToBeDestroyed() {
549+
quic::QuicSpdyServerStreamBase::OnSoonToBeDestroyed();
550+
if (stats_gatherer_ != nullptr &&
551+
stats_gatherer_->notify_ack_listener_before_soon_to_be_destroyed() &&
552+
!stats_gatherer_->loggingDone()) {
553+
// Catch all the cases where the stream is destroyed without receiving the
554+
// last ACK.
555+
stats_gatherer_->maybeDoDeferredLog(/* record_ack_timing */ false);
556+
}
557+
}
558+
542559
} // namespace Quic
543560
} // namespace Envoy

source/common/quic/envoy_quic_server_stream.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class EnvoyQuicServerStream : public quic::QuicSpdyServerStreamBase,
6565

6666
// quic::QuicStream
6767
void OnStreamFrame(const quic::QuicStreamFrame& frame) override;
68+
void OnSoonToBeDestroyed() override;
6869
// quic::QuicSpdyStream
6970
void OnBodyAvailable() override;
7071
bool OnStopSending(quic::QuicResetStreamError error) override;

source/common/quic/quic_stats_gatherer.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "envoy/stream_info/stream_info.h"
99

1010
#include "quiche/quic/core/quic_ack_listener_interface.h"
11+
#include "quiche/quic/platform/api/quic_flags.h"
1112

1213
namespace Envoy {
1314
namespace Quic {
@@ -19,7 +20,11 @@ class QuicStatsGatherer : public quic::QuicAckListenerInterface {
1920
explicit QuicStatsGatherer(Envoy::TimeSource* time_source) : time_source_(time_source) {}
2021
~QuicStatsGatherer() override {
2122
if (!logging_done_) {
22-
maybeDoDeferredLog(false);
23+
if (notify_ack_listener_before_soon_to_be_destroyed_) {
24+
ENVOY_LOG_MISC(error, "Stream destroyed without logging.");
25+
} else {
26+
maybeDoDeferredLog(false);
27+
}
2328
}
2429
}
2530

@@ -51,6 +56,9 @@ class QuicStatsGatherer : public quic::QuicAckListenerInterface {
5156
}
5257
bool loggingDone() { return logging_done_; }
5358
uint64_t bytesOutstanding() { return bytes_outstanding_; }
59+
bool notify_ack_listener_before_soon_to_be_destroyed() const {
60+
return notify_ack_listener_before_soon_to_be_destroyed_;
61+
}
5462

5563
private:
5664
uint64_t bytes_outstanding_ = 0;
@@ -65,6 +73,10 @@ class QuicStatsGatherer : public quic::QuicAckListenerInterface {
6573
bool logging_done_ = false;
6674
uint64_t retransmitted_packets_ = 0;
6775
uint64_t retransmitted_bytes_ = 0;
76+
77+
const bool notify_ack_listener_before_soon_to_be_destroyed_{
78+
GetQuicReloadableFlag(quic_notify_ack_listener_earlier) &&
79+
GetQuicReloadableFlag(quic_notify_stream_soon_to_destroy)};
6880
};
6981

7082
} // namespace Quic

source/common/runtime/runtime_features.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_reject_all);
135135
// For more information about Universal Header Validation, please see
136136
// https://github.com/envoyproxy/envoy/issues/10646
137137
FALSE_RUNTIME_GUARD(envoy_reloadable_features_enable_universal_header_validator);
138-
// TODO(pksohn): enable after canarying fix for https://github.com/envoyproxy/envoy/issues/29930
138+
// TODO(pksohn): enable after canarying the feature internally without problems.
139139
FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_defer_logging_to_ack_listener);
140140
// TODO(alyssar) evaluate and either make this a config knob or remove.
141141
FALSE_RUNTIME_GUARD(envoy_reloadable_features_reresolve_null_addresses);

test/integration/quic_http_integration_test.cc

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,13 +1417,21 @@ TEST_P(QuicHttpIntegrationTest, DeferredLogging) {
14171417
TEST_P(QuicHttpIntegrationTest, DeferredLoggingWithBlackholedClient) {
14181418
config_helper_.addRuntimeOverride("envoy.reloadable_features.quic_defer_logging_to_ack_listener",
14191419
"true");
1420+
config_helper_.addRuntimeOverride("envoy.reloadable_features.FLAGS_envoy_quiche_reloadable_flag_"
1421+
"quic_notify_stream_soon_to_destroy",
1422+
"true");
1423+
config_helper_.addRuntimeOverride("envoy.reloadable_features.FLAGS_envoy_quiche_reloadable_flag_"
1424+
"quic_notify_ack_listener_earlier",
1425+
"true");
1426+
14201427
useAccessLog(
14211428
"%PROTOCOL%,%ROUNDTRIP_DURATION%,%REQUEST_DURATION%,%RESPONSE_DURATION%,%RESPONSE_"
14221429
"CODE%,%BYTES_RECEIVED%,%ROUTE_NAME%,%VIRTUAL_CLUSTER_NAME%,%RESPONSE_CODE_DETAILS%,%"
14231430
"CONNECTION_TERMINATION_DETAILS%,%START_TIME%,%UPSTREAM_HOST%,%DURATION%,%BYTES_SENT%,%"
14241431
"RESPONSE_FLAGS%,%DOWNSTREAM_LOCAL_ADDRESS%,%UPSTREAM_CLUSTER%,%STREAM_ID%,%DYNAMIC_"
14251432
"METADATA("
1426-
"udp.proxy.session:bytes_sent)%,%REQ(:path)%,%STREAM_INFO_REQ(:path)%");
1433+
"udp.proxy.session:bytes_sent)%,%REQ(:path)%,%STREAM_INFO_REQ(:path)%,%DOWNSTREAM_TLS_"
1434+
"CIPHER%");
14271435
initialize();
14281436

14291437
// Make a header-only request and delay the response by 1ms to ensure that the ROUNDTRIP_DURATION
@@ -1455,7 +1463,7 @@ TEST_P(QuicHttpIntegrationTest, DeferredLoggingWithBlackholedClient) {
14551463
std::string log = entries[0];
14561464

14571465
std::vector<std::string> metrics = absl::StrSplit(log, ',');
1458-
ASSERT_EQ(metrics.size(), 21);
1466+
ASSERT_EQ(metrics.size(), 22);
14591467
EXPECT_EQ(/* PROTOCOL */ metrics.at(0), "HTTP/3");
14601468
EXPECT_EQ(/* ROUNDTRIP_DURATION */ metrics.at(1), "-");
14611469
EXPECT_GE(/* REQUEST_DURATION */ std::stoi(metrics.at(2)), 0);
@@ -1465,7 +1473,8 @@ TEST_P(QuicHttpIntegrationTest, DeferredLoggingWithBlackholedClient) {
14651473
// Ensure that request headers from top-level access logger parameter and stream info are
14661474
// consistent.
14671475
EXPECT_EQ(/* request headers */ metrics.at(19), metrics.at(20));
1468-
1476+
EXPECT_THAT(/* DOWNSTREAM_TLS_CIPHER */ metrics.at(21),
1477+
::testing::MatchesRegex("TLS_(AES_128_GCM|CHACHA20_POLY1305)_SHA256"));
14691478
codec_client_->close();
14701479
}
14711480

0 commit comments

Comments
 (0)