Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
554da16
testserver: roll task outcomes up into the run state
radakam Jul 28, 2026
12f20c8
job_runs: wait for run completion in WaitAfterCreate
radakam Jul 28, 2026
29847ba
job_runs: report run progress through a tracker shared with bundle run
radakam Jul 29, 2026
ac4195a
acc: run a job_run against a real workspace
radakam Jul 29, 2026
cfa31f7
acc: check the resolved job parameters are not drift on cloud
radakam Jul 29, 2026
b5a0f02
job_runs: tighten the comments added by this branch
radakam Jul 29, 2026
4d31c18
job_runs: report only the run URL and the state the run ends in
radakam Jul 29, 2026
cccdd2e
acc: run a failing job_run against a real workspace
radakam Jul 29, 2026
08c7e68
job_runs: shorten the comments added by this branch
radakam Jul 29, 2026
49db7d1
job_runs: handle a wait the user interrupted
radakam Jul 30, 2026
bfc37f0
job_runs: wait for any terminal state, not the two the SDK stops on
radakam Jul 30, 2026
73f90cd
job_runs: refuse to resolve a reference to a run that has not finished
radakam Aug 3, 2026
f2e820a
acc: merge wait_output into wait, which already ran locally too
radakam Aug 4, 2026
c17bf4b
job_runs: re-run a run that did not succeed, rather than refusing to …
radakam Aug 4, 2026
7357437
job_runs: shorten the comments added by this branch
radakam Aug 4, 2026
e345b8b
acc: run the job_run invariant config on cloud too
radakam Aug 4, 2026
1b9ed0a
testserver: report a failed task the way a real workspace does
radakam Aug 4, 2026
3b8aa72
acc: merge failed_cloud into failed_run, which already ran locally too
radakam Aug 4, 2026
0ef6e59
acc: stop ignoring job_run test inputs the comparison never flagged
radakam Aug 4, 2026
2f16818
acc: merge failed_redeploy into failed_run, so the redeploy runs on c…
radakam Aug 4, 2026
6e996f4
acc: name the job and run ids every job_runs test prints
radakam Aug 4, 2026
806b036
dresources: scope the settled-state read in testCRUD to job_runs
radakam Aug 4, 2026
a120179
dresources: drop the redundant settled-state assertion for job_runs
radakam Aug 4, 2026
7ce38aa
job_runs: report the run page and the outcome, not bundle run's event
radakam Aug 5, 2026
3c917e5
dresources: keep testCRUD's wait assertion, and scope the settled rea…
radakam Aug 5, 2026
958b70a
job_runs: pick the last failed attempt without a map
radakam Aug 5, 2026
247cb81
job_runs: read a task's outcome off its termination details
radakam Aug 5, 2026
9cf9e69
testserver: keep the exit error when a failed task writes nothing
radakam Aug 5, 2026
b3bd792
testserver: answer jobs/runs/cancel
radakam Aug 5, 2026
1c1bf29
acc: give the job_run invariant config a task cloud will run
radakam Aug 5, 2026
ec5c7bf
job_runs: say what the code does, not what it does not
radakam Aug 5, 2026
76f4449
job_runs: adopt a run still in progress, instead of recreating it
radakam Aug 5, 2026
622433c
acc: cover the redeploy that adopts an interrupted run
radakam Aug 5, 2026
6079c77
job_runs: do the adopted run's wait in DoUpdate
radakam Aug 5, 2026
644443d
job_runs: unit-test the in-progress change override
radakam Aug 5, 2026
1c90ef6
job_runs: say what happens, and drop the redundant override tests
radakam Aug 5, 2026
d0a361a
job_runs: clarify that the injected fault hits the poll, not the run
radakam Aug 5, 2026
aa23372
job_runs: say why the wait does not use the SDK waiter
radakam Aug 5, 2026
69692c2
acc: drop Local from job_runs wait tests after #6196
radakam Aug 7, 2026
604844c
job_runs: clear result_state before persistence
radakam Aug 10, 2026
fd7e2e8
acc: update job_runs redeploy golden for cleared result_state
radakam Aug 10, 2026
f5ecb4f
job_runs: keep workspace-specific fields out of the run's remote state
radakam Aug 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .nextchanges/bundles/job-runs-wait-for-completion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
direct: the experimental `job_runs` resource now waits for the triggered run to finish, so other resources can reference its outcome (e.g. `${resources.job_runs.nightly.state.result_state}`). A run that does not succeed fails the deploy, naming the failed task, and is run again on the next deploy. If a deploy is interrupted while waiting, the next one resumes waiting on the same run.
17 changes: 10 additions & 7 deletions acceptance/bundle/invariant/configs/job_run.yml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ resources:
foo:
name: test-job-$UNIQUE_NAME
tasks:
# Deploying a job_run actually runs the job, and the wait needs a task a
# workspace will execute. Serverless keeps this one to about a minute.
- task_key: only_task
notebook_task:
notebook_path: /Shared/notebook
new_cluster:
spark_version: $DEFAULT_SPARK_VERSION
node_type_id: $NODE_TYPE_ID
instance_pool_id: $TEST_INSTANCE_POOL_ID
num_workers: 1
spark_python_task:
python_file: ./job_run.py
environment_key: default

environments:
- environment_key: default
spec:
environment_version: "2"

job_runs:
foo_run:
Expand Down
1 change: 1 addition & 0 deletions acceptance/bundle/invariant/data/job_run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
print("hello from a job_run")
1 change: 1 addition & 0 deletions acceptance/bundle/refschema/out.fields.txt
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,7 @@ resources.job_runs.*.python_params[*] string ALL
resources.job_runs.*.queue *jobs.QueueSettings ALL
resources.job_runs.*.queue.enabled bool ALL
resources.job_runs.*.resolved_job_id int64 INPUT
resources.job_runs.*.result_state jobs.RunResultState REMOTE STATE
Comment thread
radakam marked this conversation as resolved.
resources.job_runs.*.run_id int64 REMOTE
resources.job_runs.*.run_name string REMOTE
resources.job_runs.*.run_page_url string REMOTE
Expand Down
11 changes: 5 additions & 6 deletions acceptance/bundle/resources/job_runs/basic/output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Resources:
>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-basic/default/files...
Deploying resources...
job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID]

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.

Can we include key name in the prefix?

"Output from jobs_runs.foo: id=[MY_RUN_ID]: ..."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to need the resource key in cmdio output at the framework-level, I think it might make sense to do as a follow up, what do you think?

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.

can be a follow up if hard to do right away

job run [MY_RUN_ID]: SUCCESS
Updating deployment state...
Deployment complete!

Expand Down Expand Up @@ -72,12 +74,9 @@ Resources:
}
}

=== the triggered run id is stored in state
>>> read_id.py my_run
[MY_RUN_ID]

>>> read_id.py my_job
[MY_JOB_ID]
=== the triggered run id is stored in state, without result_state
>>> read_state.py job_runs my_run id result_state
job_runs my_run id='[MY_RUN_ID]' result_state=None

>>> [CLI] bundle destroy --auto-approve
The following resources will be deleted:
Expand Down
8 changes: 5 additions & 3 deletions acceptance/bundle/resources/job_runs/basic/script
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ trace $CLI bundle summary
title "exactly one run-now request was made"
trace print_requests.py //jobs/run-now

title "the triggered run id is stored in state"
trace read_id.py my_run
trace read_id.py my_job
# result_state is a planning sentinel and is cleared before SaveState.
title "the triggered run id is stored in state, without result_state"
trace read_state.py job_runs my_run id result_state
read_id.py my_run > /dev/null
read_id.py my_job > /dev/null
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
bundle:
name: job-runs-failed-run

workspace:
root_path: ~/.bundle/$UNIQUE_NAME

resources:
jobs:
my_job:
name: test-job-$UNIQUE_NAME
tasks:
# Serverless keeps the run to a few minutes: the Jobs API retries the
# failed task once before giving up on it.
- task_key: main
spark_python_task:
python_file: ./fail.py
environment_key: default

environments:
- environment_key: default
spec:
environment_version: "2"

# Reads the run's outcome, so the failing run aborts the deploy before this
# job is created. Separate from my_job, which my_run already depends on, to
# avoid a cycle.
downstream_job:
name: test-downstream-job-$UNIQUE_NAME
tags:
run_result: ${resources.job_runs.my_run.state.result_state}
tasks:
- task_key: main
condition_task:
op: EQUAL_TO
left: "1"
right: "1"

job_runs:
my_run:
job_id: ${resources.jobs.my_job.id}
1 change: 1 addition & 0 deletions acceptance/bundle/resources/job_runs/failed_run/fail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
raise RuntimeError("intentional failure")

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 104 additions & 0 deletions acceptance/bundle/resources/job_runs/failed_run/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@

=== a run that finishes FAILED fails the deploy
>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files...
Deploying resources...
job run [MY_RUN_ID]: Run URL: [RUN_URL]
Error: cannot create resources.job_runs.my_run: waiting after creating id=[MY_RUN_ID]: run did not succeed: FAILED: Task main failed with message: Workload failed, see run output for details.
task "main": RuntimeError: intentional failure
run page: [RUN_URL]

Error: cannot create resources.jobs.downstream_job: dependency failed: resources.job_runs.my_run

Updating deployment state...

=== the failed run is recorded, and not having succeeded is drift
>>> read_id.py my_run
[MY_RUN_ID]

>>> [CLI] bundle plan -o json
{
"depends_on": [
{
"node": "resources.jobs.my_job",
"label": "${resources.jobs.my_job.id}"
}
],
"action": "recreate",
"new_state": {
"value": {
"job_id": [MY_JOB_ID],
"result_state": "SUCCESS"
}
},
"remote_state": {
"job_id": [MY_JOB_ID],
"result_state": "FAILED",
"run_id": [MY_RUN_ID],
"run_name": "test-job-[UNIQUE_NAME]",
"run_page_url": "[RUN_URL]",
"run_type": "JOB_RUN",
"state": {
"life_cycle_state": "INTERNAL_ERROR",
"result_state": "FAILED",
"state_message": "Task main failed with message: Workload failed, see run output for details."
}
},
"changes": {
"result_state": {
"action": "recreate",
"reason": "immutable",
"new": "SUCCESS",
"remote": "FAILED"
}
}
}

=== so a redeploy runs the job again, and fails again
>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files...
Deploying resources...
job run [MY_RUN_ID_2]: Run URL: [RUN_URL]
Error: cannot recreate resources.job_runs.my_run: waiting after creating id=[MY_RUN_ID_2]: run did not succeed: FAILED: Task main failed with message: Workload failed, see run output for details.
task "main": RuntimeError: intentional failure
run page: [RUN_URL]

Error: cannot create resources.jobs.downstream_job: dependency failed: resources.job_runs.my_run

Updating deployment state...

=== run-now was issued once per deploy, and the recreate deleted the failed run
>>> print_requests.py --keep //jobs/run-now
{
"method": "POST",
"path": "/api/2.2/jobs/run-now",
"body": {
"job_id": [MY_JOB_ID]
}
}
{
"method": "POST",
"path": "/api/2.2/jobs/run-now",
"body": {
"job_id": [MY_JOB_ID]
}
}

>>> print_requests.py //jobs/runs/delete
{
"method": "POST",
"path": "/api/2.2/jobs/runs/delete",
"body": {
"run_id": [MY_RUN_ID]
}
}

>>> [CLI] bundle destroy --auto-approve
The following resources will be deleted:
delete resources.job_runs.my_run
delete resources.jobs.my_job

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]

Deleting files...
Destroy complete!
31 changes: 31 additions & 0 deletions acceptance/bundle/resources/job_runs/failed_run/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
envsubst < databricks.yml.tmpl > databricks.yml

cleanup() {
trace $CLI bundle destroy --auto-approve
rm -f out.requests.txt
}
trap cleanup EXIT

# The error names the failed task and the message the workspace reported for it,
# and downstream_job is reported as a failed dependency because it reads the
# run's result_state.
title "a run that finishes FAILED fails the deploy"
musterr trace $CLI bundle deploy

# The framework saves the run id before WaitAfterCreate, so FAILED vs SUCCESS is drift.
title "the failed run is recorded, and not having succeeded is drift"
trace read_id.py my_run
trace $CLI bundle plan -o json | jq '.plan["resources.job_runs.my_run"]'

# Name the job too, so the run-now bodies below say which number it is.
read_id.py my_job > /dev/null

title "so a redeploy runs the job again, and fails again"
musterr trace $CLI bundle deploy
read_id.py my_run > /dev/null

# The delete names [MY_RUN_ID], the run that failed first, not the [MY_RUN_ID_2]
# that replaced it: the recreate discards the run it replaces.
title "run-now was issued once per deploy, and the recreate deleted the failed run"
trace print_requests.py --keep //jobs/run-now
trace print_requests.py //jobs/runs/delete
31 changes: 31 additions & 0 deletions acceptance/bundle/resources/job_runs/failed_run/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# job_runs is a direct-engine-only resource; the Terraform provider has no
# equivalent, so restrict the matrix to direct.
EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"]

# Runs the failing job for real, so the message the deploy names the task with is
# one a workspace reported. Serverless needs Unity Catalog.
Cloud = true
RequiresUnityCatalog = true

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.

I thought we don't need this anymore? RequiresUnityCatalog

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still needed as serverless requires UC, so without it a non-UC workspace would fail instead of skipping.


# databricks.yml is rendered by the script, and the deploy fails mid-way, leaving
# local deployment state behind.
Ignore = [
".databricks",
"databricks.yml",
]

# The host and the workspace selector in the run URL differ per workspace; the URL
# form itself is covered by libs/workspaceurls.
[[Repls]]
Old = 'Run URL: .*'
New = 'Run URL: [RUN_URL]'

[[Repls]]
Old = 'run page: .*'
New = 'run page: [RUN_URL]'

# [DATABRICKS_URL] is the recording proxy's host, not the workspace host the Jobs
# API reports here.
[[Repls]]
Old = '"run_page_url": ".*"'
New = '"run_page_url": "[RUN_URL]"'
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
bundle:
name: job-runs-interrupted-run

resources:
jobs:
my_job:
name: my-job
tasks:
- task_key: main
notebook_task:
notebook_path: /Workspace/test

job_runs:
my_run:
job_id: ${resources.jobs.my_job.id}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading