Skip to content

bugfix: safe batch retirement and bounded abandon. - #12

Open
THU-syh wants to merge 3 commits into
xLLM-AI:v0.3.12_xllm_patchfrom
THU-syh:bugfix/hccl-retirement-0312-20261113
Open

THU-syh wants to merge 3 commits into
xLLM-AI:v0.3.12_xllm_patchfrom
THU-syh:bugfix/hccl-retirement-0312-20261113

Conversation

@THU-syh

@THU-syh THU-syh commented Nov 13, 2026 •

Copy link
Copy Markdown

Description

xLLM hands Mooncake a batch of borrowed KV buffers and must not release them before the transport has actually stopped touching them. Two failure modes were unsafe: releasing as soon as the aggregate status said FAILED (in-flight work still wrote into the freed pages), and publishing "retired" from a timeout/abort that never proved the transfer had stopped.

This PR makes retirement self-describing and gives callers a bounded, explicit way to give up:

  1. Opaque-handle retirement API. TransferEngine::getBatchRetirementStatus(BatchID, bool& retired) lets the SDK decide whether a batch may be released; callers no longer read private BatchDesc/TransferTask layouts (a WITH_METRICS build produced a different TransferTask layout across translation units and caused Task ID out of range). freeBatchID uses the same library-owned check and may clean up a failed batch once it is retired.
  2. Completion-callback lifetime protection. Terminal slice counters are published under a completion pin, and batch deletion waits until outstanding callbacks return (Transport::freeBatchID), so a callback can no longer outlive the batch descriptor.
  3. Safe-retirement signal. Terminal counters can come from a confirmed stop or from a give-up. getBatchRetirementStatus(batch_id, retired, quiescent) (new overload) reports the difference: quiescent == false means the batch retired because of an unproven timeout/abort and the caller must not release the buffers. Transport::isBatchQuiescent() is the transport hook; AscendDirect marks a slice unsafe when its terminal state comes from an unproven abort/timeout (local-copy stream timeout, async route failure with a submitted ADXL request, synchronous transfer timeout).
  4. Bounded abandon. TransferEngine::abandonBatch(batch_id, timeout, AbandonOutcome&) lets a caller that has waited long enough ask the transport to stop: kQuiescentRecoverable publishes the terminal counts (safe to free), kQuiescentDeviceFault keeps the batch pending on purpose (the caller must restart the engine instead of releasing memory), kNotQuiescent means no proof exists and the caller keeps waiting.
  5. HCCL publication ordering. The HCCL slice counters are published only after the stream has retired, and markSuccess() no longer rewrites the transferred byte counter.

Production transport

xLLM builds Mooncake with USE_ASCEND_DIRECT=ON (xllm/CMakeLists.txt), i.e. the AscendDirect transport. The HCCL transport (USE_ASCEND) is not built there, so the quarantine/abort work above is implemented and tested against AscendDirect; the HCCL hunks from the base commit are kept, but a bounded HCCL abandon implementation is out of scope for this PR and will be proposed separately.

TENT only certifies COMPLETED; a cached failure/timeout is not a quiescence proof, so the TENT variant reports quiescent == retired and abandonBatch returns NotImplemented. RDMA/TENT abandon is likewise future work.

Module

  • Transfer Engine (mooncake-transfer-engine)

Type of Change

  • Bug fix

How Has This Been Tested?

Hardware-free unit tests (mooncake-transfer-engine/tests/batch_retirement_test.cpp), 12 cases:

  • public/impl retirement forwarding; drain-before-release while slices are pending;
  • completion-callback gate for retirement and transport-level cleanup;
  • failed batch retires with an error status; query errors do not retire;
  • allocation failure, transient query error, Busy cleanup wait, bounded cleanup retry (transient success and exhaustion);
  • abandonBatch outcomes: recoverable publishes terminal state, device fault keeps the batch pending, unsupported transport reports NotImplemented;
  • retirement safety: retired && quiescent versus retired && !quiescent.

Local environment: aarch64 openEuler 24.03, GCC 12.3.1, glog 0.6.0, no NPU device. transfer_engine builds in both the default and USE_ASCEND_DIRECT=ON configurations; the retirement wrapper TU that previously failed to compile now builds. clang-format 20.1.8 applied with the repository .clang-format.

Not verified here: no NPU device test and no real HCCL failure-retirement test. Device validation of the AscendDirect unsafe/fault paths (including an opt-in stall injection) is planned on 910C and will be reported separately.

Integration status

Companion: xLLM MR791. Before xLLM can merge, this SDK revision has to be published into the official dependency and its configured GitCode mirror, and MR791 has to pin it and pass its native CI. The current official 0.3.12 pin does not contain the retirement API.

Backport HCCL terminal publication and callback lifetime protection to
v0.3.12_xllm_patch. Expose library-owned batch retirement status so callers
never inspect conditional private task layouts across the SDK boundary.
The public TransferEngine::getBatchRetirementStatus wrapper calls the
TransferEngineImpl middle layer, which had no such member, so the non-TENT
wrapper translation unit did not compile and the new SDK call was unusable
from the public API.

MultiTransport::getBatchRetirementStatus also called toBatchDesc
unqualified from a class that does not derive from Transport, breaking the
multi_transport.cpp translation unit in every configuration.

Fix both and add mooncake-transfer-engine/tests/batch_retirement_test.cpp:
hardware-free coverage for the public/impl forwarding path, drain-before-
release on pending slices, the completion-callback gate for both retirement
and transport cleanup, failed batches retiring with an error status, and
query errors that must not retire.

Verified locally (aarch64, GCC 12.3, glog/gflags/gtest, no NPU):
- transfer_engine.cpp wrapper TU failed before the impl fix and compiles
  after it; multi_transport.cpp failed before the qualification fix.
- transfer_engine library builds in both USE_ASCEND_DIRECT=ON and default
  configurations; ctest -R batch_retirement passes 7/7 in the default build.
- Not covered here: the HCCL transport TU needs adapter_hccp_common.h from
  the HCCL adapter, which this host does not provide; no NPU device test.
Terminal slice counters can come from two different events: the transport
confirmed that no access is in flight, or it gave up after a timeout/abort it
could not prove. Callers must not release borrowed buffers in the second case,
but the retirement API only exposed a single `retired` flag.

Expose the distinction:
- Transport::isBatchQuiescent(): transports that publish give-up terminal
  states override it. AscendDirect marks a slice unsafe when its terminal state
  comes from an unproven timeout/abort (local copy stream timeout, async route
  failure with a submitted ADXL request, sync transfer timeout).
- getBatchRetirementStatus(batch_id, retired, quiescent) overload on
  Transport managers, MultiTransport, TransferEngineImpl and TransferEngine.
  TENT only certifies COMPLETED, so it reports quiescent == retired.
- Transport::abandonBatch()/AbandonOutcome for stalled batches: an explicit,
  bounded stop request whose outcome tells the caller whether buffers are safe
  (recoverable) or the transport must be restarted (device fault). Only the
  recoverable outcome publishes the terminal counters.

Tests: hardware-free coverage for quiescent/non-quiescent retirement and the
three abandon outcomes. Local build: transfer_engine in both the default and
USE_ASCEND_DIRECT configurations; gtest 12/12. No device validation yet.
@THU-syh THU-syh changed the title bugfix: retire hccl batches before releasing borrowed buffers. bugfix: safe batch retirement and bounded abandon. Nov 15, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant