apollo_storage: flush mmap files concurrently on dirty commits - #14939
apollo_storage: flush mmap files concurrently on dirty commits#14939gkaempfer wants to merge 3 commits into
Conversation
Each of the 8 file handlers' flush() calls its own independent blocking msync syscall. Running them sequentially on every batch commit pays their I/O cost one after another; running them on separate threads via std::thread::scope overlaps that I/O instead. Addresses part of the existing TODO(dan) on this function. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WkNTypJuaEL2w9hDAdK5Yb
batch_size defaults to 1, so FileHandlers::flush runs on every commit, and most commits (header/marker-only writes) leave all 8 mmap files clean. Spawning 8 threads unconditionally regressed that common path relative to the prior sequential version. Add needs_flush() and bail out before entering thread::scope when nothing needs an msync. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WkNTypJuaEL2w9hDAdK5Yb
The early return in FileHandlers::flush() was function-scoped, so it skipped the #[latency_histogram] macro's post-body recording of storage_file_handler_flush_latency_seconds. Restructure as an if-block so the metric still observes every call, including the no-op case. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WkNTypJuaEL2w9hDAdK5Yb
PR SummaryMedium Risk Overview When at least one file is dirty, the same eight Reviewed by Cursor Bugbot for commit ab72821. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
Artifacts upload workflows: |
What
FileHandlers<RW>::flush()(inapollo_storage) flushes 8 independent mmap-backed files sequentially on everyStorageTxnRW::commit(). Eachflush()call is an independent blockingmsyncsyscall on its own file. This PR:std::thread::scopewhen at least one file is dirty, overlapping theirmsynccalls instead of paying for them one after another.needs_flush()check (reads the existingshould_flushbool under the file's own mutex) so thethread::scope/8-spawn machinery is skipped entirely when nothing is dirty — which, sincebatch_sizedefaults to1and most commits (e.g. header/marker-only writes) don't touch every file, is the common case. Skipping this matters: unconditionally spawning 8 threads on every commit measured as a ~3000x regression (~100ns → ~300µs) on that common no-op path in review, and it's paid while holding the crate's globalSharedStatemutex, which would have serialized all other storage writers.flushing concurrentlyhalf of the pre-existingTODO(dan)comment on this function (theflushing only the relevant fileshalf remains, since spawning only for the specific dirty files was measured to buy nothing further given productionmmap_file_config.max_sizeis ~1TB andmsynccost dominates any spawn overhead once ≥1 file is actually dirty).Why
Follow-up performance review of #14927 and #14928 (both merged into
main), triggered automatically after those PRs merged. Neither of those PRs itself needed further optimization (they're log-verbosity/cost changes), but #14927 touches this exactflush()function and its diff includes theTODO(dan)comment above, which flagged an unaddressed, real hot-path optimization opportunity:flush()runs on every block commit and was doing 8 sequential blocking I/O syscalls.This PR went through two internal review-and-fix rounds (via an independent Opus review) before being opened:
needs_flush()gate.returnbypassed the#[latency_histogram]macro's latency recording (the macro'sreturn_value = { ... }wrapper doesn't see returns from inside the wrapped block) — fixed by restructuring to anifblock instead of an early return, sostorage_file_handler_flush_latency_secondsstill observes every call including the no-op case.Test plan
cargo build -p apollo_storageSEED=0 cargo test -p apollo_storage— 317 passed, 0 failed, 1 ignored (matchesorigin/mainbaseline)cargo clippy -p apollo_storage --all-targets— cleanscripts/rust_fmt.sh— no diff🤖 Generated with Claude Code
Generated by Claude Code