Skip to content

Add serialized response coalescing for high fanout fetch workloads#31010

Open
ballard26 wants to merge 7 commits into
redpanda-data:devfrom
ballard26:fetch-read-coal
Open

Add serialized response coalescing for high fanout fetch workloads#31010
ballard26 wants to merge 7 commits into
redpanda-data:devfrom
ballard26:fetch-read-coal

Conversation

@ballard26

@ballard26 ballard26 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

This PR introduces a per-shard fetch read coalescer to reduce duplicate partition reads/serialization when many consumers fetch the same partition offset concurrently (high fan-out). This feature is off by default, but can be dynamically enabled without restart via the kafka_fetch_read_coalescing_enabled cluster config.

Changes:

  • Adds a per-shardfetch_read_coalescer service(cache + in-flight sharing + metrics).
  • Integrates coalescing into do_read_from_ntp and adjusts fetch response memory accounting to support shared results.

Backports Required

  • none - not a bug fix
  • none - this is a backport
  • none - issue does not exist in previous branches
  • none - papercut/not impactful enough to backport
  • v26.1.x
  • v25.3.x
  • v25.2.x

Release Notes

Improvements

  • Partition reads and serialized responses for high fan-out workloads are now shared instead of duplicated across responses.

Allocate fetch memory units just before read_from_partition rather than
at function entry, so they are no longer held longer than needed. Move
strict_max_bytes down alongside it to keep the obligatory-read setup
with the read.
@ballard26 ballard26 requested a review from Copilot July 3, 2026 05:27
@ballard26 ballard26 requested a review from a team as a code owner July 3, 2026 05:27
@ballard26 ballard26 removed the request for review from a team July 3, 2026 05:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a per-shard “fetch read coalescer” to reduce duplicate partition reads/serialization when many consumers fetch the same partition offset concurrently (high fanout). It wires the coalescer into the Kafka fetch path behind a new dynamic config flag, and adds both unit and end-to-end tests.

Changes:

  • Add fetch_read_coalescer (cache + in-flight sharing + metrics) and expose it via kafka::server.
  • Integrate coalescing into do_read_from_ntp and adjust fetch response memory accounting to support shared results.
  • Add unit tests for the coalescer plus an rptest exercising coalescing via metrics.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tools/type-checking/type-check-strictness.json Adds the new rptest file to strict type checking inputs.
tests/rptest/tests/fetch_read_coalescing_test.py New end-to-end fanout test validating coalescing engages and data correctness.
src/v/kafka/server/tests/fetch_test.cc Updates test helper call to pass the new read_coalescer() dependency.
src/v/kafka/server/tests/fetch_read_coalescer_test.cc New unit tests covering coalescer hit/miss, staleness, inflight sharing, errors, and enable/disable behavior.
src/v/kafka/server/tests/BUILD Registers the new coalescer unit test target.
src/v/kafka/server/server.h Adds the coalescer member and exposes read_coalescer() accessor.
src/v/kafka/server/server.cc Constructs the coalescer with cache sizing and binds it to the new config property.
src/v/kafka/server/handlers/fetch.h Extends read_result (record_count, shared memory units), and extends read_from_ntp signature to accept the coalescer.
src/v/kafka/server/handlers/fetch.cc Implements coalesced read path (shared reads + cloning) and adapts memory-units handling.
src/v/kafka/server/fetch_read_coalescer.h New coalescer public API/types (key, cache entry, metrics).
src/v/kafka/server/fetch_read_coalescer.cc New coalescer implementation (cache/inflight sharing + metrics).
src/v/kafka/server/BUILD Adds new coalescer sources/headers and dependency on chunked_kv_cache.
src/v/config/configuration.h Adds kafka_fetch_read_coalescing_enabled configuration property declaration.
src/v/config/configuration.cc Defines the new tunable property and its description/default.

Comment thread src/v/kafka/server/handlers/fetch.cc
Comment thread src/v/kafka/server/handlers/fetch.cc
Comment thread src/v/config/configuration.cc
@vbotbuildovich

Copy link
Copy Markdown
Collaborator

CI test results

test results on build#86698
test_status test_class test_method test_arguments test_kind job_url passed reason test_history
FLAKY(PASS) ShadowLinkTopicFailoverTests test_producer_ids_failover {"storage_mode": "local"} integration https://buildkite.com/redpanda/redpanda/builds/86698#019f268d-37ae-42ec-8764-268cecdb19ce 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0058, p0=1.0000, reject_threshold=0.0100. adj_baseline=0.1000, p1=0.3487, trust_threshold=0.5000) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=ShadowLinkTopicFailoverTests&test_method=test_producer_ids_failover
FAIL SIPartitionMovementTest test_cross_shard {"cloud_storage_type": 2, "num_to_upgrade": 0, "with_cloud_topics": true} integration https://buildkite.com/redpanda/redpanda/builds/86698#019f268d-37ac-4345-8b36-980aaad2495b 0/1 https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=SIPartitionMovementTest&test_method=test_cross_shard
FLAKY(PASS) ShadowLinkingRandomOpsTest test_node_operations {"failures": true, "workload_set": "cloud_combos"} integration https://buildkite.com/redpanda/redpanda/builds/86698#019f268d-37b3-4e5a-82a3-94f21a1d6d48 19/21 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0180, p0=0.3050, reject_threshold=0.0100. adj_baseline=0.1000, p1=0.3917, trust_threshold=0.5000) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=ShadowLinkingRandomOpsTest&test_method=test_node_operations
FLAKY(PASS) SimpleEndToEndTest test_relaxed_acks {"write_caching": false} integration https://buildkite.com/redpanda/redpanda/builds/86698#019f268d-37b3-4710-b7cf-5a43c996f2e1 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0017, p0=1.0000, reject_threshold=0.0100. adj_baseline=0.1000, p1=0.3487, trust_threshold=0.5000) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=SimpleEndToEndTest&test_method=test_relaxed_acks

ballard26 added 6 commits July 3, 2026 13:53
Change read_result::memory_units and the response placeholder's units
slot from std::optional to std::shared_ptr, wrapping the units in
make_shared on the read path. This lets a single set of units be shared
by every response built from a coalesced read result, released when the
last reference drops.
Adds a per-shard fetch read coalescer that dedups reads with the same
(ktp, offset, isolation, max_bytes, obligatory) key.

Obligatory reads are cached separately from non-obligatory reads as the
in-flight read of non-obligatory reads can return empty results which
isn't valid for obligatory ones.

Although not tracked in the key the known end-offset of the partition is
tracked along with the read_result. This lets the coalescer check if the
currently cached result is "fresh" by checking whether the end-offset
for that read is greater than or equal to the provided end-offset. If
it's not fresh the cached read is replaced with a newer one.
Live (needs_restart::no) cluster flag, default off, gating the fetch read
coalescer. Wired into the fetch path in a following commit.
Adds the per-shard fetch_read_coalescer to the server class. And then
threads it through fetch_ntps into do_read_from_ntp.

With the coalescing flag off (default) the fetch reads remain as they
were before. When on, concurrent and back-to-back readers of the
same (ntp, offset, isolation, max_bytes, obligatory) key share one
serialized read. This shared read is kept alive until all responses
containing it are fully sent.
Tests that a fanout workload with independent consumers reading the
same partition offset with read coalescing enabled share their
serialized response in the fetch path.
This commit is just to run the full CI against this config enabled. Will
be dropped before merge.
@ballard26 ballard26 changed the title [WIP] Add serialized response coalescing for high fanout fetch workloads Add serialized response coalescing for high fanout fetch workloads Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants