Add durable execution to DatabricksRunNowOperator#69174
Merged
amoghrajesh merged 3 commits intoJul 1, 2026
Conversation
uranusjr
approved these changes
Jun 30, 2026
kaxil
reviewed
Jun 30, 2026
1 task
Contributor
Author
|
Thanks for review @kaxil. Merging this one, since i've handled your comments, let me know if there are more comments and I can handle in follow ups. |
75 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why we are doing this
DatabricksRunNowOperatortriggers a run of an existing Databricks job, gets back arun_id, and then polls synchronously on the worker until the run finishes. Thatrun_idlives only in the worker process. If the worker crashes or is preempted mid-poll (eviction, OOM, a deploy, a spot reclaim), Airflow retries the task in a fresh process with no memory of therun_id, so it callsrun-nowagain, triggering a second run of the same job. The original run keeps executing on Databricks, orphaned, while the retry runs a duplicate.This is the same crash-safety gap closed for
DatabricksSubmitRunOperatorin #68974, applied to the run-now path. For long-running jobs it means paying twice for the same work, and the only existing mitigation (cancel_previous_runs=True) cancels rather than reconnects.Benefits this will bring in
Approach
The operator now builds on the AIP-103 task state store. On the first run it persists the Databricks run id to the task state store before polling begins. On a retry it reads that id back and inspects the run's current state: still running means reconnect and keep polling, already succeeded means return immediately, and terminally failed (or a run whose history has expired) means trigger a fresh run. Reconnect keys off the persisted
run_id, so it works the same whether the job was identified byjob_idorjob_name. Deferrable mode is unchanged and takes precedence when enabled.Changes of note
get_job_status/is_job_active/is_job_succeeded) are intentionally duplicated fromDatabricksSubmitRunOperatorrather than extracted into a shared base, to keep each operator self-contained and this PR scoped to RunNow. They are small and each operator has its own tests covering them.DatabricksApiError(carryinghttp_status_code) introduced alongside the SubmitRun work, so it is a structured404check rather than message-string matching._build_run_now_payload()(merge, validate, resolvejob_nametojob_id, inject params) and thecancel_previous_runsstep. On reconnect the payload is rebuilt via the side-effect-free path, sorepair_runhas the resolvedjob_idit needs and works across the reconnect boundary for bothjob_idandjob_name, without re-firingcancel_previous_runsagainst the run being reconnected to.Backcompat
How to opt out
Set
durable=Falseon the operator:This restores the previous behavior: always trigger a fresh run on retry and never touch the task state store. It can also be set through
default_argsto opt out across a whole Dag or deployment.Testing
DAG used:
Before my changes
Kill mid run:

Worker comes back up:
Duplicate submissions:
After my changes
Killed mid run:
Job on dbx:
Restored worker:
No duplicate job:
Killed the job from airflow:
Was generative AI tooling used to co-author this PR?
{pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.