Skip to content

[None][test] disagg startup: detect and fail fast on worker OOM/crash#16456

Merged
brnguyen2 merged 8 commits into
NVIDIA:mainfrom
brnguyen2:brnguyen/disagg-startup-fail-fast
Jul 16, 2026
Merged

[None][test] disagg startup: detect and fail fast on worker OOM/crash#16456
brnguyen2 merged 8 commits into
NVIDIA:mainfrom
brnguyen2:brnguyen/disagg-startup-fail-fast

Conversation

@brnguyen2

@brnguyen2 brnguyen2 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Log work_dir and worker log paths at launch so failures are easy to locate without digging through pytest output.
  • Fix worker log/config filename collision when multiple workers share port=0: use worker_index instead of port in filenames (worker_ctx_0.log, worker_ctx_1.log, etc.).
  • Detect worker crashes during startup via process.poll(); persist worker logs under cwd so they survive test teardown.
  • Fix workspace cleanup on overlayfs: use ignore_errors=True on shutil.rmtree since overlayfs may reject directory removal even with correct permissions.
  • Fix swallowed crash exception: previously ticker.cancel() discarded any RuntimeError _tick() had raised; await ticker in a finally block so the exception propagates correctly.
  • Fix watchdog exception silently leaving wait_for_disagg_server_ready polling for up to server_start_timeout: run both as asyncio tasks with asyncio.wait(FIRST_COMPLETED) so whichever finishes first (success or exception) cancels the other.
  • Detect workers alive but hung (stuck in MPI cleanup after OOM) via /cluster_info: if no new workers register for 60s after the disagg server first becomes reachable, declare the cluster stuck.
  • Log startup progress every 10s (elapsed, remaining, workers registered) and log total setup duration after cluster is ready.
  • Lower server_start_timeout for mixed-stress from 7200s to 600s (10m) — sufficient for an 8-GPU setup with a warm Triton cache.
  • Add logging to MpiSession.shutdown_abort and MpiPoolSession.shutdown to surface where MPI cleanup stalls after a worker crash.

Test plan

  • Validated on B200 8-GPU machine: with GPUs 4-7 occupied by another user, the gen worker OOMs during startup. The test now fails within ~40s with a clear RuntimeError: 1 worker(s) exited during startup: worker_gen_0.log (rc=1) instead of hanging indefinitely.
  • Previously the test would hang for up to 2 hours (7200s timeout) after the watchdog detected the crash, because the wait_for_disagg_server_ready coroutine was not cancelled.

Summary by CodeRabbit

  • Bug Fixes

    • Improved distributed session shutdown handling with clearer progress, timeout, and failure diagnostics.
    • Enhanced startup monitoring for disaggregated deployments to detect failed workers and stalled cluster initialization earlier.
    • Prevented worker configuration and log-file conflicts when launching multiple workers.
  • Tests

    • Improved cleanup of temporary test workspaces.
    • Added more reliable worker logs and startup timing information for diagnosing integration-test failures.
    • Reduced the default wait time for mixed-stress cluster startup checks.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

MPI shutdown paths now provide lifecycle logging. Disaggregated integration workers use indexed artifact names, startup monitoring detects exited workers and registration stalls, failure logs are retained, and mixed-stress startup uses a 600-second timeout.

Changes

MPI shutdown lifecycle

Layer / File(s) Summary
MPI shutdown logging
tensorrt_llm/llmapi/mpi_session.py
Shutdown and timeout paths now log worker waits, abort attempts, clean exits, and MPI pool completion states.

Disaggregated integration startup

Layer / File(s) Summary
Worker artifact disambiguation
tests/integration/defs/disaggregated/disagg_test_utils.py
Worker indexes are threaded through context and generation worker helpers and used in configuration and log filenames.
Cluster startup watchdog and cleanup
tests/integration/defs/disaggregated/test_disaggregated.py, tests/integration/defs/conftest.py
Cluster setup retains failure logs, reports worker log paths, monitors readiness and worker registration concurrently, and updates workspace cleanup.
Mixed-stress startup timing
tests/integration/defs/disaggregated/test_disaggregated.py
Mixed-stress setup propagates startup settings, reports setup duration, and uses a 600-second server startup timeout.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant setup_disagg_cluster
  participant wait_for_disagg_server_ready
  participant worker_processes
  participant cluster_info
  setup_disagg_cluster->>worker_processes: launch context and generation workers
  setup_disagg_cluster->>wait_for_disagg_server_ready: wait for server readiness
  setup_disagg_cluster->>worker_processes: check worker process exits
  setup_disagg_cluster->>cluster_info: poll registered workers
  wait_for_disagg_server_ready-->>setup_disagg_cluster: readiness result
  cluster_info-->>setup_disagg_cluster: registration status
  setup_disagg_cluster->>worker_processes: terminate on startup failure
Loading

Suggested reviewers: xinhe-nv

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is specific, concise, and matches the main change: fail-fast disaggregated startup on worker crashes/OOMs.
Description check ✅ Passed The description provides a clear summary and test plan that cover the main behavior and validation for the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/integration/defs/disaggregated/disagg_test_utils.py (1)

86-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Annotate the changed worker APIs and document the public wrapper arguments.

Add precise parameter and return types to all three signatures, plus Args/Returns sections for run_ctx_worker and run_gen_worker. As per coding guidelines, “Annotate every function” and “document public function arguments.”

Also applies to: 157-166, 184-193

🤖 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 `@tests/integration/defs/disaggregated/disagg_test_utils.py` around lines 86 -
95, Annotate all three worker APIs—_run_worker, run_ctx_worker, and
run_gen_worker—with precise parameter and return types, including optional and
callback/configuration values as appropriate. Add Args and Returns sections to
the docstrings of the public wrappers run_ctx_worker and run_gen_worker,
documenting every exposed argument and the returned value. Do not alter their
runtime behavior.

Source: Coding guidelines

🤖 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/llmapi/mpi_session.py`:
- Line 131: Update the shutdown logging statement in the MPI session flow to use
the f-string !r conversion flag for reason instead of calling repr(reason),
preserving the existing “Reason to shutdown” message.

In `@tests/integration/defs/disaggregated/test_disaggregated.py`:
- Around line 853-858: Update the pending-task cleanup loop to suppress only
asyncio.CancelledError when awaiting each cancelled task; remove the broad
Exception catch so any non-cancellation failure from pending tasks propagates
instead of being discarded.
- Around line 821-830: Update the worker-registration polling logic around
last_worker_count so the no-progress timeout is only evaluated while count is
below the expected worker total; once all expected workers register, stop or
bypass the timer during subsequent readiness waits. Add a mocked slow-readiness
regression in the disaggregated integration tests to verify complete
registration does not raise the no-progress RuntimeError even when final server
readiness exceeds _NO_PROGRESS_TIMEOUT.

---

Nitpick comments:
In `@tests/integration/defs/disaggregated/disagg_test_utils.py`:
- Around line 86-95: Annotate all three worker APIs—_run_worker, run_ctx_worker,
and run_gen_worker—with precise parameter and return types, including optional
and callback/configuration values as appropriate. Add Args and Returns sections
to the docstrings of the public wrappers run_ctx_worker and run_gen_worker,
documenting every exposed argument and the returned value. Do not alter their
runtime behavior.
🪄 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: 6a2fbde5-7f53-4409-9416-9a38e619b176

📥 Commits

Reviewing files that changed from the base of the PR and between 573bd5f and 51fd791.

📒 Files selected for processing (4)
  • tensorrt_llm/llmapi/mpi_session.py
  • tests/integration/defs/conftest.py
  • tests/integration/defs/disaggregated/disagg_test_utils.py
  • tests/integration/defs/disaggregated/test_disaggregated.py

Comment thread tensorrt_llm/llmapi/mpi_session.py Outdated
Comment thread tests/integration/defs/disaggregated/test_disaggregated.py Outdated
Comment thread tests/integration/defs/disaggregated/test_disaggregated.py
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59541 [ run ] triggered by Bot. Commit: 51fd791 Link to invocation

Comment thread tests/integration/defs/disaggregated/test_disaggregated.py Outdated
- Log work_dir path and worker log paths at launch so failures are easy
  to locate.

- Fix worker log/config filename collision when multiple workers share
  port=0: use worker_index instead of port in filenames.

- Detect worker crashes during startup via process.poll(); persist worker
  logs under cwd so they survive test teardown.

- Fix workspace cleanup on overlayfs: use ignore_errors=True on rmtree
  since overlayfs may reject directory removal even with correct perms.

- Fix swallowed crash exception: previously ticker.cancel() discarded any
  RuntimeError _tick() had raised; await ticker in a finally block so the
  exception propagates correctly.

- Fix watchdog exception silently leaving wait_for_disagg_server_ready
  polling for up to server_start_timeout: run both as asyncio tasks with
  asyncio.wait(FIRST_COMPLETED) so whichever finishes first cancels the
  other.

- Detect workers alive but hung (stuck in MPI cleanup after OOM) via
  /cluster_info: if no new workers register for 60s after the disagg
  server first becomes reachable, declare the cluster stuck.

- Log startup progress every 10s (elapsed, remaining, workers registered)
  and setup duration after cluster is ready.

- Lower server_start_timeout for mixed-stress from 7200s to 600s (10m).

- Add logging to MpiSession.shutdown_abort and MpiPoolSession.shutdown to
  surface where MPI cleanup stalls after a worker crash.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
…meout

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
@brnguyen2
brnguyen2 force-pushed the brnguyen/disagg-startup-fail-fast branch from 5030744 to e38a5c5 Compare July 16, 2026 00:14
@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@brnguyen2
brnguyen2 enabled auto-merge (squash) July 16, 2026 00:20
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59555 [ run ] triggered by Bot. Commit: e38a5c5 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59541 [ run ] completed with state ABORTED. Commit: 51fd791

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59555 [ run ] completed with state SUCCESS. Commit: e38a5c5
/LLM/main/L0_MergeRequest_PR pipeline #48001 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 tests/integration/defs/disaggregated/test_disaggregated.py Outdated
@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@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.

Question on the log-retention part — am I reading this right? The retained logs live under the llm_venv workspace, which the session fixture seems to rmtree at teardown, and IIUC the CI stage only uploads ${stageName}/ (the pytest --output-dir). If both hold, the full worker logs wouldn't reach the downloadable artifacts — is there an earlier collection step or another path I'm missing? If not (non-blocking, fine as a follow-up), copying them into --output-dir on failure might be all that's needed.

@BowenFu BowenFu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — disagg startup fail-fast on worker OOM via process.poll(); can't false-positive on long-lived servers, and the readiness poller is correctly cancelled/​re-raised on a crash.

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59707 [ run ] triggered by Bot. Commit: a8b7ddd Link to invocation

@brnguyen2

brnguyen2 commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

@nv-xtf Good catch. For the tests in this PR, save_log defaults to False, so worker stdout/stderr inherits fd 1/fd 2 from the parent process. In CI, the s3_output.py plugin redirects those fds via os.dup2() before each test, so subprocess output lands in the per-test stdout.log/stderr.log files under --output-dir and gets uploaded to Artifactory.

This iteration is mainly meant to make local testing easier. Explicitly copying logs into --output-dir on failure is a good follow-up once this lands.

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59707 [ run ] completed with state SUCCESS. Commit: a8b7ddd
/LLM/main/L0_MergeRequest_PR pipeline #48134 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

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59765 [ run ] triggered by Bot. Commit: a8b7ddd Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59765 [ run ] completed with state SUCCESS. Commit: a8b7ddd
/LLM/main/L0_MergeRequest_PR pipeline #48191 completed with status: 'SUCCESS'
Pipeline passed with automatic retried tests. Check the rerun report for details.

CI Report

Link to invocation

@brnguyen2
brnguyen2 merged commit b5321af into NVIDIA:main Jul 16, 2026
7 checks passed
lowsfer added a commit to lowsfer/TensorRT-LLM that referenced this pull request Jul 20, 2026
…l init

MpiPoolSession.shutdown() is reachable from __del__ (via shutdown_abort)
and may run on a session that never completed __init__ -- e.g. after
release_exit_joins() marks the pool dead, or in the released-session
path exercised by test_proxy_fast_death.py::
test_pool_session_shutdown_never_blocks_after_release, which builds the
object with __new__. shutdown() read self.n_workers (log line) and
self._wait_shutdown unguarded, raising AttributeError, while the
_pool_dead check right above already used getattr defensively.

Guard both accesses with getattr, matching the existing _pool_dead
style, so a released/partially-constructed session shuts down without
crashing. This unbreaks a cross-PR merge-skew regression on main: the
unguarded self.n_workers log came from NVIDIA#16456 while the __new__-based
test came from NVIDIA#16312; each was green alone but the combination fails
deterministically. Also add the missing D205 docstring blank lines the
ruff-legacy gate flagged on the touched file. Verified: the whole
test_proxy_fast_death.py file now passes (23 passed, was 1 failed).

Signed-off-by: Yao Yao <lowsfer@users.noreply.github.com>
lowsfer added a commit to lowsfer/TensorRT-LLM that referenced this pull request Jul 21, 2026
…l init

MpiPoolSession.shutdown() is reachable from __del__ (via shutdown_abort)
and may run on a session that never completed __init__ -- e.g. after
release_exit_joins() marks the pool dead, or in the released-session
path exercised by test_proxy_fast_death.py::
test_pool_session_shutdown_never_blocks_after_release, which builds the
object with __new__. shutdown() read self.n_workers (log line) and
self._wait_shutdown unguarded, raising AttributeError, while the
_pool_dead check right above already used getattr defensively.

Guard both accesses with getattr, matching the existing _pool_dead
style, so a released/partially-constructed session shuts down without
crashing. This unbreaks a cross-PR merge-skew regression on main: the
unguarded self.n_workers log came from NVIDIA#16456 while the __new__-based
test came from NVIDIA#16312; each was green alone but the combination fails
deterministically. Also add the missing D205 docstring blank lines the
ruff-legacy gate flagged on the touched file. Verified: the whole
test_proxy_fast_death.py file now passes (23 passed, was 1 failed).

Signed-off-by: Yao Yao <lowsfer@users.noreply.github.com>
lowsfer added a commit to lowsfer/TensorRT-LLM that referenced this pull request Jul 21, 2026
…l init

MpiPoolSession.shutdown() is reachable from __del__ (via shutdown_abort)
and may run on a session that never completed __init__ -- e.g. after
release_exit_joins() marks the pool dead, or in the released-session
path exercised by test_proxy_fast_death.py::
test_pool_session_shutdown_never_blocks_after_release, which builds the
object with __new__. shutdown() read self.n_workers (log line) and
self._wait_shutdown unguarded, raising AttributeError, while the
_pool_dead check right above already used getattr defensively.

Guard both accesses with getattr, matching the existing _pool_dead
style, so a released/partially-constructed session shuts down without
crashing. This unbreaks a cross-PR merge-skew regression on main: the
unguarded self.n_workers log came from NVIDIA#16456 while the __new__-based
test came from NVIDIA#16312; each was green alone but the combination fails
deterministically. Also add the missing D205 docstring blank lines the
ruff-legacy gate flagged on the touched file. Verified: the whole
test_proxy_fast_death.py file now passes (23 passed, was 1 failed).

Signed-off-by: Yao Yao <lowsfer@users.noreply.github.com>
JunyiXu-nv added a commit to JunyiXu-nv/TensorRT-LLM that referenced this pull request Jul 22, 2026
…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 added a commit to JunyiXu-nv/TensorRT-LLM that referenced this pull request Jul 22, 2026
…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>
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.

10 participants