[None][fix] Keep ADP ranks in collective lockstep on request errors and fail fast on desync - #16687
[None][fix] Keep ADP ranks in collective lockstep on request errors and fail fast on desync#16687roborluo wants to merge 1 commit into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
Walkthrough
ChangesAttention-DP Response Coordination
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant ExecutorLoop
participant ErrorHandler
participant AttentionDPGather
participant PendingTransferResponses
ExecutorLoop->>ErrorHandler: process current executor errors
ErrorHandler->>AttentionDPGather: gather synchronized responses
AttentionDPGather-->>ErrorHandler: return validated contributions
ErrorHandler->>PendingTransferResponses: buffer or flush responses
ExecutorLoop->>PendingTransferResponses: flush after each loop pass
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tensorrt_llm/_torch/pyexecutor/py_executor.py`:
- Around line 6644-6651: Replace the unbounded resp!r interpolation in
_enqueue_responses with a bounded, safe summary that cannot expose full response
contents or depend on an unsafe custom __repr__. Preserve the existing type
information and collective-desynchronization context in the RuntimeError
message.
- Around line 6634-6652: Update the response-merging logic in _enqueue_responses
to validate every item in each gathered list or tuple before calling
gather_responses.extend. Require each entry to be a two-field response
structure, rejecting malformed values such as scalar entries or entries with the
wrong length via the same clear RuntimeError path, then extend only validated
entries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: f54097f4-1650-45d1-9543-e3eb9780cce6
📒 Files selected for processing (1)
tensorrt_llm/_torch/pyexecutor/py_executor.py
…t errors Under attention DP, _enqueue_responses gathers per-rank response lists via tp_gather, and TP collectives pair by call order, not by type. _handle_errors is reached only by the rank(s) that observed an error: for request-scoped failures such as a failed disagg generation KV transfer, a single rank enqueues its error responses solo while the other ranks proceed to the per-step tp_allgather(batch_size). The collectives cross: the erroring rank deserializes a response-list payload inside safe_allgather (UnpicklingError: pickle data was truncated) and rank 0's response gather receives a stray batch-size int (TypeError: 'int' object is not iterable in gather_responses.extend), while the remaining ranks hang until the hang detector fires. Observed on a GB300 TP8 ADP disagg gen server: rank 2 hit 'Error occurred during generation transfer' (dataTransceiver.cpp), entered _handle_errors, and its solo gather crossed with peers' _can_queue allgather in the same iteration. Fix: on the non-fatal path, buffer error responses in _pending_transfer_responses instead of enqueueing directly. The buffer is flushed at _flush_pending_transfer_responses, which every rank executes unconditionally at synchronized points in the executor loops, so the gather is always entered in lockstep. Fatal errors keep the direct enqueue since the executor is shutting down. This covers every per-rank-divergent caller of _handle_errors in one place. Same failure family as the per-rank-divergent collective entry fixed in NVIDIA#14020; complements the fail-fast diagnostic in NVIDIA#16687. Signed-off-by: Bofeng Luo <bofengl@nvidia.com>
|
/bot run --disable-fail-fast |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tensorrt_llm/_torch/pyexecutor/py_executor.py`:
- Around line 6527-6539: Move the _flush_pending_transfer_responses() call out
of the can_queue-guarded block in the non-overlap scheduling loop so it executes
at an unconditional, rank-symmetric point. Ensure it also runs when no batch can
be queued, while preserving collective ordering across ranks and existing
behavior when a batch is available.
- Around line 6527-6539: The non-fatal attention-DP path must retain each
request’s result queue until its buffered error response is flushed. Update the
termination flow around _do_terminate_request and
_flush_pending_transfer_responses so result_wait_queues entries are not removed
before _enqueue_responses can deliver the response; perform cleanup after
synchronized flushing, while preserving immediate cleanup for paths that do not
buffer responses.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 2f30393e-ae51-46b8-a77a-16dcd2986907
📒 Files selected for processing (1)
tensorrt_llm/_torch/pyexecutor/py_executor.py
|
/bot run --disable-fail-fast |
…t errors Under attention DP, _enqueue_responses gathers per-rank response lists via tp_gather, and TP collectives pair by call order, not by type. _handle_errors is reached only by the rank(s) that observed an error: for request-scoped failures such as a failed disagg generation KV transfer, a single rank enqueues its error responses solo while the other ranks proceed to the per-step tp_allgather(batch_size). The collectives cross: the erroring rank deserializes a response-list payload inside safe_allgather (UnpicklingError: pickle data was truncated) and rank 0's response gather receives a stray batch-size int (TypeError: 'int' object is not iterable in gather_responses.extend), while the remaining ranks hang until the hang detector fires. Observed on a GB300 TP8 ADP disagg gen server: rank 2 hit 'Error occurred during generation transfer' (dataTransceiver.cpp), entered _handle_errors, and its solo gather crossed with peers' _can_queue allgather in the same iteration. Fix: on the non-fatal path, buffer error responses in _pending_transfer_responses instead of enqueueing directly. The buffer is flushed at _flush_pending_transfer_responses, which every rank executes unconditionally at synchronized points in the executor loops, so the gather is always entered in lockstep. Fatal errors keep the direct enqueue since the executor is shutting down. This covers every per-rank-divergent caller of _handle_errors in one place. Same failure family as the per-rank-divergent collective entry fixed in NVIDIA#14020; complements the fail-fast diagnostic in NVIDIA#16687. Signed-off-by: Bofeng Luo <bofengl@nvidia.com>
33bd50a to
8f1ea81
Compare
|
/bot run --disable-fail-fast |
…t errors Under attention DP, _enqueue_responses gathers per-rank response lists via tp_gather, and TP collectives pair by call order, not by type. _handle_errors is reached only by the rank(s) that observed an error: for request-scoped failures such as a failed disagg generation KV transfer, a single rank enqueues its error responses solo while the other ranks proceed to the per-step tp_allgather(batch_size). The collectives cross: the erroring rank deserializes a response-list payload inside safe_allgather (UnpicklingError: pickle data was truncated) and rank 0's response gather receives a stray batch-size int (TypeError: 'int' object is not iterable in gather_responses.extend), while the remaining ranks hang until the hang detector fires. Observed on a GB300 TP8 ADP disagg gen server: rank 2 hit 'Error occurred during generation transfer' (dataTransceiver.cpp), entered _handle_errors, and its solo gather crossed with peers' _can_queue allgather in the same iteration. Fix: on the non-fatal path, buffer error responses in _pending_transfer_responses instead of enqueueing directly. The buffer is flushed at _flush_pending_transfer_responses, which every rank executes unconditionally at synchronized points in the executor loops, so the gather is always entered in lockstep. Fatal errors keep the direct enqueue since the executor is shutting down. This covers every per-rank-divergent caller of _handle_errors in one place. Same failure family as the per-rank-divergent collective entry fixed in NVIDIA#14020; complements the fail-fast diagnostic in NVIDIA#16687. Signed-off-by: Bofeng Luo <bofengl@nvidia.com>
8f1ea81 to
dcc4dc6
Compare
|
Addressed review feedback in the amended commit:
Not taken: per-entry (two-field tuple) validation of gathered items. The guard targets crossed collectives, where the stray payload is a non-list scalar; contributions that are lists of malformed entries would indicate a different bug and still fail loudly at the unpack in the consumer loop. |
|
/bot run --disable-fail-fast |
chienchunhung
left a comment
There was a problem hiding this comment.
Thanks for the PR!
|
Thanks for the comments, Will check |
dcc4dc6 to
12b5e6f
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/unittest/_torch/executor/test_py_executor.py`:
- Around line 1937-1949: Initialize the missing is_benchmark_disagg attribute in
_make_executor_loop_stub, using the default non-benchmark value expected by
_executor_loop before entering its loop. Keep the existing stub setup unchanged
otherwise.
- Around line 1887-2019: Add a focused test for PyExecutor._enqueue_responses
that mocks tp_gather() to return [1] and verifies the method raises RuntimeError
matching "TP collective desync". Initialize only the executor state and
dependencies required by _enqueue_responses, keeping the test alongside the
existing response-flush coverage.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: a1d4bc35-8fda-40b6-ae2d-f74ef7140ccc
📒 Files selected for processing (2)
tensorrt_llm/_torch/pyexecutor/py_executor.pytests/unittest/_torch/executor/test_py_executor.py
577def0 to
7e8c912
Compare
1b457e5 to
713d49d
Compare
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tensorrt_llm/_torch/pyexecutor/py_executor.py (1)
3982-4003: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winFlush buffered responses on overlap-loop early exits.
This adds the required flushes only to
_executor_loop._executor_loop_overlapcan also buffer a non-fatal ADP response in_handle_disagg_cache_errors_synced(), but its clean-exit path at Line 4476 and retry path at Lines 4481-4487 break or continue without flushing. The queued client can then wait until timeout.Add the same flush before both overlap-loop early exits. Add focused overlap-loop tests for clean shutdown and benchmark retry.
Proposed fix
if scheduled_batch is None: + self._flush_pending_transfer_responses() break can_forward, should_retry = self._check_benchmark_disagg_gate( scheduled_batch, can_forward) if should_retry: if self._is_kv_manager_v2: for req in scheduled_batch.generation_requests: self.kv_cache_manager.revert_allocate_generation( req) self._finalize_adp_dummy_allocation(False) + self._flush_pending_transfer_responses() continue🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/pyexecutor/py_executor.py` around lines 3982 - 4003, Update _executor_loop_overlap to call _flush_pending_transfer_responses() immediately before both its clean-exit break and benchmark-retry continue paths, matching the flush behavior in _executor_loop. Add focused tests covering buffered-response delivery during overlap-loop clean shutdown and benchmark retry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@tensorrt_llm/_torch/pyexecutor/py_executor.py`:
- Around line 3982-4003: Update _executor_loop_overlap to call
_flush_pending_transfer_responses() immediately before both its clean-exit break
and benchmark-retry continue paths, matching the flush behavior in
_executor_loop. Add focused tests covering buffered-response delivery during
overlap-loop clean shutdown and benchmark retry.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: a95abea6-7666-408d-b78d-b245c9c4ac11
📒 Files selected for processing (2)
tensorrt_llm/_torch/pyexecutor/py_executor.pytests/unittest/_torch/executor/test_py_executor.py
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/unittest/_torch/executor/test_py_executor.py
713d49d to
0fe5df7
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
Signed-off-by: Bofeng Luo <bofengl@nvidia.com>
0fe5df7 to
f15f0b2
Compare
|
/bot run --disable-fail-fast |
Summary
Under attention DP,
_enqueue_responsesperforms atp_gather; collectives must be entered by every rank in the same order. A request-scoped error can be observed by only one rank, so directly publishing its response can cross the peers' next collective (usuallytp_allgather(batch_size)).This change:
can_queueiterations.Test Coverage
Added focused unit coverage for buffered-response delivery, empty ADP participation, retry/shutdown exits, the single-flush behavior, and rank-local fatal handling.
Local macOS validation:
python3 -m py_compile— passedgit diff --check— passedcuda-pythonhas no supported macOS bindings); full test execution needs CUDA/Linux CI.PR Checklist
Dev Engineer Review
RuntimeErrorfor malformed gathered payloads and skipsNonecontributions.can_queueis TP-uniform under Attention-DP.QA Engineer Review
TestPendingTransferResponseFlushcovers rank-local fatal errors, empty Attention-DP contributions, buffered-response delivery and clearing, and executor-loop flushes during normal exit, benchmark retry, idle passes, and overlap-loop paths.tests/integration/test_lists/files changed. The added unit tests are not listed in test-db or QA files.