Skip to content

[https://nvbugs/6561778][fix] Fence all ranks before pytest launch in multi-node slurm_run.sh - #17372

Open
trtllm-agent wants to merge 1 commit into
NVIDIA:mainfrom
tensorrt-cicd:repair-bot-bug6561778
Open

[https://nvbugs/6561778][fix] Fence all ranks before pytest launch in multi-node slurm_run.sh#17372
trtllm-agent wants to merge 1 commit into
NVIDIA:mainfrom
tensorrt-cicd:repair-bot-bug6561778

Conversation

@trtllm-agent

@trtllm-agent trtllm-agent commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Root cause: On multi-node GB200 CI runs, jenkins/scripts/slurm_run.sh has no cross-rank synchronization between slurm_install_setup and eval $pytestCommand — the install lock in slurm_install.sh lives under $resourcePathNode (/tmp), so it is node-local and can only fence SLURM_LOCALID peers on the same host, never another node. Per-rank work amplifies the skew (non-zero ranks cover the coverage-config write with a blind sleep 30, and slurm_setup_runtime_env shells out to pip3), so one node reaches pytest while another is still installing. Pytest's first action is import tensorrt_llm, whose module-scope MPI collective runs under --mpi=pmix (added exactly when nodeCount > 1) with a 300s ring fence timeout, so the skew aborts every rank instead of merely running late — the same defect already solved for sibling bug 6541343.
  • Fix: Added a slurm_wait_all_ranks barrier on the shared $jobWorkspace immediately before eval $pytestCommand: each rank touches a marker under a directory keyed by both SLURM_JOB_ID and SLURM_STEP_ID (so markers from another job or an earlier step of the same job cannot satisfy the count) and polls a glob count every 10s until all SLURM_NTASKS ranks are present. The wait is bounded at 3600s — above the 2700s pip3 retry budget in slurm_install.sh so a merely slow rank still releases the barrier, while a dead rank fails the stage loudly instead of hanging to the partition walltime — and it no-ops for single-rank or unset-jobWorkspace runs. The function was validated separately (a warm container never executes slurm_run.sh, so the pytest verify cannot exercise it): the function was extracted verbatim (md5-matched) and all 8 cases were run live on 2 nodes, confirming cross-host blocking at the same step, one-poll release for concurrent ranks, correct no-ops, rc=1 at exactly the timeout for a missing rank, and correct rejection of stale earlier-step and other-job markers. The waiver for TestDeepSeekR1::test_nvfp4_multi_gpus[latency] is removed accordingly.
  • Original test: pytest tests/integration/defs/accuracy/test_llm_api_pytorch.py::TestDeepSeekR1::test_nvfp4_multi_gpus[latency] -v
  • Automated fix generated by repair-bot

Test plan

  • Verify fix on the same GPU type as the original failure
  • Check for regressions in related tests

Links

Dev Engineer Review

  • Added a shared jobWorkspace barrier before pytest execution.
  • Each rank writes a job- and step-specific readiness marker.
  • Ranks wait for all SLURM_NTASKS markers.
  • The barrier polls every 10 seconds and times out after 3600 seconds.
  • Single-rank runs and runs without jobWorkspace skip the barrier.
  • This fixes the node-local install-lock race before pytest MPI initialization.
  • Removed the obsolete waiver for accuracy/test_llm_api_pytorch.py::TestDeepSeekR1::test_nvfp4_multi_gpus[latency].
  • Barrier validation covered synchronization, no-op, timeout, and stale-marker cases.
  • No public or exported declarations changed.

QA Engineer Review

  • No test code changed.
  • Removed one entry from tests/integration/test_lists/waives.txt.
  • No test-db/ or qa/ files were modified.
  • The removed waiver has a valid test path and test identifier.
  • Verdict: needs follow-up because CBTS coverage data is unavailable.

The only install lock (slurm_install.sh) lives under $resourcePathNode, which
is /tmp -- node-local, and a per-step tmpfs under pyxis -- so its wait loop
fences just the $SLURM_LOCALID peers of one node and a node can never observe
another node's lock. Nothing then stops slurm_run.sh from reaching
`eval $pytestCommand` on one node while another is still installing, and the
per-rank work in between skews the ranks further: non-zero ranks cover rank 0's
coverage-config write with a blind `sleep 30`, and slurm_setup_runtime_env
shells out to pip3.

Pytest's first action is `import tensorrt_llm`, whose module-scope MPI
collective must be entered by every rank. Under --mpi=pmix, which is added
exactly when nodeCount > 1, that collective has a 300s fence timeout, so a node
whose pip3 install stalls (up to the 2700s retry budget) makes the collective
abort every rank rather than merely run late. The ranks die between pytest
setup and teardown, which leaves the nodeid in unfinished_test.txt and makes
generate_timeout_xml.py synthesize the "Test terminated unexpectedly" this bug
reports -- there is no traceback for the test body. PMIX_MCA_gds=hash does not
mitigate this: a fence that times out never exchanges the modex regardless of
GDS mode, and the pml_ucx errors seen alongside it are downstream of that same
missing exchange.

Add a marker barrier on the shared $jobWorkspace immediately before
`eval $pytestCommand`. It counts SLURM_NTASKS rank markers rather than nodes,
so the fenced set is exactly the set that enters the aborting collective, and
placing it after the block that wipes SLURM_* keeps it a no-op for single-node
and disaggregated benchmark/server runs, which reach it with SLURM_NTASKS
unset. The marker directory is keyed per job and per step because $jobWorkspace
outlives a step, so a later step must not be released by an earlier one's
markers. The wait is bounded above the 2700s pip3 budget so a genuinely dead
rank fails the stage with a clear message instead of hanging until the
partition walltime.

The barrier is byte-identical to the one reviewed on the sibling attribution of
this same defect (6541343), so whichever lands first collapses the other's hunk
on rebase. Also un-waive this bug's test, whose body was already healthy: the
reproduce log passes all three accuracy phases (MMLU 87.013 vs 85.513, GSM8K
95.375 vs 92.217, CnnDailymail rouge1 30.271 vs 26.716) and fails only on an
fsspecIO thread leaked by HuggingFace's downloader, an environment artifact
unrelated to the launch defect.

Signed-off-by: trtllm-agent <296075020+trtllm-agent@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 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: 43887b69-f081-4f92-8566-1715939205b9

📥 Commits

Reviewing files that changed from the base of the PR and between f12c5e5 and 85754cf.

📒 Files selected for processing (2)
  • jenkins/scripts/slurm_run.sh
  • tests/integration/test_lists/waives.txt
💤 Files with no reviewable changes (1)
  • tests/integration/test_lists/waives.txt

Walkthrough

The Slurm launcher now waits for all multi-rank processes before pytest. The change also removes the waiver for the DeepSeekR1 NVFP4 multi-GPU latency test.

Changes

Slurm test execution

Layer / File(s) Summary
Slurm rank readiness barrier
jenkins/scripts/slurm_run.sh
Adds a shared-filesystem barrier with per-rank markers, progress logging, single-rank bypasses, workspace checks, and a 3600-second timeout before pytest.
Integration test waiver removal
tests/integration/test_lists/waives.txt
Removes the skip waiver for TestDeepSeekR1::test_nvfp4_multi_gpus[latency].

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

Sequence Diagram(s)

sequenceDiagram
  participant SlurmRank
  participant SharedFilesystem
  participant Pytest
  SlurmRank->>SharedFilesystem: Create job and step readiness marker
  SlurmRank->>SharedFilesystem: Wait for all rank markers
  SharedFilesystem-->>SlurmRank: Report ready-rank count
  SlurmRank->>Pytest: Start pytest after barrier completion
Loading

Possibly related PRs

Suggested reviewers: brnguyen2

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the NVBugs fix and the main change: fencing all ranks before pytest in multi-node Slurm runs.
Description check ✅ Passed The description explains the root cause, fix, affected test, validation results, and links, although it does not include the template checklist.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 brnguyen2 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.

The mechanism checks out: slurm_install.sh's lock lives under $resourcePathNode (node-local), the aggregated path launches one srun with --mpi=pmix for multi-node, and $jobWorkspace (/home/svc_tensorrt/bloom/scripts/...) is in getMountListForSlurmTest, so it is visible to every rank — the barrier is placed and keyed correctly (job + step). Timeout failure propagates through the ERR trap into --kill-on-bad-exit=1, which is the right blast radius.

Two things before merge:

  1. The PR states the fence can't be exercised by the reproduction environment (a warm container never runs slurm_run.sh), so nothing has run the changed code on the path it's meant to fix. Since the waiver comes out in the same PR, please run the actual 2-node post-merge stage — /bot run --extra-stage "GB200-8_GPUs-2_Nodes-PyTorch-Post-Merge-1" — and link the result.

  2. waives.txt still carries neighbouring multi-node DeepSeekR1 entries (6561775, 6561777). If this is an infra-level fence fix, are those expected to stay waived, or is the scope deliberately one test?

Also: the 11-line preamble at slurm_run.sh:75 is a post-mortem, not a code comment. 3–4 lines plus the bug ID would age better.

"all $numRanks ranks to be ready; ready: $ready/$numRanks"
return 1
fi
# One rank reports progress; all of them would spam the log every 10s.

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.

This guard doesn't do what the comment says. The script runs under set -xEeuo pipefail (line 4) and never disables tracing before here, so every rank already emits the full trace of the loop body — the glob assignment, both [ tests, the sleep — to the single #SBATCH --output= log every 10s. Suppressing the echo on non-zero ranks removes one line out of ~6 per rank per iteration.

Either wrap the loop in set +x / set -x (and keep the rank-0 echo as the only progress signal), or drop the guard and the comment.

mkdir -p "$readyDir"
touch "$readyDir/rank_${SLURM_PROCID}.ready"

# Bounded so a dead rank fails the stage loudly instead of hanging until the

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.

The stated justification for 3600s doesn't hold. slurm_install.sh has several retry_command --timeout 2700 calls plus a --timeout 1800 wget, so its worst-case budget is well over an hour, and the non-LOCALID-0 branch waits on the lock with no timeout at all. "Exceeds the 2700s pip3 retry budget" is a per-command bound, not a bound on install duration.

What 3600s actually bounds is arrival skew — each rank starts its own deadline after finishing its own install — which is the right thing to bound and comfortably above the ~10min skew in the bug. Worth saying that instead, since the current wording invites someone to "fix" the number against the wrong quantity later.

@@ -21,7 +21,6 @@ accuracy/test_llm_api_autodeploy.py::TestQwen3_5_397B_MoE::test_bf16_small[4] SK
accuracy/test_llm_api_pytorch.py::TestDeepSeekR1::test_fp8_blockscale[throughput] SKIP (https://nvbugs/6561775)

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.

This removal is the only user-visible part of the PR and it is the least-verified. The PR body says the fence "can't be exercised by the pytest verify run at all (a warm container never executes slurm_run.sh)" — so the code path being fixed was never executed end-to-end, only an extracted copy of the helper was.

Please post /bot run --extra-stage "GB200-8_GPUs-2_Nodes-PyTorch-Post-Merge-1" and link the green run before merging; otherwise this re-enables a test in post-merge on a mechanism argument alone.

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.

3 participants