feat(driver): eventfd-driven reaping to replace the 200us busy-poll (backlog#1102)#1
Merged
Merged
Conversation
…rustfs/backlog#1102) The spike drove the loop with a 200us sleep + 50ms recv_timeout busy-poll, adding a latency floor and idle CPU. Replace it with two eventfds: one is registered with the ring (IORING_REGISTER_EVENTFD) so the kernel signals it on every CQE, the other is signaled by submit()/shutdown so a new message wakes the loop immediately. The loop blocks in poll() on both (with a 50ms heartbeat that only bounds the wait so the drain deadline is still checked and queued cancels are picked up). All intake/submit/reap/bounded-drain logic is unchanged — submit() still runs each turn, flushing the NODROP overflow list, so the rustfs/backlog#1056 hang does not return. This is internal only: the public API and every invariant are unchanged, and all 11 cancel-safety tests pass under real io_uring (both run-docker.sh legs). The dedicated driver thread stays; moving reaping onto a tokio AsyncFd (no thread) is the larger remaining step. Co-Authored-By: heihutu <heihutu@gmail.com>
houseme
added a commit
that referenced
this pull request
Jul 10, 2026
The README described eventfd reaping, O_DIRECT, and LocalIoBackend integration as unlanded and listed streaming reads on the roadmap; all of that has since merged or been closed by measurement. Bring it in line with the actual public API and record the honest performance picture. README changes: - status now reflects that the read path is wired into rustfs/rustfs behind a runtime probe and off by default (RUSTFS_IO_URING_READ_ENABLE), and how to pin the git dependency; - document sharded rings (probe_and_start_sharded) and native O_DIRECT (read_at_direct) with runnable examples matching the current signatures; - a "when this crate helps — and when it does not" section reporting the measured results, including where io_uring loses (single sequential stream, low concurrency) and the roughly-neutral end-to-end S3 GET, plus the two benchmarking traps this repo hit (a 76x regression mistaken for a win, and page-cache-hit microbenchmarks not transferring end-to-end); - roadmap trimmed to what is actually open (write path, register_files, SQPOLL) with the closed-by-measurement decisions moved to the CHANGELOG. Add CHANGELOG.md (Keep a Changelog format): every landed change #1..#6 with the commit that carried it, the pre-history of the audited spike, and a "decisions recorded, not implemented" section so the NO-GO items are not silently re-opened. Verified README claims against source: the six documented signatures match src/driver.rs, ProbeFailure/StatsSnapshot/UringDriver are the lib exports, and the O_DIRECT example's alignment passes the driver's validation. Co-authored-by: heihutu <heihutu@gmail.com>
houseme
added a commit
that referenced
this pull request
Jul 10, 2026
* docs: refresh README for shipped features and add a CHANGELOG The README described eventfd reaping, O_DIRECT, and LocalIoBackend integration as unlanded and listed streaming reads on the roadmap; all of that has since merged or been closed by measurement. Bring it in line with the actual public API and record the honest performance picture. README changes: - status now reflects that the read path is wired into rustfs/rustfs behind a runtime probe and off by default (RUSTFS_IO_URING_READ_ENABLE), and how to pin the git dependency; - document sharded rings (probe_and_start_sharded) and native O_DIRECT (read_at_direct) with runnable examples matching the current signatures; - a "when this crate helps — and when it does not" section reporting the measured results, including where io_uring loses (single sequential stream, low concurrency) and the roughly-neutral end-to-end S3 GET, plus the two benchmarking traps this repo hit (a 76x regression mistaken for a win, and page-cache-hit microbenchmarks not transferring end-to-end); - roadmap trimmed to what is actually open (write path, register_files, SQPOLL) with the closed-by-measurement decisions moved to the CHANGELOG. Add CHANGELOG.md (Keep a Changelog format): every landed change #1..#6 with the commit that carried it, the pre-history of the audited spike, and a "decisions recorded, not implemented" section so the NO-GO items are not silently re-opened. Verified README claims against source: the six documented signatures match src/driver.rs, ProbeFailure/StatsSnapshot/UringDriver are the lib exports, and the O_DIRECT example's alignment passes the driver's validation. Co-Authored-By: heihutu <heihutu@gmail.com> * fix: gate Linux-only benchmark examples --------- Co-authored-by: heihutu <heihutu@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
First productionization step for rustfs/backlog#1102: replace the spike's 200µs busy-poll driver loop with eventfd-driven reaping.
Change
Two
eventfds wake the driver loop:IORING_REGISTER_EVENTFD) so the kernel signals it on every CQE;submit()/shutdown so a new message wakes the loop immediately.The loop blocks in
poll()on both, with a 50ms heartbeat that only bounds the wait (so the bounded-drain deadline is still checked and a queued cancel is picked up promptly). All intake/submit/reap/bounded-drain logic is unchanged —submit()still runs every turn, flushing the NODROP overflow list, so the rustfs/backlog#1056 hang does not return.Internal only: the public API and every cancel-safety invariant are unchanged.
Verification
run-docker.shlegs (leg 1 seccomp-blocked → graceful degradation; leg 2 unconfined → real io_uring), release build.cargo fmt --check+clippy --all-targets -D warningsclean.Scope
The dedicated driver thread stays. Moving reaping onto a tokio
AsyncFd(removing the thread, integrating with the tokio reactor) is the larger remaining part of #1102, along with O_DIRECT, async backpressure, and the singleton ring.