[TransferEngine] Propagate batch memory operation errors#2869
Conversation
There was a problem hiding this comment.
Code Review
This pull request improves error handling and propagation in memory registration and unregistration batch operations across various transport implementations (such as HCCL, UBShmem, Barex, CXI, CXL, EFA, HIP, NVLink, RDMA, Sunrise Link, and TCP). It ensures that errors from individual registration or unregistration calls are properly captured and returned (either by returning early or tracking the first error encountered), rather than being ignored or mapped to generic errors. A unit test has also been added to verify that unregisterLocalMemoryBatch correctly propagates transport errors. I have no feedback to provide.
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.
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
he-yufeng
left a comment
There was a problem hiding this comment.
Reviewed as a maintainer: traced the per-buffer error flow through the backends and the aggregation loop, not just the diff. This is a real fix and the pattern is applied consistently.
What I checked:
The bug is real. Every classic backend's registerLocalMemoryBatch / unregisterLocalMemoryBatch called registerLocalMemory(...) / unregisterLocalMemory(...) in a loop and discarded the return value, then fell through to updateLocalSegmentDesc() and returned that. So a per-buffer transport failure was logged-or-dropped and the batch still reported success. The fix captures each per-buffer return and short-circuits with if (ret) return ret;, so the first failing buffer propagates and — because the check sits before updateLocalSegmentDesc() — metadata is published only when every buffer registered. That gating is the important half and it's correct.
The if (ret < 0) → if (ret) change in transfer_engine_impl.cpp is the other half, not cosmetic. Some backends surface positive status codes (e.g. the CANN/HCCL family, where success is 0 and errors are positive), so the old ret < 0 silently let a positive error through as success. Switching to "any nonzero is an error" is what actually catches those. It's sound as long as no backend's batch call returns a positive non-error value — and it doesn't: each batch function returns either a propagated registerLocalMemory code (0 / error) or updateLocalSegmentDesc() (0 / error), never a positive count. Worth a one-line confirmation from you that this holds for every backend you touched, since it's the one assumption the change rests on.
The parallel-backend "wait for all in-flight ops, return the first error" behavior stays inside each backend, and the TCP regression (unregister an address that was never registered) pins the previously-swallowed path. Consistent across the backend list.
One note in passing: the HcclTransport hunk also changes the trailing registerLocalMemory(..., true, -1) to ..., true, false) — an int -1 where a bool is expected. Looks like a correct cleanup, just calling it out so it's intentional and not swept in.
LGTM.
338c93c to
dc1922e
Compare
|
Confirmed: every touched batch API returns 0 on success and nonzero only on error; none returns a positive non-error value. The HCCL |
|
The current |
he-yufeng
left a comment
There was a problem hiding this comment.
Read the batch memory paths across the backends. This is a real fix: the classic / hccl / ubshmem batch register and unregister discarded per-buffer transport failures and returned only the metadata-update result, so a failed buffer registration surfaced as success. Two things are done well: the parallel backends (barex, cxi) waiting for every in-flight op and returning the first error is the right shape, and switching the per-buffer calls to update_metadata=false so the single batch-end updateLocalSegmentDesc() publishes once (instead of a redundant per-buffer update) is a nice cleanup.
A few points, none blocking:
-
Partial-batch register can leave orphans. In the sequential backends (e.g.
HcclTransport::registerLocalMemoryBatch),if (ret) return retearly-returns on the first buffer's failure. Since the per-buffer calls now passupdate_metadata=falseand the batch-endupdateLocalSegmentDesc()is skipped by the early return, buffers[0..i-1]end up registered in the transport but not reflected in metadata. What's the contract on a partial-batch failure - does the caller tear down / roll back, or are those registrations reconciled later? If neither, unregistering the already-registered buffers before returning (best-effort), or documenting the partial-failure state, would avoid an untracked registration. -
The
if (ret < 0)->if (ret)widening now treats any nonzero as an error. Is the register/unregister return contract strictly 0=success / nonzero=error across every transport, i.e. can no transport ever return a positive non-error (a count, handle, or size)? If one can, this would turn a positive non-error into a spurious failure. If the convention is already 0-or-negative everywhere, the widening is harmless - just worth confirming. -
Sequential unregister should be best-effort like the parallel ones. The parallel backends' unregister loops correctly collect
first_errorand keep going, but the sequentialunregisterLocalMemoryBatch(hccl) early-returns on the first failure, leaving the remaining addresses registered. On a teardown path that is a leak, and it is inconsistent with the parallel backends. Making the sequential unregister best-effort (keep unregistering, return the first error) would match them.
dc1922e to
516b949
Compare
|
Addressed in two follow-up commits: |
#2965) TransferEngineImpl::registerLocalMemory registered the region on each transport in turn but, when a later transport failed, only released the reservation bookkeeping and returned -- leaving the region still registered on the transports that had already succeeded. Track the attempted transports and unregister them in reverse on failure, mirroring the rollback registerLocalMemoryBatch gained in #2869. Refs #2869 Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
…2869) * [TransferEngine] Propagate batch memory operation errors * [TransferEngine] Make batch unregister best-effort * [TransferEngine] Roll back failed batch registrations
kvcache-ai#2965) TransferEngineImpl::registerLocalMemory registered the region on each transport in turn but, when a later transport failed, only released the reservation bookkeeping and returned -- leaving the region still registered on the transports that had already succeeded. Track the attempted transports and unregister them in reverse on failure, mirroring the rollback registerLocalMemoryBatch gained in kvcache-ai#2869. Refs kvcache-ai#2869 Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
Description
Batch memory operations could lose transport errors and leave partial registration state. This PR:
Regression tests cover unknown addresses, continuation after errors, and partial-registration rollback.
Module
Type of Change
How Has This Been Tested?
Test commands:
```bash
cmake -S . -B build -G Ninja -DBUILD_UNIT_TESTS=ON -DWITH_TE=ON -DWITH_STORE=OFF -DWITH_STORE_RUST=OFF -DWITH_EP=OFF -DWITH_P2P_STORE=OFF
cmake --build build --target transport_uint_test -j4
./build/mooncake-transfer-engine/tests/transport_uint_test
```
Test results:
Checklist
AI Assistance Disclosure