[None][fix] Keep ADP ranks in collective lockstep on request errors and fail fast on desync - #16687
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
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 publish responses
ExecutorLoop->>PendingTransferResponses: flush after each loop pass
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
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. |
0fe5df7 to
f15f0b2
Compare
|
/bot run --disable-fail-fast |
f15f0b2 to
67296bc
Compare
|
/bot run --disable-fail-fast |
|
Fixed some format issue. No logical change. Sorry first time contribute to TRTLLM |
67296bc to
7d74e24
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>
7d74e24 to
45ad013
Compare
|
/bot run --disable-fail-fast |
1 similar comment
|
/bot run --disable-fail-fast |
|
PR_Github #65433 [ run ] triggered by Bot. Commit: |
|
PR_Github #65433 [ run ] completed with state
|
… ADP fatal alignment explicit CI L0_MergeRequest_PR #53184 failed on this PR (x86_64 + SBSA + GPU stages). Unit tests (test_py_executor.py::TestPendingTransferResponseFlush): - LlmResponse() -> LlmResponse(request_id=...) (request_id is required). - hang_detector mocked as MagicMock() (it is used as a context manager; plain Mock() does not support the protocol). - dist.mapping.tp_group set to an iterable so _enqueue_responses' rank check works under the non-ADP flush path. _handle_errors ADP fatal handling (test_disagg_inflight_cancel_gate.py:: test_preclassified_fatal_error_keeps_adp_response_collectives_aligned raised "already fatal"): - Distinguish a synchronized (collective-aligned) fatal from a novel rank-local one. Instead of inferring alignment from `self._fatal_error is not None` (which a rank-local setter could trip, recreating the desync this PR prevents), add an explicit `fatal_is_collective_aligned` parameter that only the synchronized caller (_handle_disagg_cache_errors_synced, after its world allreduce) passes. A synchronized fatal publishes/terminates in lockstep across ranks and does not raise; a novel rank-local fatal skips the tp_gather and raises for local teardown. - Update test_disagg_inflight_cancel_gate.py to the explicit protocol. Signed-off-by: Bofeng Luo <bofengl@nvidia.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Head branch was pushed to by a user without write access
|
/bot run --disable-fail-fast |
|
PR_Github #65732 [ run ] triggered by Bot. Commit: |
|
PR_Github #65732 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
|
PR_Github #65813 [ run ] triggered by Bot. Commit: |
|
PR_Github #65813 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
|
PR_Github #65998 [ run ] triggered by Bot. Commit: |
|
PR_Github #65998 [ run ] completed with state |
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
Nonecontributions and raises a boundedRuntimeErrorfor malformed gathered payloads.can_queueis TP-uniform under Attention-DP.QA Engineer Review
TestPendingTransferResponseFlushcovers rank-local fatal errors, empty Attention-DP participation, buffered response delivery and clearing, rank-zero request termination, normal executor exits, benchmark retries, idle passes, and overlap-loop flushes.tests/integration/test_lists/files changed. The unit tests are not listed intest-db/orqa/files.