From 679bb62a17a58c20c2591ca7ef7a87a96f00beed Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 7 May 2026 13:32:54 +0000 Subject: [PATCH] diag(quic-interop): chunk-size + openBidiStreamsBatch entry/exit logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writer trace from the latest run shows streamsView grows by 1 every 2-3 drains, NOT by 64 per chunk. Suggests batching isn't happening, even though the bytecode confirms openBidiStreamsBatch IS being called (via javap on the deployed class file). Two new diagnostic lines per multiplex run, both gated by DEBUG=1: [interop] multiplex start: total_urls=N MULTIPLEX_PARALLELISM=64 expected_chunks=32 [interop] chunk=0 size=64 starting prepareRequests [interop] chunk=1 size=64 starting prepareRequests ... [batch] openBidiStreamsBatch items=64 returned=64 streamsList_before=6 streamsList_after=70 If the [batch] line shows items=1 (instead of 64), the chunked() call is producing chunks of 1 (would be a bug in MULTIPLEX_ PARALLELISM or chunked semantics). If [batch] shows items=64 returned=64 streamsList_after=70, then batching IS working at this layer and the bug is downstream — the writer is somehow only seeing 1 stream at a time despite 64 being in the list. https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT --- .../quic/interop/runner/InteropClient.kt | 12 ++++++++++++ .../vitorpamplona/quic/connection/QuicConnection.kt | 13 +++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt b/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt index 449269e84..d9ce893f0 100644 --- a/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt +++ b/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt @@ -364,8 +364,20 @@ private fun runTransferTest( // (responses dribble in over a long stretch). val debug = System.getenv("QUIC_INTEROP_DEBUG") == "1" val transferStartMs = nowMs() + if (debug) { + System.err.println( + "[interop] multiplex start: total_urls=${urls.size} " + + "MULTIPLEX_PARALLELISM=$MULTIPLEX_PARALLELISM " + + "expected_chunks=${(urls.size + MULTIPLEX_PARALLELISM - 1) / MULTIPLEX_PARALLELISM}", + ) + } urls.chunked(MULTIPLEX_PARALLELISM).forEachIndexed { chunkIdx, chunk -> val chunkStartMs = nowMs() + if (debug && chunkIdx < 3) { + System.err.println( + "[interop] chunk=$chunkIdx size=${chunk.size} starting prepareRequests", + ) + } // Single lock-held batch open + enqueue. // Without this, openBidiStream's per-call // lock acquire / release lets the send loop diff --git a/quic/src/commonMain/kotlin/com/vitorpamplona/quic/connection/QuicConnection.kt b/quic/src/commonMain/kotlin/com/vitorpamplona/quic/connection/QuicConnection.kt index aeaafd667..e23cabdaf 100644 --- a/quic/src/commonMain/kotlin/com/vitorpamplona/quic/connection/QuicConnection.kt +++ b/quic/src/commonMain/kotlin/com/vitorpamplona/quic/connection/QuicConnection.kt @@ -800,9 +800,18 @@ class QuicConnection( init: (QuicStream, I) -> R, ): List { if (items.isEmpty()) return emptyList() - return streamsLock.withLock { - items.map { init(openBidiStreamLocked(), it) } + val streamsBefore = if (writerDebugEnabled) streams.size else 0 + val result = + streamsLock.withLock { + items.map { init(openBidiStreamLocked(), it) } + } + if (writerDebugEnabled) { + System.err.println( + "[batch] openBidiStreamsBatch items=${items.size} returned=${result.size} " + + "streamsList_before=$streamsBefore streamsList_after=${streams.size}", + ) } + return result } /**