Conversation
…put) With print_console=False, exec/exec_cmd_list now print NOTHING: gate both the command echo AND the per-host "Host == <ip> ==" banner behind print_console in MultiProcessParallelHandle and the inner ParallelHandle. Previously the banner was logged unconditionally, so bulk/polling reads still spammed host headers. Callers (e.g. a poll loop) decide what to surface. Adds tests. Signed-off-by: Saravanan Solaiyappan <saravanan.solaiyappan@amd.com>
…nsoleSpinner(#446) poll_for_completion now delegates to cvs/lib/utils/LogPoller (behavior identical): completion when every node logs "completed step: <steps-1>", the config error_patterns + always-on fatal signatures + NaN/Inf scanned per chunk, node 0 streamed, and a spinner between 10s drains. Removed the in-suite _TrainingSpinner / is_complete / _scan_chunk_for_errors / _drain_new_log_lines and the per-node cursor (now in LogPoller), plus the unused sys import. Tests keep jaxmaxtext's error-signature coverage by exercising its patterns through a LogPoller wired like production, plus poll_for_completion delegation tests. Signed-off-by: Saravanan Solaiyappan <saravanan.solaiyappan@amd.com>
ddb1416 to
6673f04
Compare
…nsoleSpinner(#446) poll_for_completion now delegates to cvs/lib/utils/LogPoller (behavior identical): completion when every node logs "completed step: <steps-1>", the config error_patterns + always-on fatal signatures + NaN/Inf scanned per chunk, node 0 streamed, and a spinner between 10s drains. Removed the in-suite _TrainingSpinner / is_complete / _scan_chunk_for_errors / _drain_new_log_lines and the per-node cursor (now in LogPoller), plus the unused sys import. Tests keep jaxmaxtext's error-signature coverage by exercising its patterns through a LogPoller wired like production, plus poll_for_completion delegation tests. Signed-off-by: Saravanan Solaiyappan <saravanan.solaiyappan@amd.com>
6673f04 to
c18bdd1
Compare
Framework-agnostic log-tailing poll loop and console spinner under
cvs/lib/utils/, usable by any suite (training, inference, benchmarks).
- ConsoleSpinner: one-line in-place busy indicator on the controlling terminal
(/dev/tty) -- shown even under pytest fd capture, never written to --log-file
-- with a periodic logger heartbeat off a TTY.
- LogPoller: orchestrator-driven loop. Drains new per-node log lines every
drain_interval_s (tail from a per-node cursor), streams the followed node,
scans each new chunk for error signatures (nan_pattern -> always_on_patterns
-> error_patterns; case-insensitive) raising on the first hit, detects
completion via grep of complete_pattern with an all|any|node0 policy, and
animates a ConsoleSpinner between drains. Pattern-driven ("batteries
included") but overridable via is_complete / scan_chunk callables. Requires
only orch.hosts and orch.exec_cmd_list(print_console=False).
- README documents how other frameworks adopt it; unit tests for both.
Signed-off-by: Saravanan Solaiyappan <saravanan.solaiyappan@amd.com>
…nsoleSpinner(#446) poll_for_completion now delegates to cvs/lib/utils/LogPoller (behavior identical): completion when every node logs "completed step: <steps-1>", the config error_patterns + always-on fatal signatures + NaN/Inf scanned per chunk, node 0 streamed, and a spinner between 10s drains. Removed the in-suite _TrainingSpinner / is_complete / _scan_chunk_for_errors / _drain_new_log_lines and the per-node cursor (now in LogPoller), plus the unused sys import. Tests keep jaxmaxtext's error-signature coverage by exercising its patterns through a LogPoller wired like production, plus poll_for_completion delegation tests. Signed-off-by: Saravanan Solaiyappan <saravanan.solaiyappan@amd.com>
c18bdd1 to
010df54
Compare
|
has the baremetal path (as well as the container path) been tested? if so, can you link a ticket with the runs |
atnair-amd
left a comment
There was a problem hiding this comment.
design: The extraction is useful for JAX MaxText, but the current API generalizes JAX's specific topology rather than the broader CVS workload model.
LogPoller assumes:
log_paths[i]belongs toorch.hosts[i];- every host has exactly one log;
- completion is a regex found in those logs; and
- the orchestrator supports
exec_cmd_list.
Those assumptions fit JAX, where every node writes one training.log and every node must emit the final-step marker. Other suites have materially different layouts:
- vLLM: server logs exist per rank, but the benchmark client log exists only on the head. Using one path per
orch.hostswould either poll nonexistent client logs on workers or execute redundant reads of the head log. - SGLang disaggregated: hosts have roles such as prefill, decode, router, and benchmark. A host may own multiple logs, while a benchmark-only host has no server log. Readiness also requires specific role coordinators—not simply
all,any, ornode0. - Megatron/Primus: the last node is currently authoritative.
node0is wrong,anycould accept the wrong node, andallchanges existing behavior. - ATOM: native server readiness comes from an HTTP
/healthprobe, while benchmark completion may come from a result artifact. A log regex is not the authoritative completion signal. - xDiT: execution is foreground through Pssh/LocalPssh and completion is the command's exit status. There may be no remote log file to poll.
- Baremetal:
LogPollerdocuments baremetal support, butBaremetalOrchestratordoes not implement the requiredexec_cmd_listmethod.
There are also reliability problems in the generic contract:
- Partial-line loss: if the poller reads
Traceback (most recentbefore the writer finishes the line,splitlines()advances the cursor. Whencall last)is appended, the nexttail -n +Nskips the completed line and the traceback pattern is never detected. - Transport failures advance the file cursor: the parallel layer can return timeout or connection-exception text as ordinary output. The poller counts those lines even though they did not come from the file, potentially skipping real lines on the next successful poll.
- The timeout is not bounded:
poll()checks its deadline, but theexec_cmd_listcalls receive no timeout. A hung SSH operation can therefore block beyondtimeout_s. - Stale completion markers: completion uses
grepover the entire file. Suites that append to existing logs, such as Primus, can immediately match a marker from a previous run. - Late failures: observing a completion marker does not prove the process exited successfully. A shutdown traceback written after the final drain can be missed.
I think there are three reasonable scopes for this PR:
- Keep the poller private to JAX MaxText, extracting it only to simplify that suite without presenting its topology as a general CVS contract.
- Keep it shared but describe it narrowly as a per-node container-log poller for homogeneous workloads, without claiming general baremetal/training/inference support.
- Make it genuinely reusable by introducing explicit targets—host, path, role, label, and completion group—plus structured execution results, bounded command timeouts, byte-safe cursors, and pluggable completion probes for log markers, process exit, HTTP health, and result artifacts.
The pssh quiet-mode fix remains useful independently; this concern is about the public LogPoller contract and its claim of suite-wide generality.
splitlines() counts a partial last line and splits on CR, so the next tail -n +K skipped completed lines. Advance only on newline-terminated records and re-read a trailing fragment on the following drain. Co-authored-by: Cursor <cursoragent@cursor.com>
amd-droy
left a comment
There was a problem hiding this comment.
lgtm. thanks Saravanan.
… poll commands Address review feedback that the LogPoller contract over-claimed CVS-wide generality. Document the single-topology model it actually implements (one container log per host, exec_cmd_list drain, marker/callable completion), its non-goals (multi-log/role topologies, head-only client logs, foreground/no-log workloads), and the two caller preconditions it cannot enforce (fresh per-run logs; completion marker != clean exit). Correct the false claim that the baremetal orchestrator satisfies the contract -- it does not implement exec_cmd_list, so only the container orchestrator is supported today. Also bound each drain/completion read with cmd_timeout_s (default 60s, passed to exec_cmd_list) so a hung SSH/tail cannot block past the overall timeout_s. The jaxmaxtext caller spells the knob out. Tests + README updated. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks @atnair-amd — this is a fair critique, and you're right that the original README over-claimed a CVS-wide contract that the implementation doesn't actually honor. Rather than redesign the API in this PR (your option 3), we've taken option 2: keep Scope, documented explicitly (README):
Baremetal (also answering your testing question): you're correct — Reliability items:
The pssh quiet-mode fix stands on its own as you noted. If you'd still prefer option 1 (keep it private under Follow up: We will discuss with other framework owners and decide whether to improve on top this implementation to make it generic OR keep it training specific. |
|
lgtm Saravanan, thanks! |
Summary
Extracts the JAX MaxText per-node log-polling loop into a reusable, framework-agnostic CVS library (
cvs/lib/utils/) so any suite (training, inference, benchmarks) can tail per-node logs, stream/scan output, detect completion, and keep the console alive — without re-implementing the loop. jaxmaxtext is then re-pointed at the shared utility.Motivation
Every suite that launches a long-running containerized workload and tails per-node logs needs the same machinery: drain new lines every few seconds, surface them, watch for failure signatures, detect completion, and animate a spinner in between. This logic previously lived inside
jaxmaxtext_training_lib.py. Pulling it out removes ~135 net lines from that suite and makes the loop testable and reusable.What's in this PR (3 commits)
1.
fix(parallel): silent mode for pssh execMakes
print_console=Falseactually silence all orchestrator chatter, not just bulk output:phandle._process_outputnow gates the#---#/Host == <ip> ==banners behindprint_console.multiprocess_phandlelogs the command echo (cmd = ...) atdebuginstead ofinfowhenprint_console=False.2.
feat(utils): reusable LogPoller + ConsoleSpinnerNew shared utilities under
cvs/lib/utils/with tests and a README:LogPoller— orchestrator-driven poll loop: per-nodetailwith a cursor (each line reaches the console/log exactly once), one followedstream_node, ordered case-insensitiveerror_patternsscanning, completion viacomplete_pattern+complete_policy(all/any/node0) or anis_completecallable, and ascan_chunkcallable escape hatch. Raises on error signature or timeout; returns on completion.ConsoleSpinner— one-line in-place busy indicator on/dev/tty(visible even under pytest's fd-level capture, never written to--log-file); falls back to a periodic heartbeat off-TTY (CI/nohup).New
LogPolleroptions included here:ignore_error_patterns={name: regex}(defaultNone) — an error match whose offending line also matches one of these is treated as benign and skipped. Per-line matching means a benign line cannot mask a real error elsewhere in the same chunk.silent_poll=True(default) — keeps the per-nodetail/grepcommands off the console; passsilent_poll=Falseto echo them (print_console=True) when debugging the poll loop itself.3.
refactor(jaxmaxtext): drive the training poll loop via LogPollerDeletes the bespoke spinner/poll/scan/drain code from
jaxmaxtext_training_lib.pyand delegates toLogPoller, merging NaN/Inf + always-on fatal + config-driven signatures into a single orderederror_patternsdict. Tests updated accordingly (spinner/drain/is_complete tests moved to the utils suite).Testing
cvs/lib/utils/unittests/— 22 LogPoller + 5 ConsoleSpinner tests.make fmt-check,make lint(ruff + pylint E1205/E1206), and the affected unit suites all pass.Backward compatibility
No behavior change for existing callers: jaxmaxtext keeps the defaults (
ignore_error_patterns=None,silent_poll=True), which reproduce the prior quiet, first-match-wins behavior.