Skip to content

apollo_consensus_orchestrator: fetch backfill windows from the batcher concurrently - #14963

Open
gkaempfer wants to merge 2 commits into
mainfrom
claude/perf/apollo-consensus-orchestrator-parallel-backfill-83214
Open

apollo_consensus_orchestrator: fetch backfill windows from the batcher concurrently#14963
gkaempfer wants to merge 2 commits into
mainfrom
claude/perf/apollo-consensus-orchestrator-parallel-backfill-83214

Conversation

@gkaempfer

Copy link
Copy Markdown
Contributor

Performance follow-up to #14892

Automated performance-review routine, triggered by the merge of #14892, found a
sequential-latency issue in two backfill loops on the consensus-critical
finalize_decision/decision_reached path.

Problem

collect_recent_block_hashes and collect_recent_state_commitment_infos each run
once per block and loop over a small, bounded window of heights (≤ 11 after
#14892's cap), calling the batcher once per height with a sequential .await
inside the for loop:

for block_height in lowest_height..=height.0 {
    ...
    self.deps.batcher.get_state_commitment_infos(block_number).await
    ...
}

Each iteration pays a full round trip to the batcher component before the next one
starts. In a remote deployment topology (HTTP, per this repo's component-server
model) this means up to ~10-11 sequential round trips in series, on a path that
per the existing code comments "must not stall block production."

Fix

Fetch each window concurrently with futures::future::join_all — the same pattern
already used elsewhere in this file (send_reproposal) and in
build_proposal.rs/validate_proposal.rs — then apply the exact same
sequential break/skip/cap decision logic afterward, over the resolved results in
original height order. This preserves identical observable behavior to the
sequential version (including the "skip a leading gap only when the cende
recorder is empty, but break immediately on any gap once it has a real offset"
logic) while collapsing the round trips into a couple of concurrent batches
instead of paying for each one back-to-back.

Review

An Opus subagent reviewed the diff for correctness against every branch/edge case
(empty ranges, height = 0, the exact-11 vs exact-10 window bounds, mock-ordering
assumptions in existing tests, whether the batcher calls are safe to fire
concurrently given the local component server's serial processing loop) and
flagged 4 suggestion-level findings, all addressed in a follow-up commit:

  • Made the state-commitment-infos fetch bound branch-aware instead of a generic
    max(), so it can't silently under-fetch if either window's size changes later.
  • Built the block-hashes height list once and reused it for both the join_all
    and the zip, instead of relying on two independently-constructed ranges
    staying in lockstep.
  • Renamed a local to avoid colliding with an unrelated window_size used
    elsewhere in this file.
  • Trimmed a comment that described what the bound computed rather than why.

Test

  • cargo test -p apollo_consensus_orchestrator — 200 passed, 0 failed (including
    all 9 collect_recent_state_commitment_infos_sends_expected_delta cases and the
    collect_recent_block_hashes/retrospective_* coverage)
  • cargo clippy -p apollo_consensus_orchestrator --all-targets -- -D warnings — clean
  • scripts/rust_fmt.sh — clean

🤖 Generated by an automated performance-review routine (Claude Code), triggered by the merge of #14892.


Generated by Claude Code

claude added 2 commits August 13, 2026 13:18
…r concurrently

collect_recent_block_hashes and collect_recent_state_commitment_infos run once per
block on the consensus-critical finalize_decision path and previously awaited each
batcher call sequentially in a loop, paying up to window_size round trips in series.
Fetch the (small, bounded) windows concurrently with join_all instead, while keeping
the exact same break/skip/cap semantics when processing the resolved results in order.
…backfill

Make the state-commitment-infos fetch bound branch-aware instead of a generic
max() so it can't silently under-fetch if either window's size changes later.
Build the block-hashes height list once and reuse it for both the join_all
and the zip, instead of relying on two independently-constructed ranges
staying in lockstep. Rename the block-hashes window_size local to avoid
colliding with the unrelated fee_proposal window_size used elsewhere in this
file.
@cursor

cursor Bot commented Aug 13, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches consensus-critical finalize/cende blob preparation; behavior is intended to be identical but concurrent batcher load and ordering edge cases warrant careful review.

Overview
On the consensus-critical finalize_decision path, collect_recent_block_hashes and collect_recent_state_commitment_infos no longer call the batcher once per height in a serial loop. Each bounded window is prefetched with futures::future::join_all, then the same height-ordered break/skip/cap rules run over the paired results.

For state commitment backfill, the PR also materializes the height list up front with a branch-aware fetch cap (full fallback window when the cende recorder is empty vs MAX_COMMITMENT_INFOS_BACKFILL otherwise) so concurrent prefetch cannot under-fetch relative to the old sequential behavior.

Reviewed by Cursor Bugbot for commit 0e907d5. Bugbot is set up for automated code reviews on this repo. Configure here.

@gkaempfer gkaempfer self-assigned this Aug 13, 2026
@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

@github-actions

Copy link
Copy Markdown

There hasn't been any activity on this pull request recently, and in order to prioritize active work, it has been marked as stale.
This PR will be closed and locked in 7 days if no further activity occurs.
Thank you for your contributions!

@github-actions github-actions Bot added the stale label Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants