fix(test): reliable nightly GPU runs — phased execution as default, script-owned timings - #1643
Open
planetf1 wants to merge 8 commits into
Open
fix(test): reliable nightly GPU runs — phased execution as default, script-owned timings#1643planetf1 wants to merge 8 commits into
planetf1 wants to merge 8 commits into
Conversation
ollama --version prints a multi-line warning block to stdout (e.g.
"Warning: client version is 0.32.2"). The probe's awk '{print $NF}'
therefore produced the two-line string "instance\n0.32.2", which never
passed the sort -V -C comparison, so every run re-downloaded and
re-extracted the 1.9 GB Ollama tarball even when the right version was
installed — and concurrent runs raced the re-extraction (observed:
"tar: File exists" killing one of two parallel nightly jobs).
Extract the first dotted version number instead.
Assisted-by: opencode
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
vLLM 0.29 startup (package import, weight load, torch.compile, CUDA graph capture, API server startup) measured 54-181 s on H100s depending on compile-cache warmth and whether instances start concurrently — the script's hardcoded 120 s readiness loop killed healthy runs that needed 143-181 s. Add VLLM_READY_TIMEOUT (default 120 s) and use it in the readiness loop and the failure message. Assisted-by: opencode Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Stopping the 'ollama serve' parent leaves its llama-server worker children alive; they keep holding GPU memory (observed 5.4 GiB still resident 120 s after shutdown in a phased nightly run, caught by the post-phase GPU release check). pkill the workers scoped to this install's runtime directory — other jobs' workers carry their own runtime path in their command line and are not affected. Assisted-by: opencode Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
The warmup list included two models no test in test/ requests (examples only, ~6.7 GiB of warmup residency per nightly) and omitted llama3.2:1b, which the SOFAI e2e tests do request — the server downloaded it on demand mid-test, adding a hidden network dependency. Replace the two examples-only entries with llama3.2:1b and document the sync obligation. Assisted-by: opencode Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
SERIAL_PHASES (default 1) runs each backend group as its own pytest process with per-phase server lifecycles: only one CUDA context is alive at a time, peak GPU memory drops from ~72 GiB to ~33 GiB, in-process HF models are released between phases, and the single-context phases (hf/vllm/base) can run on an exclusive LSF GPU (-gpu "num=1"). - PHASES=hf,ollama,vllm,base selects which phases run, so one script can drive the recommended two-job split (exclusive for single-context phases; mode=shared for the Ollama phase, whose per-model llama-server workers need multi-process GPU access). - wait_gpu_free() release gate between phases and after each server stop (max_wait 120 s, poll 10 s, threshold 2 GiB). - The script owns the diagnostic report contract: full per-test durations and JSON reports under the log directory in both modes (pytest_full.log + pytest_report.json single-run; phase_<name>.log + pytest_report_<name>.json per phase). Callers pass selection/verbosity args only; a caller -m falls back to single-run mode with a warning. - SERIAL_PHASES=0 keeps the legacy single-process --group-by-backend run. Document the LSF submission patterns in the script header and update test/README.md (CI tier table, nightly description, local-run example, group warm-up note). Assisted-by: opencode Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
- stop_vllm only removes a vLLM venv this invocation created (VLLM_VENV_OWNED); the recommended two-job split no longer lets the ollama-only job delete the venv its sibling runs vLLM from, and VLLM_EXTERNAL runs no longer lose a venv the script never built. - The Ollama install block runs only when this run will use Ollama (single-run, or phased with the ollama phase enabled), so the exclusive split's other job skips a 1.9 GB download and the two jobs no longer race the install path on a fresh node. - PHASES tokens are validated (typo now dies loudly instead of silently running zero phases with exit 0); phased runs also fail if no phase actually executed. - run_phase treats pytest exit 5 (nothing selected in this phase) as a pass, so documented -k selections no longer fail the run. - wait_gpu_free survives a transient nvidia-smi failure (its own contract is diagnostic, not fatal) and its timeout wording no longer calls co-tenant memory a leak. - Phased pytest runs now pass --group-by-backend, so the conftest group warm-up/eviction (keep_alive=-1 pinning) actually fires in the default mode; each run also starts from a clean coverage file. - A vLLM start failure now records the phase as failed and continues with the base phase instead of aborting the whole run. - vLLM readiness default raised 120s -> 300s (observed cold starts hit 181s; the loop breaks on readiness so a high default is free). - The -m guard also catches the attached -mVALUE form; caller --durations-min no longer suppresses the built-in durations list; removed the --no-json-report arm (not a real pytest option). - Signal kills (bkill, wall-time) now route through the EXIT trap so servers are torn down; trap/INT/TERM added. - Fixed the stale Ollama readiness message (120s, not 30s) and the orphan-pkill scoping comment (per OLLAMA_BIN, not per job). - Header docs: per-job MELLEA_LOGDIR/COVERAGE_FILE in the two-job example, gpu_release_gate.log in the report inventory, node-id wording removed; README warm-up note corrected. Assisted-by: opencode Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
…t-file In phased mode, a caller-supplied --json-report-file was forwarded verbatim into every phase's pytest invocation, so each phase overwrote it and only the last phase's results survived. Strip the caller's --json-report-file out of per-phase args and merge the phases' own pytest_report_<name>.json files into the caller's requested path once all phases finish. Assisted-by: Claude Code Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
ajbozarth
requested changes
Sep 10, 2026
ajbozarth
left a comment
Contributor
There was a problem hiding this comment.
Some feedback from Claude. Solid PR — verified the phase marker partition holds (clean split, no double-runs, addopts only carries -m "not slow" so re-stating it per phase is correct, and the server-dependent tests land in the right phase). Three findings below; only the first is worth acting on.
- Make the per-phase JSON merge non-fatal: wrap the uv run call in set +e/set -e so a truncated or malformed pytest_report_p*.json (e.g. a phase OOM-killed mid-write) can't abort the script under set -e before EXIT_CODE is recorded, which would surface a merge traceback instead of the real per-phase pass/fail. - Skip an individual unreadable report with a warning instead of failing the whole merge, so the other phases' results still make it through. - Truncate pytest_full.log once before phase 1 so a reused MELLEA_LOGDIR doesn't mix this run's phased output with a previous run's. - Name WITH_VLLM in the "no phases executed" die message, since a WITH_VLLM=0 vllm-only PHASES selection is the likely cause. Addresses review from ajbozarth on PR #1643. Assisted-by: Claude Code Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
ajbozarth
approved these changes
Sep 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Issue
Fixes #1642
Description
Summary. The nightly GPU run was unreliable on the shared H100 pool — five script bugs, and a single pytest process keeping Ollama + vLLM up at the same time (~72 GiB peak, at the mercy of co-tenants: 15 OOM failures observed from one neighbour). This PR fixes the bugs and makes phased execution the default: one pytest process per backend group, one CUDA context at a time, ~33 GiB peak, and the
hf/vllm/basephases can run on an exclusive GPU. Every run now records per-test durations and JSON reports without callers passing any flags. Both modes verified green: 4309/0.What changes for nightly callers
SERIAL_PHASES=1). Legacy single-process run:SERIAL_PHASES=0 --group-by-backend.pytest_report_<name>.json) +phase_<name>.log+ consolidatedpytest_full.log. Consumers of the old singlepytest_report.jsonmust be updated (single-run mode still writes one).--durations*/--json-report*).-mfalls back to single-run with a loud warning (per-phase selection would otherwise apply the same-mfour times).test/README.md):PHASES=hf,vllm,baseon-gpu "num=1"(exclusive) +PHASES=ollamaonmode=shared:gmem=20G(Ollama's per-model workers can't run exclusive). Concurrent jobs need distinctMELLEA_LOGDIRandCOVERAGE_FILE. A singlemode=sharedjob still works.SERIAL_PHASES,PHASES,VLLM_READY_TIMEOUT(default 300 s),GPU_FREE_TIMEOUT,GPU_FREE_THRESHOLD_MB.Observed issues (H100, 2026-09-09, pre-fix)
tar: File existskilled one of two).ollama serveshutdown leftllama-serverworkers holding 5.4 GiB 120 s later.llama3.2:1b(SOFAI e2e) → on-demand mid-test download.Changes made (one commit each)
--versionoutput.VLLM_READY_TIMEOUT(default 300 s) + fast-fail when the server process dies during startup, pointing atvllm.log.wait_gpu_free()release gate,PHASES=, baked-in reporting,-mguard, docs.PHASEStypos and zero-phase runs now fail loudly; pytest exit 5 (nothing selected in a phase) no longer fails the run; phased runs pass--group-by-backendso the conftest group warm-up fires; a vLLM start failure fails only the vllm phase, not the base phase; signal kills route through the shutdown trap.Test timing (H100-80, full suite,
--durations=0)14 m 29 s wall (4308 passed). Groups: huggingface 412.5 s (47%) · ollama 247.9 s (29%) · base 136 s (16%) · openai_vllm 4.1 s · server start/stop ~40 s. Slowest:
test_think_big28.3 s;test_intrinsics_formatters.py151.6 s (15 fresh model loads);test_groundedness_*79.8 s;test_component_typing::test_generating23.5 s clean, 179 s under co-tenant pressure. Phased mode: +17–23% wall for the 4-way split. DGX Spark (128 GB unified): ~1.5–2.5 h expected; useVLLM_GPU_MEM=0.2–0.25(0.4 would reserve 51 GiB) — fuller discussion to follow.Verification (final runs, LSF H100-80)
PHASEStypo → loud failure; empty phase selection (exit 5) → treated as pass; aWITH_VLLM=1job without the vllm phase does not delete the shared venv; release gate non-fatal with co-tenant residentFollow-ups (out of scope)
curlhas no--max-time(pre-existing: hangs on nodes without egress).WITH_EXAMPLES=1runs pull examples-only models on demand (not in the warmup list by design).WITH_VLLMauto-detect fires on GPU-less nodes (nvidia-smi -Lexits 0 without devices); now degrades gracefully, detection could be tightened.Testing
Attribution
Adding a new component, requirement, sampling strategy, or tool?
If your PR adds or modifies one of the types below, check the matching box. A checklist of type-specific review items will be posted as a comment.
NOTE: Please ensure you have an issue that has been acknowledged by a core contributor and routed you to open a pull request against this repository. Otherwise, please open an issue before continuing with this pull request.