feat(io): add io_uring backend#2389
Conversation
Merge Protections🟢 All 3 merge protections satisfied — ready to merge. Show 3 satisfied protections🟢 Require kind label
🟢 Require version label
🟢 Require linked issue for feature/bug PRs
|
There was a problem hiding this comment.
Code Review
This pull request introduces support for the io_uring asynchronous I/O framework (uring_io) as an alternative I/O backend. It adds CMake configuration options to detect and link liburing, integrates the new I/O type into various datacell factories, and implements the UringIO class along with its parameters and unit tests. The feedback highlights several critical areas for improvement: a potential file descriptor and file leak in the UringIO constructor if initialization fails, a performance bottleneck caused by mixing O_DIRECT reads with buffered writes (requiring frequent fsync calls), error-prone manual resource management in MultiReadImpl that should leverage RAII, and the use of preprocessor macros for fallback types which should be replaced with clean C++ type aliases.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
Adds an optional io_uring-based I/O backend for Linux builds, integrating it into IO parameter parsing and the major datacell factories, with CMake-based detection/linking of liburing and fallback behavior when unavailable.
Changes:
- Introduces
UringIO,UringIOContext, andUringIOParameter(plus unit tests) underHAVE_LIBURING. - Extends IO type parsing/dispatch and datacell factories to recognize
"uring_io", with fallback-to-buffer behavior whenliburingis missing. - Adds a build option (
ENABLE_LIBURING) and CMake detection/linking forliburing.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/io/uring_io.h | Declares UringIO and provides a no-liburing alias fallback. |
| src/io/uring_io.cpp | Implements UringIO operations including multi-read using io_uring. |
| src/io/uring_io_test.cpp | Adds unit tests for UringIO behavior. |
| src/io/uring_io_parameter.h | Defines UringIOParameter with fallback behavior when liburing is absent. |
| src/io/uring_io_parameter.cpp | Implements JSON (de)serialization for UringIOParameter. |
| src/io/uring_io_parameter_test.cpp | Adds a unit test for UringIOParameter JSON round-tripping. |
| src/io/uring_io_context.h | Adds an io_uring context wrapper and pool type. |
| src/io/io_parameter.cpp | Adds "uring_io" parsing with one-time fallback warning when liburing is unavailable. |
| src/io/io_headers.h | Wires uring_io.h into the common IO header set. |
| src/io/CMakeLists.txt | Adds new sources and links liburing when available. |
| src/inner_string_params.h | Adds the "uring_io" IO type string constant. |
| src/datacell/rabitq_split_datacell.h | Allows "uring_io" as a known IO type for validation. |
| src/datacell/graph_interface.cpp | Enables graph datacell instantiation with UringIO. |
| src/datacell/flatten_interface.cpp | Enables flatten datacell instantiation with UringIO. |
| src/datacell/bucket_interface.cpp | Enables bucket datacell instantiation with UringIO. |
| cmake/VSAGSystemDeps.cmake | Detects liburing and defines HAVE_LIBURING/NO_LIBURING. |
| cmake/VSAGOptions.cmake | Adds ENABLE_LIBURING option. |
3b1b211 to
da3ff4d
Compare
da3ff4d to
8ca7877
Compare
640f09c to
acfe848
Compare
acfe848 to
c857bb3
Compare
c857bb3 to
273d1d1
Compare
273d1d1 to
0481c6b
Compare
a4e3e84 to
bb6fe09
Compare
bb6fe09 to
0c6d81c
Compare
0c6d81c to
aff8f32
Compare
aff8f32 to
e793549
Compare
e793549 to
0f49e96
Compare
0f49e96 to
27c575e
Compare
|
Automated pull request review completed. Review effort: Submitted 4 inline comments. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/io/uring_io/uring_io_test.cpp:56
- The UringIO parameter test doesn’t assert the intended behavior when liburing is missing (factory should fall back to BufferIOParameter and ToJson should reflect the fallback). Adding a compile-time conditional check here (like async_io_test.cpp does) will prevent this test from silently passing while not validating the fallback path.
auto json = JsonType::Parse(fmt::format(param_str, path));
auto io_param = IOParameter::GetIOParameterByJson(json);
IndexCommonParam common_param;
common_param.allocator_ = allocator;
auto io = std::make_unique<UringIO>(io_param, common_param);
TestBasicReadWrite(*io);
vsag-bot
left a comment
There was a problem hiding this comment.
Automated inline review completed.
Review effort: high (775 changed lines across 19 files).
Submitted 4 inline comments.
Reviewed commit 9af05eb.
Add an optional UringIO backend with liburing detection, IO parameter parsing, datacell factory wiring, and unit coverage for both liburing and fallback paths. Address review feedback: - Add missing <mutex>, <cerrno>, <cstdlib>, <fmt/format.h> includes - Replace malloc/free with allocator_->Allocate/Deallocate for consistency - Fix potential integer overflow in offset bounds checks - Add uring_io.h to io_headers.h aggregation - Make ReleaseImpl non-static to access allocator Signed-off-by: LHT129 <tianlan.lht@antgroup.com> Assisted-by: OpenCode
| int submitted = io_uring_submit(ring); | ||
| if (submitted < 0) { | ||
| for (auto& obj : objs) { | ||
| ReleaseUringReadObject(obj); | ||
| } | ||
| ctx_guard.Abandon(); | ||
| throw VsagException(ErrorType::INTERNAL_ERROR, | ||
| fmt::format("io_uring_submit failed: {}", strerror(-submitted))); | ||
| } | ||
|
|
||
| int remaining_to_submit = static_cast<int>(prepared) - submitted; | ||
| while (remaining_to_submit > 0) { | ||
| int ret = io_uring_submit(ring); | ||
| if (ret <= 0) { | ||
| break; | ||
| } | ||
| submitted += ret; | ||
| remaining_to_submit -= ret; | ||
| } | ||
|
|
||
| int first_error = 0; | ||
| bool has_short_read = false; | ||
| uint64_t completed = 0; | ||
| while (completed < static_cast<uint64_t>(submitted)) { |
| void | ||
| Abandon() { | ||
| static std::mutex abandon_mutex; | ||
| static std::vector<std::shared_ptr<UringIOContext>> abandoned; | ||
| std::lock_guard<std::mutex> lock(abandon_mutex); | ||
| abandoned.push_back(std::move(ctx_)); | ||
| ctx_ = nullptr; | ||
| } |
| UringIO::ResizeImpl(uint64_t size) { | ||
| auto ret = IOSyscall::FTruncate(this->wfd_, size); | ||
| if (ret == -1) { | ||
| throw VsagException(ErrorType::INTERNAL_ERROR, "ftruncate failed"); | ||
| } | ||
| this->size_ = size; | ||
| } |
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| #include "io/uring_io/uring_io.h" |
| UringIO::UringIO(const IOParamPtr& param, const IndexCommonParam& common_param) | ||
| : UringIO(std::dynamic_pointer_cast<UringIOParameter>(param), common_param) { | ||
| } |
What
This PR adds an optional
io_uringIO backend for Linux systems with liburing available.Changes include:
UringIO,UringIOContext, andUringIOParameter.ENABLE_LIBURING,HAVE_LIBURING, andNO_LIBURING.uring_iointo IO parameter parsing, IO headers, flatten/bucket/graph datacell factories, and RabitQ split IO type validation.uring_iotobuffer_iowith a one-time warning when liburing is unavailable.Both filtered test runs passed with 4 test cases and 16805 assertions.
Closes #1533