Skip to content

[TRTLLM-13409][fix] hard-kill all ranks when one rank's executor loop crashes#16592

Open
JunyiXu-nv wants to merge 4 commits into
NVIDIA:mainfrom
JunyiXu-nv:dev-junyix-feat-rank-crash-hard-kill
Open

[TRTLLM-13409][fix] hard-kill all ranks when one rank's executor loop crashes#16592
JunyiXu-nv wants to merge 4 commits into
NVIDIA:mainfrom
JunyiXu-nv:dev-junyix-feat-rank-crash-hard-kill

Conversation

@JunyiXu-nv

@JunyiXu-nv JunyiXu-nv commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Dev Engineer Review

  • Added configurable multi-rank crash handling with grace-period support and safe fallback behavior.
  • Executor crash cleanup now starts a watchdog before cleanup and performs hard-kill afterward while preserving the original exception.
  • Single-rank execution and disabled grace periods remain exempt; kill failures are contained.
  • No configuration or test-list files were changed.

QA Engineer Review

  • Added and modified coverage for:
    • hard_kill_on_rank_crash
    • _event_loop_wrapper crash and clean-exit paths
    • test_pool_session_shutdown_never_blocks_after_release
  • These tests are not referenced in tests/integration/test_lists/ (test-db/ or qa/), so CI/manual test-list coverage is not established.
  • Verdict: needs follow-up.

Description

When a rank's executor loop dies on an exception, the rank stops participating in collectives but nothing tells its peers: every peer blocks in its next collective until its own HangDetector fires 300s later, and the whole multi-GPU test session burns that long for an error that was already known (the A4 AutoDeploy catches are this signature: peers crash, the survivor wedges in ADP until the 300s backstop).

Single-rank worlds are exempt (no peers to unblock), and the kill helper never raises (it runs in a finally where an exception would mask the loop's original error).

Test Coverage

PR Checklist

Please review the following before submitting your PR:

  • PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

@JunyiXu-nv
JunyiXu-nv requested review from a team as code owners July 20, 2026 05:22
@JunyiXu-nv
JunyiXu-nv force-pushed the dev-junyix-feat-rank-crash-hard-kill branch from 03e5e9e to cf7fdc1 Compare July 20, 2026 05:23
@JunyiXu-nv

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0d20235f-6c56-4abc-9746-d2dea9f6b0cb

📥 Commits

Reviewing files that changed from the base of the PR and between c0c1a43 and d7d2cd4.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/pyexecutor/py_executor.py
  • tests/unittest/executor/test_proxy_fast_death.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unittest/executor/test_proxy_fast_death.py
  • tensorrt_llm/_torch/pyexecutor/py_executor.py

Walkthrough

Adds configurable rank-crash hard-kill handling with a grace period, optional watchdog, and defensive error handling. Executor crash cleanup now starts the watchdog before cleanup and invokes direct world termination afterward, with tests covering policy and ordering.

Changes

Rank crash handling

Layer / File(s) Summary
Configurable rank-crash kill policy
tensorrt_llm/_torch/pyexecutor/hang_detector.py
Adds environment-based grace parsing, optional disabling, world-level hard-kill behavior, and daemon watchdog startup.
Executor crash integration
tensorrt_llm/_torch/pyexecutor/py_executor.py
Tracks event-loop crashes, starts the watchdog before cleanup, and invokes direct hard-kill handling after cleanup.
Kill behavior validation
tests/unittest/_torch/executor/test_hang_detector_kill.py, tests/unittest/executor/test_proxy_fast_death.py
Tests grace handling, watchdog applicability, kill error suppression, crash cleanup ordering, exception preservation, clean exits, and shutdown state setup.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EventLoop
  participant Watchdog
  participant Cleanup
  participant HardKill
  EventLoop->>Watchdog: Arm after crash
  EventLoop->>Cleanup: Execute local cleanup
  EventLoop->>HardKill: Terminate world after cleanup
  Watchdog->>HardKill: Trigger after grace period
Loading

Suggested reviewers: schetlur-nv, reasonsolo, brnguyen2, xinhe-nv, yihuilu512

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows the required [ticket][type] format and accurately summarizes the main change.
Description check ✅ Passed The description explains the bug and fix, but the Test Coverage section is left blank.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #60270 [ run ] triggered by Bot. Commit: cf7fdc1 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #60270 [ run ] completed with state SUCCESS. Commit: cf7fdc1
/LLM/main/L0_MergeRequest_PR pipeline #48629 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@JunyiXu-nv

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #60547 [ run ] triggered by Bot. Commit: cf7fdc1 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #60547 [ run ] completed with state FAILURE. Commit: cf7fdc1
/LLM/main/L0_MergeRequest_PR pipeline #48859 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@JunyiXu-nv

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #60580 [ run ] triggered by Bot. Commit: cf7fdc1 Link to invocation

@nv-xtf nv-xtf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — One non-blocking question inline about kill reachability if cleanup blocks on a PP send handle.

# the grace inside lets the stashed error reach rank-local
# waiters and the ready handshake first, so the client sees
# the original exception rather than a bare worker death.
hard_kill_on_rank_crash(self.dist.world_size)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One reachability concern: the kill only runs after _executor_loop_cleanup() returns, but cleanup performs an unbounded wait() on pending PP send handles. If one of those handles hangs after the crash, this call is never reached, so peer ranks still rely on their 300s HangDetector — the behavior this PR aims to avoid. Could we start a separate watchdog thread before _executor_loop_cleanup() that waits for the grace period and then triggers the hard kill? This would ensure the kill still fires if cleanup blocks on a PP send handle.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for review! That really makes sense, adapted a watchdog for the graceful clean up.

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #60580 [ run ] completed with state FAILURE. Commit: cf7fdc1
/LLM/main/L0_MergeRequest_PR pipeline #48890 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

Comment thread tensorrt_llm/_torch/pyexecutor/py_executor.py Outdated
@JunyiXu-nv
JunyiXu-nv force-pushed the dev-junyix-feat-rank-crash-hard-kill branch from cf7fdc1 to ec5a475 Compare July 22, 2026 03:53
@JunyiXu-nv
JunyiXu-nv requested review from a team as code owners July 22, 2026 03:53
… crashes

When a rank's executor loop dies on an exception, the rank stops
participating in collectives but nothing tells its peers: every peer
blocks in its next collective until its own HangDetector fires 300s
later, and the whole multi-GPU test session burns that long for an
error that was already known (the A4 AutoDeploy catches are this
signature: peers crash, the survivor wedges in ADP until the 300s
backstop).

Escalate at error time instead: after the loop's local cleanup has
woken rank-local waiters, hard-kill the world via the ST-1 propagation
path. A grace period (TLLM_RANK_CRASH_HARD_KILL_GRACE, default 10s,
negative disables) lets the cleaner error paths win the race first --
the stashed error reaches rank-local response waiters, the init-phase
ready handshake returns the real exception to the proxy, and the worker
future completes with the original error -- so the client reports the
actual failure rather than a bare worker death.

Single-rank worlds are exempt (no peers to unblock), and the kill
helper never raises (it runs in a finally where an exception would mask
the loop's original error).

Signed-off-by: JunyiXu-nv <219237550+JunyiXu-nv@users.noreply.github.com>
…blocks or raises

The kill sat after _executor_loop_cleanup() in the finally block, so it
was skippable in exactly the situations it exists for: cleanup blocking
without bound (unbounded wait() on a PP send handle wedged by the crash)
or cleanup raising (aborting the finally before the kill). Either way
peers fall back to burning 300s in their own HangDetectors.

Arm a daemon watchdog thread BEFORE cleanup that fires the kill at
crash + grace regardless of cleanup progress, and nest the post-cleanup
kill in its own finally so a raising cleanup cannot skip it.

Signed-off-by: JunyiXu-nv <219237550+JunyiXu-nv@users.noreply.github.com>
…pool-session shutdown test

The test builds MpiPoolSession via __new__ (a real spawn is neither
needed nor wanted), but MpiPoolSession.shutdown() now reads n_workers
(added by NVIDIA#16456 while the test was in flight) and _wait_shutdown, both
set only in __init__. Provide them explicitly and drop the waive.

Signed-off-by: JunyiXu-nv <219237550+JunyiXu-nv@users.noreply.github.com>
@JunyiXu-nv
JunyiXu-nv force-pushed the dev-junyix-feat-rank-crash-hard-kill branch from ec5a475 to c0c1a43 Compare July 22, 2026 07:34
@JunyiXu-nv

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #60921 [ run ] triggered by Bot. Commit: c0c1a43 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #60921 [ run ] completed with state SUCCESS. Commit: c0c1a43
/LLM/main/L0_MergeRequest_PR pipeline #49186 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@xinhe-nv

Copy link
Copy Markdown
Collaborator

@coderabbitai review the code as qa engineer, can it handle multi nodes scenarios? in multi nodes scenarios, each node has multiple tasks.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Relevant module outlines ==='
ast-grep outline tensorrt_llm/_torch/pyexecutor/hang_detector.py --items all || true
ast-grep outline tensorrt_llm/_torch/pyexecutor/py_executor.py --items all --match 'PyExecutor|_event_loop_wrapper' || true
ast-grep outline tests/unittest/_torch/executor/test_hang_detector_kill.py --items all || true

echo '=== Crash-kill and propagation definitions/usages ==='
rg -n -C 5 --glob '*.py' \
  '\b(propagate_hard_kill|hard_kill_on_rank_crash|start_rank_crash_kill_watchdog|_event_loop_wrapper|RANK_CRASH_KILL_GRACE_ENV)\b' \
  tensorrt_llm tests

echo '=== Distributed topology / process-launch context ==='
rg -n -C 4 --glob '*.py' \
  '\b(world_size|global_rank|local_rank|node_rank|MPI|mpirun|torchrun|rank)\b' \
  tensorrt_llm/_torch/pyexecutor | head -n 500

echo '=== Relevant test body ==='
sed -n '1,420p' tests/unittest/_torch/executor/test_hang_detector_kill.py

Length of output: 106501


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Hard-kill implementation ==='
sed -n '45,185p' tensorrt_llm/_torch/pyexecutor/hang_detector.py

echo '=== MPI helper definitions and related abort calls ==='
rg -n -C 8 --glob '*.py' \
  '^\s*def (mpi_comm|mpi_disabled|global_mpi_rank|mpi_rank)\b|\b(Abort|abort)\s*\(' \
  tensorrt_llm tests | head -n 600

echo '=== MPI helper implementation candidates ==='
fd -t f -e py . tensorrt_llm | xargs rg -l \
  '^\s*def (mpi_comm|mpi_disabled|global_mpi_rank|mpi_rank)\b' | while IFS= read -r file; do
  echo "--- $file"
  rg -n -C 5 '^\s*def (mpi_comm|mpi_disabled|global_mpi_rank|mpi_rank)\b' "$file"
done

echo '=== Distributed test and launcher candidates ==='
fd -t f -e py . tests | xargs rg -n -i -C 3 \
  '\b(mpirun|mpi4py|MPI\.COMM_WORLD|torchrun|multinode|multi-node|nnodes|nproc.per.node)\b' | head -n 800

Length of output: 115054


@xinhe-nv Static QA verdict: intended to handle multi-node MPI worlds, but it is not yet proven or guaranteed for a multi-node / multiple-tasks-per-node deployment.

What should work

  • The logic is based on the global self.dist.world_size, not node-local rank counts. Therefore, a crash on any rank enters the same kill path whether peers are on the same node or remote nodes.
  • When MPI is initialized with MPI.THREAD_MULTIPLE, propagate_hard_kill() calls mpi_comm().Abort(137). For a communicator spanning all tasks in the job, this is the correct mechanism to tear down ranks across all nodes and unblock NCCL collectives.
  • The watchdog is armed before cleanup, which addresses the case where cleanup is blocked waiting on a send handle after a rank crashes.

Important limitation / risk

If MPI.THREAD_MULTIPLE is unavailable, the implementation falls back to SIGKILL of only the local process. Remote ranks are then terminated only if the launcher propagates a bad exit:

  • mpirun: expected to propagate the failure.
  • srun: requires --kill-on-bad-exit (as the code comment notes).
  • Other schedulers/wrappers need equivalent verified behavior.

Without that launcher setting, tasks on other nodes can remain blocked until their independent hang detectors time out. Thus, multi-node correctness currently depends on deployment configuration rather than being fully enforced by this code.

Coverage gap

tests/unittest/_torch/executor/test_hang_detector_kill.py tests mocked propagation with synthetic world_size values only. It does not test:

  1. at least two nodes;
  2. multiple ranks/tasks per node;
  3. an injected executor-loop crash on one rank while others block in a collective;
  4. both MPI.THREAD_MULTIPLE and fallback behavior;
  5. the actual srun/launcher kill-propagation configuration.

QA recommendation before relying on this in production

Add a multi-node failure-injection integration test with, for example, two nodes and at least two tasks per node. Force one rank’s executor loop to fail while the remaining ranks are waiting in a collective, then assert:

  • all ranks across both nodes exit within TLLM_RANK_CRASH_HARD_KILL_GRACE plus a bounded margin;
  • no peer waits for the 300-second hang-detector timeout;
  • the launcher reports a non-zero job exit;
  • the test is run for the supported launchers, especially Slurm with --kill-on-bad-exit.

Also document that all ranks that must die together must belong to the same MPI communicator/job. This mechanism intentionally kills the whole participating world; it is not scoped per node or per individual task.

@xinhe-nv

Copy link
Copy Markdown
Collaborator

@coderabbitai is --kill-on-bad-exit mandatory for cluster job? otherwise it won't kill the hang ranks?

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🤖 web_search executed:

Slurm srun --kill-on-bad-exit documentation default behavior task exits non-zero terminate job step

Length of output: 5224


@xinhe-nv No—not universally.

  • If propagate_hard_kill() reaches MPI_Comm.Abort() on the communicator containing all ranks, it should terminate the full multi-node MPI world directly. srun --kill-on-bad-exit is not required for that path.
  • If it falls back to killing only the crashing local process, then the other ranks are killed promptly only if the launcher terminates the whole srun step on that task failure. For Slurm, explicitly pass --kill-on-bad-exit (or -K1) unless the cluster’s KillOnBadExit configuration is known to be enabled.

Without either a successful communicator-wide MPI abort or Slurm step-wide failure propagation, peers on other nodes can remain hung in collectives until another timeout or external job cleanup occurs.

For deterministic cluster behavior, I recommend launching the participating ranks in one srun step with:

srun --kill-on-bad-exit=1 ...

Also ensure the job does not use srun --no-kill / SLURM_NO_KILL, because that disables automatic termination on node failure and can defeat step-level kill behavior.

Slurm documents that, when --kill-on-bad-exit is omitted, the default comes from the site-level KillOnBadExit setting: https://slurm.schedmd.com/srun.html

@JunyiXu-nv

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #61181 [ run ] triggered by Bot. Commit: d7d2cd4 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #61181 [ run ] completed with state FAILURE. Commit: d7d2cd4
/LLM/main/L0_MergeRequest_PR pipeline #49430 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@JunyiXu-nv

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #61695 [ run ] triggered by Bot. Commit: d7d2cd4 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #61695 [ run ] completed with state FAILURE. Commit: d7d2cd4
/LLM/main/L0_MergeRequest_PR pipeline #49900 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

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.

5 participants