apollo_batcher: skip spawn_blocking for the non-blocking is_done check#14854
apollo_batcher: skip spawn_blocking for the non-blocking is_done check#14854gkaempfer wants to merge 1 commit into
Conversation
is_done() only reads an atomic flag (Scheduler::done), so routing it through spawn_blocking costs a full blocking-pool dispatch round-trip for no benefit. PR #14842 tightened the block-builder loop's sleep to results_polling_interval_millis (10ms default) while txs are in progress, so this check now runs ~20x more often per block than before. Call it inline instead. Co-authored-by: Claude <noreply@anthropic.com>
PR SummaryLow Risk Overview This matches the fact that completion is a cheap atomic read ( A short comment documents why this path is treated differently from other executor accesses. Reviewed by Cursor Bugbot for commit 5db378b. Bugbot is set up for automated code reviews on this repo. Configure here. |
Summary
Follow-up perf optimization found while reviewing #14842 (merged), which split the block-builder loop's polling cadence so it sleeps only
results_polling_interval_millis(default 10ms) instead oftx_polling_interval_millis(e.g. 200ms in prod) while transactions are in progress. That change makes thewhileloop inbuild_block_inneriterate roughly 20x more often per block whenever there are in-flight transactions.On every iteration, the loop checked whether the block is full via:
is_done()(TransactionExecutorTrait::is_done→ConcurrentTransactionExecutor::is_done→Scheduler::done()) is just a single atomic load (self.done_marker.load(Ordering::Acquire)) — no I/O, no meaningful CPU work, and the underlying mutex (lock_executor'stry_lock) is uncontended by design since only this one task ever touches the executor. Routing this trivial, non-blocking read throughspawn_blockingcosts a full dispatch round-trip to Tokio's blocking thread pool (channel send, thread wake, context switch) for no benefit — overhead that is now paid ~20x more often per block after #14842.This PR calls
is_done()directly, inline, on the async task instead:No other executor access changes —
handle_executed_txs()(which does real, non-trivial work extracting execution outputs) still goes throughspawn_blockingas before.Expected impact
Removes one blocking-thread-pool dispatch per block-builder loop iteration (out of the two that existed per iteration), which now fires on the tightened ~10ms cadence rather than the previous ~200ms cadence during active execution — i.e. up to ~20x more frequently per block than before #14842.
Verification
cargo build -p apollo_batcher— succeeds.SEED=0 cargo test -p apollo_batcher— allblock_builder_testcases pass (24/24), includingcase_4_block_full_before_is_doneandcase_5_block_full_after_is_done, which exercise this exact code path. (One unrelated pre-existing failure,batcher_test::call_contract_success, reproduces identically on the base commit without this change — it fails trying to download the Cairo compiler, a sandbox network limitation, not a regression.)🤖 Generated with Claude Code
Generated by Claude Code