Skip to content

fix(aorta): bound setup and run by timeout_seconds on daemon threads - #330

Open
speriaswamy-amd wants to merge 1 commit into
surya/aorta-mn-04-torchrunfrom
surya/aorta-mn-05-timeouts
Open

fix(aorta): bound setup and run by timeout_seconds on daemon threads#330
speriaswamy-amd wants to merge 1 commit into
surya/aorta-mn-04-torchrunfrom
surya/aorta-mn-05-timeouts

Conversation

@speriaswamy-amd

Copy link
Copy Markdown
Contributor

Stack 5/6 — splits #171. Base: #329.

Why

Neither setup() nor run() put a deadline on their parallel per-node work, so a single stalled node hung the entire CVS invocation with no result. Hit live: an NCCL collective stalled on one node of a 2-node run and the process sat there indefinitely instead of reporting TIMEOUT.

ThreadPoolExecutor cannot fix this on its own — its workers are non-daemon, and CPython's atexit hook joins every one of them at interpreter shutdown regardless of shutdown(wait=False). A node stuck in a blocking Docker or SSH call would still wedge the process on the way out.

What changed

  • _run_bounded_parallel() — runs each task on a daemon thread against a shared deadline, returning per-key results / errors / timeouts. Abandoned threads cannot block exit. Replaces ThreadPoolExecutor in both setup() and run().
  • _setup_single_node() takes a cancel Event: if it finishes launching its container after setup() has given up, it tears that container down itself. Otherwise it would register into self._containers after teardown()'s one-time snapshot had already run, orphaning the container on the node. (Builds on fix(runner): tear down resources after a partially-successful setup #326.)

Test

ruff clean. Unit tests 623 → 631 (8 new, including a case asserting the worker thread is actually a daemon, and end-to-end setup()/run() timeout cases that assert the call returns promptly).

Neither setup() nor run() put a deadline on their parallel per-node work, so a
single stalled node hung the entire CVS invocation with no result. This was hit
live: an NCCL collective stalled on one node of a 2-node run and the process sat
there indefinitely instead of reporting TIMEOUT.

ThreadPoolExecutor cannot fix this on its own. Its workers are non-daemon and
CPython's atexit hook joins every one of them at interpreter shutdown regardless
of shutdown(wait=False), so a node stuck in a blocking Docker or SSH call would
still wedge the process on the way out. _run_bounded_parallel() runs each task on
a daemon thread against a shared deadline and reports per-key results, errors, and
timeouts; abandoned threads cannot block exit.

Also closes a setup/teardown race that the new deadline makes reachable:
_setup_single_node() takes a cancel Event and, if it finishes launching its
container after setup() has given up, tears that container down itself. Otherwise
it would register into self._containers after teardown()'s one-time snapshot had
already run, orphaning the container on the node.

Co-Authored-By: Claude <noreply@anthropic.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.

1 participant