Skip to content

job_runs: make a triggered run idempotent across deploy retries - #5882

Open
radakam wants to merge 1 commit into
job-runs-wait-for-completionfrom
job-runs-idempotency-wait-for-completion
Open

job_runs: make a triggered run idempotent across deploy retries#5882
radakam wants to merge 1 commit into
job-runs-wait-for-completionfrom
job-runs-idempotency-wait-for-completion

Conversation

@radakam

@radakam radakam commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Changes

DoCreate sends an idempotency_token derived from the run config plus a new CreateIdentity framework hook, so a retried create rejoins the run it already triggered. The identity is the workspace root path, the resource key, and the id of the run being replaced when re-creating one whose remote copy is gone.

The new bundle-only rerun_token folds into the token to force a fresh run of an unchanged config. Setting idempotency_token directly is rejected at validate time.

DoDelete becomes a noop, which also drops the cancel-before-delete path from #5881.

Why

A deploy that fires run-now and then crashes before recording the run id cannot tell, on retry, that the job already started, so it triggers a second run. A token the CLI derives rather than the user sets fixes that, as long as every part of it is reconstructible from config and state — hence the identity, and hence rejecting a user-set token.

Delete is a noop because the Jobs API keeps a token reserved once the run it triggered is deleted: deleting a run tombstones its token and a later deploy of the same config errors instead of running. Cancelling is no better, since it settles the run on CANCELED, which no deploy that dedupes onto it could ever complete. A run whose wait was interrupted keeps going, and the error that ended the wait links its run page.

Tests

Six direct-engine acceptance tests: retrying after a lost run id, replacing a run that vanished remotely, bumping rerun_token, two runs of one job in one bundle, flipping config away and back to dedupe onto the original run, and rejecting a user-set token. idempotency_token is masked with a Distinct replacement so a golden with several run-now calls shows whether they shared a token or rotated.

Unit tests cover token derivation, rejoining an already-triggered run, and DoDelete issuing no requests. libs/testserver models the API's deduplication, including a token staying reserved after its run is deleted.

@radakam
radakam temporarily deployed to test-trigger-is July 9, 2026 14:20 — with GitHub Actions Inactive
@radakam
radakam temporarily deployed to test-trigger-is July 9, 2026 14:20 — with GitHub Actions Inactive
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 8e63c56

Run: 30546484129

Env 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
🔄​ aws linux 2 5 3 328 1334 6:11
💚​ aws windows 5 3 332 1332 8:24
💚​ azure linux 5 3 330 1333 6:46
💚​ azure windows 5 3 332 1331 8:03
💚​ gcp linux 2 4 329 1335 8:22
💚​ gcp windows 2 4 331 1333 10:13
10 interesting tests: 5 RECOVERED, 3 SKIP, 2 flaky
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/invariant/no_drift 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🔄​ TestFilerWorkspaceNotebook 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p
🔄​ TestFilerWorkspaceNotebook/rJupyterNb.ipynb 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p
💚​ TestFetchRepositoryInfoAPI_FromRepo 💚​R 💚​R 💚​R 💚​R 🙈​S 🙈​S
💚​ TestFetchRepositoryInfoAPI_FromRepo/root 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/subdir 💚​R 💚​R 💚​R 💚​R
Top 6 slowest tests (at least 2 minutes):
duration env testname
8:34 gcp windows TestAccept
6:25 azure windows TestAccept
6:20 aws windows TestAccept
3:04 gcp linux TestAccept
2:56 azure linux TestAccept
2:51 aws linux TestAccept

@radakam
radakam temporarily deployed to test-trigger-is July 10, 2026 09:31 — with GitHub Actions Inactive
@radakam
radakam temporarily deployed to test-trigger-is July 10, 2026 09:31 — with GitHub Actions Inactive
@radakam
radakam force-pushed the job-runs-idempotency-wait-for-completion branch from acf313b to 3a5ea16 Compare July 13, 2026 09:13
@radakam
radakam temporarily deployed to test-trigger-is July 13, 2026 09:13 — with GitHub Actions Inactive
@radakam
radakam temporarily deployed to test-trigger-is July 13, 2026 09:13 — with GitHub Actions Inactive
@radakam
radakam temporarily deployed to test-trigger-is July 14, 2026 08:30 — with GitHub Actions Inactive
@radakam
radakam temporarily deployed to test-trigger-is July 14, 2026 08:30 — with GitHub Actions Inactive
@radakam
radakam force-pushed the job-runs-idempotency-wait-for-completion branch from 51dca78 to 66ca15a Compare July 14, 2026 10:07
@radakam
radakam temporarily deployed to test-trigger-is July 14, 2026 10:07 — with GitHub Actions Inactive
@radakam
radakam temporarily deployed to test-trigger-is July 14, 2026 10:07 — with GitHub Actions Inactive
@radakam
radakam temporarily deployed to test-trigger-is July 14, 2026 10:10 — with GitHub Actions Inactive
@radakam
radakam temporarily deployed to test-trigger-is July 14, 2026 10:10 — with GitHub Actions Inactive
@radakam
radakam temporarily deployed to test-trigger-is July 14, 2026 11:00 — with GitHub Actions Inactive
@radakam
radakam temporarily deployed to test-trigger-is July 14, 2026 11:00 — with GitHub Actions Inactive
@radakam
radakam requested a review from denik July 14, 2026 11:52
@radakam
radakam marked this pull request as ready for review July 14, 2026 11:52
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Approval status: pending

/acceptance/bundle/ - needs approval

38 files changed
Suggested: @denik
Also eligible: @pietern, @janniklasrose, @shreyas-goenka, @andrewnester, @lennartkats-db, @anton-107

/bundle/ - needs approval

15 files changed
Suggested: @denik
Also eligible: @pietern, @janniklasrose, @shreyas-goenka, @andrewnester, @lennartkats-db, @anton-107

General files (require maintainer)

5 files changed
Based on git history:

  • @denik -- recent work in bundle/direct/, bundle/phases/, bundle/direct/dresources/

Any maintainer (@andrewnester, @anton-107, @denik, @pietern, @shreyas-goenka, @simonfaltum, @renaudhartert-db, @janniklasrose, @lennartkats-db) can approve all areas.
See OWNERS for ownership rules.

@radakam radakam changed the title Add idempotency_token to job_runs resource job_runs: wait for run completion, expose output, dedupe deploy retries Jul 22, 2026
radakam added a commit that referenced this pull request Jul 28, 2026
## Changes

The fake workspace reported every run as `TERMINATED SUCCESS`,
overwriting the `FAILED` state it had just recorded for a task it
executed locally. A run now reports the terminal state its tasks add up
to.

Tasks whose code the fake workspace does not have are left successful.
An immutable deployment, for example, uploads the bundle as a snapshot
zip that the fake workspace never unpacks, so there is nothing to
execute; that gap is in the fake workspace, not in the job under test.

With that, `job_runs/failed_run` drops the `runs/get` and
`runs/get-output` stubs from its `test.toml` and fails for real, on a
task that exits non-zero.

## Why

Follow-up to #5882, where a stub stood in for a failing run because the
fake workspace could not produce one. Splitting it out keeps the
behaviour change for existing tests out of that PR.

The stub could not know the test server's address, so it could not
report a run page URL; the golden now covers the run URL and the
progress lines too.

## Tests

`libs/testserver` unit tests for the rollup, for completing the tasks
that jobs/runs/submit leaves running, and for a task whose code is
missing not failing its run. `job_runs/failed_run` covers the end-to-end
path. The rest of the acceptance suite passes unchanged.
resources.job_runs.my_run: Run URL: [DATABRICKS_URL]/jobs/[NUMID]/runs/[NUMID]?o=[NUMID]
resources.job_runs.my_run: [TIMESTAMP] "my-job" RUNNING
resources.job_runs.my_run: [TIMESTAMP] "my-job" TERMINATED FAILED task main failed
Error: cannot create resources.job_runs.my_run: job run [NUMID] did not succeed: FAILED: task main failed

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.

if possible, use replace_ids.py or add_repl.py to record replacements for specific IDs.


STATE=.databricks/bundle/default/resources.json
run_id() {
jq -r '.state["resources.job_runs.my_run"].__id__' "$STATE"

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.

there is read_id.py

@radakam
radakam force-pushed the job-runs-idempotency-wait-for-completion branch from d2a1cc6 to 2d11c4d Compare July 28, 2026 22:08
@radakam
radakam changed the base branch from main to job-runs-wait-for-completion July 28, 2026 22:11
@radakam radakam changed the title job_runs: wait for run completion, expose output, dedupe deploy retries job_runs: expose output, dedupe deploy retries Jul 29, 2026
DoCreate derives idempotency_token as a SHA-256 of the run config plus a create
identity (workspace root path, resource key, and the prior run id when
re-creating a vanished run), so a deploy that fires run-now and then crashes
before recording the id rejoins that same run on retry instead of starting a
duplicate. The token is computed by the CLI and rejected if set in bundle
configuration; the new bundle-only rerun_token field folds into it, so bumping
it re-runs a configuration that already ran.

DoDelete becomes a noop: the Jobs API keeps an idempotency_token reserved once
the run it triggered is deleted, so deleting the run would tombstone the token
and break re-running the same configuration. That drops the cancel-before-delete
path this branch added earlier: with no delete there is nothing to make room
for, and cancelling would settle the run on CANCELED, which no later deploy that
dedupes onto it could complete. A run whose wait was interrupted keeps going,
and the error that ended the wait links its run page.
@radakam
radakam force-pushed the job-runs-idempotency-wait-for-completion branch from 26d8bc8 to 8e63c56 Compare July 30, 2026 13:19
@radakam radakam changed the title job_runs: expose output, dedupe deploy retries job_runs: make a triggered run idempotent across deploy retries Jul 30, 2026
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