From bd3f67277ad387005488cc5a526d60510b65e049 Mon Sep 17 00:00:00 2001 From: chrislu Date: Thu, 16 Oct 2025 18:55:25 -0700 Subject: [PATCH] fix: Correct throttle time semantics in Fetch responses When long-polling finds data available during the wait period, return immediately with throttleTimeMs=0. Only use throttle time for quota enforcement or when hitting the max wait timeout without data. Previously, the code was reporting the elapsed wait time as throttle time, causing clients to receive unnecessary throttle delays (10-33ms) even when data was available, accumulating into significant latency for continuous fetch operations. This aligns with Kafka protocol semantics where throttle time is for back-pressure due to quotas, not for long-poll timing information. --- weed/mq/kafka/protocol/fetch.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/weed/mq/kafka/protocol/fetch.go b/weed/mq/kafka/protocol/fetch.go index ca758c30e..0f0e85320 100644 --- a/weed/mq/kafka/protocol/fetch.go +++ b/weed/mq/kafka/protocol/fetch.go @@ -97,11 +97,18 @@ func (h *Handler) handleFetch(ctx context.Context, correlationID uint32, apiVers // Continue with polling } if hasDataAvailable() { + // Data became available during polling - return immediately with NO throttle + // Throttle time should only be used for quota enforcement, not for long-poll timing + throttleTimeMs = 0 break pollLoop } } - elapsed := time.Since(start) - throttleTimeMs = int32(elapsed / time.Millisecond) + // If we got here without breaking early, we hit the timeout + // Only set throttle time if we're returning without data (true long-poll timeout) + if throttleTimeMs == 0 && !hasDataAvailable() { + elapsed := time.Since(start) + throttleTimeMs = int32(elapsed / time.Millisecond) + } } // Build the response