Skip to content

l1/reader: Add lookahead for object metadata#29673

Merged
wdberkeley merged 2 commits intodevfrom
l1-readahead
Mar 4, 2026
Merged

l1/reader: Add lookahead for object metadata#29673
wdberkeley merged 2 commits intodevfrom
l1-readahead

Conversation

@wdberkeley
Copy link
Copy Markdown
Contributor

This adds optional object metadata information to the get_extent_metadata_forwards metastore API, which allows the L1 reader to populate a lookahead buffer of object metadata. The object metadata is optionally added because it requires additional lookups to populate, and users of the extent iterator that don't need this metadata shoudn't pay for it (and it seemed like overkill to add a second almost-identical call).

The previous one-at-a-time get_first_ge usage required N RPCs for N objects, and each RPC required 2 LSM lookups (2N total). For N objects, this new method requires 1 RPC, and the RPC requires 1 range scan and N point lookups. This is a win for N > 1, especially on RPC count, and the cost is the same for N = 1.

However, in the typical Kafka fetch case, I don't think this lookahead is helpful, because one L1 object should have many fetches' worth of data, so the lookahead is not worth it most of the time. Still, it might be helpful for some internal readers that stream a lot of batches through one L1 reader instance.

I hope this doesn't conflict too hard with the caching stuff. It's conceptually orthogonal.

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
  • v25.3.x
  • v25.2.x
  • v25.1.x

Release Notes

  • none

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 adds optional object metadata (object ID, footer position, object size) to the get_extent_metadata_forwards metastore API, enabling the L1 reader to populate a lookahead buffer of object metadata. This optimization reduces RPC calls from N to 1 for N objects by batching metadata lookups.

Changes:

  • Added include_object_metadata parameter to get_extent_metadata_forwards API with corresponding fields in extent_metadata RPC type
  • Implemented lookahead buffer in L1 reader to prefetch multiple objects' metadata in a single RPC
  • Added lookahead_objects configuration parameter to control prefetch behavior

Reviewed changes

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

Show a summary per file
File Description
src/v/cloud_topics/log_reader_config.h Added lookahead_objects configuration field for controlling metadata prefetch
src/v/cloud_topics/level_one/metastore/rpc_types.h Extended extent_metadata and get_extent_metadata_request with object metadata fields
src/v/cloud_topics/level_one/metastore/metastore.h Updated interface with include_object_metadata parameter and added documentation
src/v/cloud_topics/level_one/metastore/simple_metastore.h Added include_object_metadata parameter to method signatures
src/v/cloud_topics/level_one/metastore/simple_metastore.cc Implemented object metadata lookup logic
src/v/cloud_topics/level_one/metastore/replicated_metastore.h Updated method signature with new parameter
src/v/cloud_topics/level_one/metastore/replicated_metastore.cc Forwarded include_object_metadata parameter and updated RPC conversion
src/v/cloud_topics/level_one/domain/simple_domain_manager.cc Updated domain manager to pass through object metadata in conversions
src/v/cloud_topics/level_one/domain/db_domain_manager.cc Implemented object metadata lookup in DB-backed implementation
src/v/cloud_topics/level_one/frontend_reader/level_one_reader.h Added lookahead buffer and helper methods for managing it
src/v/cloud_topics/level_one/frontend_reader/level_one_reader.cc Implemented lookahead buffer logic and refactored object lookup
src/v/cloud_topics/level_one/metastore/tests/simple_metastore_test.cc Added comprehensive tests for object metadata feature
src/v/cloud_topics/level_one/frontend_reader/tests/reader_test.cc Added tests for lookahead functionality with single and multiple objects
src/v/cloud_topics/level_one/frontend_reader/tests/l1_reader_fixture.h Extended test fixture to support lookahead parameter

@vbotbuildovich
Copy link
Copy Markdown
Collaborator

vbotbuildovich commented Feb 21, 2026

CI test results

test results on build#80897
test_class test_method test_arguments test_kind job_url test_status passed reason test_history
ScalingUpTest test_scaling_up_with_recovered_topic null integration https://buildkite.com/redpanda/redpanda/builds/80897#019c7ed8-562c-4e0c-a165-c71228ad6f76 FLAKY 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0000, 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=ScalingUpTest&test_method=test_scaling_up_with_recovered_topic
test results on build#81316
test_class test_method test_arguments test_kind job_url test_status passed reason test_history
src/v/wasm/tests/wasm_transform_test src/v/wasm/tests/wasm_transform_test unit https://buildkite.com/redpanda/redpanda/builds/81316#019cb046-313c-4d9c-9dc3-41eba2f56c17 FAIL 0/1
test results on build#81356
test_class test_method test_arguments test_kind job_url test_status passed reason test_history
ControllerLogLimitMirrorMakerTests test_mirror_maker_with_limits null integration https://buildkite.com/redpanda/redpanda/builds/81356#019cb5c6-b166-44da-a1ff-71cb719dd880 FLAKY 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0358, p0=1.0000, reject_threshold=0.0100. adj_baseline=0.1037, p1=0.3345, trust_threshold=0.5000) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=ControllerLogLimitMirrorMakerTests&test_method=test_mirror_maker_with_limits
QuotaManagementUpgradeTest test_upgrade null integration https://buildkite.com/redpanda/redpanda/builds/81356#019cb5c2-e159-4e05-a3c8-3c61b5e86b8a FAIL 0/1 https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=QuotaManagementUpgradeTest&test_method=test_upgrade
TestReadReplicaService test_identical_lwms_after_delete_records {"cloud_storage_type": 1, "partition_count": 5} integration https://buildkite.com/redpanda/redpanda/builds/81356#019cb5c2-e15a-402d-b0ab-7d6fb689a3be FLAKY 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0552, p0=1.0000, reject_threshold=0.0100. adj_baseline=0.1568, p1=0.1818, trust_threshold=0.5000) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=TestReadReplicaService&test_method=test_identical_lwms_after_delete_records
ScalingUpTest test_moves_with_local_retention {"use_topic_property": false} integration https://buildkite.com/redpanda/redpanda/builds/81356#019cb5c6-b168-48e9-8f7d-d0676fc43885 FLAKY 19/21 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0050, p0=0.0961, 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=ScalingUpTest&test_method=test_moves_with_local_retention

Comment thread src/v/cloud_topics/level_one/metastore/metastore.h Outdated
Comment thread src/v/cloud_topics/level_one/metastore/metastore.h Outdated
Comment thread src/v/cloud_topics/level_one/metastore/rpc_types.h Outdated
kafka::offset offset, model::timeout_clock::time_point /*deadline*/) {
std::optional<l1::metastore::object_response>
level_one_log_reader_impl::consume_lookahead_buffer(kafka::offset offset) {
// Discard stale entries whose data is entirely before the requested
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.

how can this happen?

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.

Is this possible because of the caching stuff? e.g we might reuse a reader for serving a request at offset 50 from a cached reader which has/had extents [30,39],[40,49],[50,100] prefetched or something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is Claude being very defensive. I don't expect we ever hit this but it is the correct thing to do if somehow the lookahead is behind where we want to read from.

Comment thread src/v/cloud_topics/level_one/metastore/metastore.h Outdated
Comment thread src/v/cloud_topics/level_one/metastore/metastore.h Outdated
Comment thread src/v/cloud_topics/level_one/domain/db_domain_manager.cc Outdated
Comment thread src/v/cloud_topics/level_one/domain/db_domain_manager.cc Outdated
Comment thread src/v/cloud_topics/level_one/metastore/simple_metastore.cc Outdated
Comment thread src/v/cloud_topics/log_reader_config.h
@wdberkeley
Copy link
Copy Markdown
Contributor Author

Force push to rebase on dev. More fixes in second push.

Add an `include_object_metadata` flag to the
get_extent_metadata_forwards RPCs. When set, the domain manager does
per-extent object point lookups and populates oid, footer_pos, and
object_size on the extent_metadata response. The information is only
included when the flag is set because of the additional cost of the
per-extent lookups.

The three object fields are wrapped in
std::optional<extent_object_info> so that when the flag is not set,
only a single false byte is encoded instead of three default-valued
fields.

If an extent references an object that cannot be found in the local
metastore, an error is logged and an exception is thrown, since this
indicates a serious data integrity problem.

This will be used in a follow-up to add object metadata prefetching to
the L1 reader.
This changes how the L1 reader gets object metadata. Instead of calling
get_first_ge using the reader's start offset or the next offset after
the last offset of the current object, it uses the forward extent
iterator to get multiple objects' worth at a time.

What's the advantage? Reading N objects before required N get_first_ge
requests, which are full metastore RPCs, and each get_first_ge required
2 lookups in the metastore. The new lookahead buffer requires 1 RPC,
which requires 1 range read for N extents and N lookups for the object
metadata. This is a big reduction in metastore RPCs if readers are
reading multiple objects worth of data at once, and equivalent if only
one object's info is needed.

That said, this is probably not helpful for the typical Kafka fetch
path, because each L1 object should contain many fetch requests worth of
data (assuming sensible configuration). It might be helpful for some
internal readers. Therefore, the default lookahead only grabs one
object, so its behavior reduces to the previous behavior (look up the
next object when we need it).
@wdberkeley
Copy link
Copy Markdown
Contributor Author

Force push to address Tyler's feedback.

@wdberkeley wdberkeley requested a review from rockwotj March 3, 2026 21:55
@wdberkeley wdberkeley enabled auto-merge March 3, 2026 22:34
@wdberkeley wdberkeley merged commit 2ea7c06 into dev Mar 4, 2026
19 checks passed
@wdberkeley wdberkeley deleted the l1-readahead branch March 4, 2026 01:46
.build());
auto add_res
= m.add_objects(os, terms_builder().add(tid_a, 0_tm, 0_o).build()).get();
ASSERT_TRUE(add_res.has_value());
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.

this test consistently fails for me both locally and in CI

oleiman added a commit to oleiman/redpanda that referenced this pull request Mar 5, 2026
…readahead"

This reverts commit 2ea7c06, reversing
changes made to 250b0f7.
oleiman added a commit to oleiman/redpanda that referenced this pull request Mar 5, 2026
…readahead"

This reverts commit 2ea7c06, reversing
changes made to 250b0f7.
oleiman added a commit that referenced this pull request Mar 5, 2026
Revert "Merge pull request #29673 from redpanda-data/l1-readahead"
wdberkeley added a commit that referenced this pull request Mar 5, 2026
Re-applies the changes from #29673 (reverted in #29757) with a fix
for the TestGetExtentMetadataForwardsWithObjectMetadata test, which
needed preregister_objects calls after #29739 made preregistration
mandatory.
wdberkeley added a commit that referenced this pull request Mar 5, 2026
Re-applies the changes from #29673 (reverted in #29757) with two fixes:

1. TestGetExtentMetadataForwardsWithObjectMetadata now calls
   preregister_objects before add_objects, required after #29739 made
   preregistration mandatory.

2. l1_reader_cache::evict_stale() had a co_await inside its iteration
   loop. During suspension, take_reader() on the fetch scheduling group
   could erase entries from the intrusive list, invalidating the
   iterator. Fix by collecting stale readers without yielding, then
   closing them after the loop — matching the pattern already used by
   stop().
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.

6 participants