Skip to content

Exit air run --watch when the streamed node succeeds - #7031

Open
stevenchen-db wants to merge 4 commits into
mainfrom
schen/air-watch-exit-on-completion
Open

stevenchen-db wants to merge 4 commits into
mainfrom
schen/air-watch-exit-on-completion

Conversation

@stevenchen-db

@stevenchen-db stevenchen-db commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Changes

air run --watch now stops as soon as the command succeeds on the node it streams, instead of waiting for Jobs to mark the run terminal.

While streaming, the CLI watches the streamed node's new lines for the entry script's "Execution Summary" block ending in Completed at, which is printed only on exit code 0. When it appears, the command exits 0, and in -o json the closing envelope reports SUCCESS even though Jobs may still say RUNNING. A failure summary is ignored and the CLI waits for Jobs as today, so a retried attempt keeps being followed and a failed run still exits non-zero. Only --watch opts in; air logs is unchanged.

Why

Jobs reports the run terminal well after the user command exits. On a 2-node H100 run, the summary printed at 21:46:19 but the run was marked terminal at 21:46:51, so --watch looked hung for ~30s after the code finished. --watch follows one node, so it now stops when that node's command stops.

Tests

  • Unit tests for the summary matcher, exiting on success, a summary split across polls, ignoring a failure summary, exiting on a retried attempt's success, the opt-in gate, and the text and JSON --watch paths. Removing the opt-in gate, the success-only match, the matcher's state across polls, or the JSON SUCCESS status each makes a test fail.
  • go test ./cmd/air/... and TestAccept/air/ pass; ./task fmt and ./task checks are clean.
  • staticcheck and unused could not run locally (Go 1.27 toolchain crash), so CI is their first run.

This pull request and its description were written by Isaac.

Jobs reports an AI Runtime run terminal tens of seconds after the user
command exits, so `air run --watch` kept running after the work was done.

The stream now watches for the entry script's success summary. Once the
streamed node prints it, every node's newest lines are checked for the
same block, pinned to the current attempt, and the command exits 0 with a
SUCCESS closing envelope. A failure summary, a missing summary, an unknown
node count, or an API error keeps the existing wait for Jobs, since a failed
attempt may be retried.

Co-authored-by: Isaac <no-reply@databricks.com>
@stevenchen-db
stevenchen-db requested review from a team as code owners October 12, 2026 16:19
@stevenchen-db
stevenchen-db requested a review from rclarey October 12, 2026 16:19
@github-actions github-actions Bot added the AIR Databricks AI Runtime CLI label Oct 12, 2026
Co-authored-by: Isaac <no-reply@databricks.com>
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: e6328c8

Run: 38340195159

Env ✅​pass 🙈​skip Time
✅​ aws linux-2core-8gb 276 16 5:24
✅​ aws-windows-latest-4core-16gb 278 14 3:37
✅​ azure linux-2core-8gb 275 16 5:18
✅​ azure-windows-latest-4core-16gb 277 14 3:59
✅​ gcp linux-2core-8gb 276 16 5:23
✅​ gcp-windows-latest-4core-16gb 278 14 3:27
Top 6 slowest tests (at least 2 minutes):
duration env testname
3:59 gcp linux-2core-8gb TestAccept
3:57 azure-windows-latest-4core-16gb TestAccept
3:51 azure linux-2core-8gb TestAccept
3:47 aws linux-2core-8gb TestAccept
3:34 aws-windows-latest-4core-16gb TestAccept
3:23 gcp-windows-latest-4core-16gb TestAccept

stevenchen-db and others added 2 commits October 12, 2026 16:48
Once the streamed node's summary triggered the check, every poll re-fetched
every node up to the first unfinished one, so a failing node late in a large
run cost one request per node per second until Jobs reported the run
terminal. A node that succeeded stays succeeded, so the check now resumes at
the first unconfirmed node, and a retry resets that progress.

Also pin with tests that a missing node count blocks the early exit and
that each node is confirmed again on a new attempt.

Co-authored-by: Isaac <no-reply@databricks.com>
--watch follows one node, so stop when that node's command succeeds instead
of first confirming every node through extra per-node log requests. This
drops the node count, the per-attempt checks, and the confirmed-node
tracking. A failure summary still waits for Jobs, so a retried attempt keeps
being followed and a failed run still exits non-zero.

Co-authored-by: Isaac <no-reply@databricks.com>
@stevenchen-db stevenchen-db changed the title Exit air run --watch when the command succeeds on every node Exit air run --watch when the streamed node succeeds Oct 13, 2026
Comment thread cmd/air/logstream.go
log.Infof(st.ctx, "air logs: run %d finished in state %s", st.req.runID, st.status.displayState())
return st.status.succeeded(), nil
}
if st.req.exitOnCompletion && st.completion.completed {

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.

In a multi-node run, node 0 can succeed while another worker fails. This returns success immediately, so --watch exits 0 and emits SUCCESS without observing the failure or retries. Could we restrict this shortcut to single-node runs or confirm all nodes first?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AIR Databricks AI Runtime CLI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants