From 55ed09ef730e86624cdcbf860a60652f712b12d2 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Sat, 6 Jun 2026 21:37:31 -0400 Subject: [PATCH 1/2] fix(cosmos): HTTP/2 PING handler must observe child-stream read activity, not just parent-pipeline frames Follow-up to #49095. Root cause: Http2PingHandler is installed on the parent (TCP) channel and only updated lastReadActivityNanos from parent-pipeline channelRead -- which sees PING ACK / SETTINGS / GOAWAY but NOT HEADERS / DATA frames. With reactor-netty's Http2MultiplexHandler in the parent pipeline, all application H2 traffic is demuxed onto per-stream child channels, so a connection serving thousands of QPS still looked idle to the PING handler, causing unnecessary PINGs and measurable P95 overhead under load. Fix: - New package-private AttributeKey LAST_CHILD_STREAM_READ_ACTIVITY_NANOS seeded on the parent channel in handlerAdded. - Http2PingCloseRewrapHandler (already installed in every child stream's pipeline) overrides channelReadComplete to stamp the parent attribute via accumulateAndGet(now, Math::max) -- once per read cycle, monotonic. - Http2PingHandler.maybeSendPing now uses effectiveLastReadActivityNanos = max(field, child attr) for both the idle-threshold check AND the outstanding-PING early-return: if child-stream reads arrived after an outstanding PING, clear pingOutstandingSinceNanos and reset consecutiveFailures (defense against middleboxes that drop PING-ACK but pass application H2 frames). - Names use lastReadActivity / read activity -- both signals are inbound reads, never writes. Tests: two new unit tests in Http2PingHandlerTest covering the suppression path and the outstanding-PING reset path; both bypass real time via reflection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../http/Http2PingHandlerTest.java | 119 ++++++++++++++++- sdk/cosmos/azure-cosmos/CHANGELOG.md | 1 + .../http/Http2PingCloseRewrapHandler.java | 42 ++++++ .../implementation/http/Http2PingHandler.java | 123 ++++++++++++++++-- 4 files changed, 270 insertions(+), 15 deletions(-) diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/http/Http2PingHandlerTest.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/http/Http2PingHandlerTest.java index 004cde861d87..a7403ce5bf57 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/http/Http2PingHandlerTest.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/http/Http2PingHandlerTest.java @@ -16,6 +16,7 @@ import java.lang.reflect.Method; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import static org.assertj.core.api.Assertions.assertThat; @@ -25,7 +26,7 @@ * Covers state transitions that do not require advancing real time -- the handler's * timeout / threshold logic reads {@code System.nanoTime()}, so the tests sidestep * the clock by pre-setting {@code pingOutstandingSinceNanos} (or - * {@code lastActivityNanos}) via reflection: + * {@code lastReadActivityNanos}) via reflection: *