Uh oh!
There was an error while loading. Please reload this page.
Crash recovery for YARN cluster mode in SparkSubmitOperator built on AIP-103 - #67473
Conversation
a1cce88 to
51f90ffCompare6771ad2 to
391052cCompareamoghrajesh
commented
Jun 4, 2026
Running w full tests |
amoghrajesh
commented
Jun 5, 2026
cc @nailo2c |
potiuk
commented
Jun 5, 2026
Some checks are.failing though |
amoghrajesh
commented
Jun 5, 2026
I'll be on it, @nailo2c is going to help me test this one soon too |
I tested the What passed:
What failed: The RM state confirmed that the app was not killed; it completed naturally: {
"id": "application_1780697276912_0017",
"state": "FINISHED",
"finalStatus": "SUCCEEDED",
"name": "airflow-67473-kill"
}So the local task process received This appears to come from I also verified the proposed fix locally by making the public method delegate to defkill_yarn_application(self, application_id: str) ->None:
"""Public alias for ResumableJobMixin / operator on_kill paths."""self._kill_yarn_application(application_id)With that change in place, the same {
"id": "application_1780697276912_0019",
"state": "KILLED",
"finalStatus": "KILLED",
"name": "airflow-67473-kill"
}It would also be worth adding an operator-level |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
amoghrajesh
commented
Jun 7, 2026
Thanks for your comments @nailo2c! I have handled them now |
aaron-y-chen
commented
Jun 7, 2026
Hi @amoghrajesh, I've tested your latest code, and it worked well on my dataproc yarn cluster, LGTM :) |
amoghrajesh
commented
Jun 10, 2026
Addressed all review comments. Here is a summary of whats in the final state: Hook (
Operator ( Four paths for YARN cluster mode in
|
amoghrajesh
commented
Jun 10, 2026
Merging this one now, thanks @nailo2c for production testing. |
Uh oh!
There was an error while loading. Please reload this page.
Backport failed to create: v3-2-test. View the failure log Run detailsNote: As of Merging PRs targeted for Airflow 3.X In matter of doubt please ask in #release-management Slack channel.
You can attempt to backport this manually by running: cherry_picker f3ca1c2 v3-2-testThis should apply the commit to the v3-2-test branch and leave the commit in conflict state marking After you have resolved the conflicts, you can continue the backport process by running: cherry_picker --continueIf you don't have cherry-picker installed, see the installation guide. |
amoghrajesh
commented
Jun 10, 2026
No need to backport |
aaron-y-chen
commented
Jun 10, 2026
My pleasure 😄 |
Was generative AI tooling used to co-author this PR?
Built on top of a few PRs: #67118 and even #65991
What problem are we solving?
SparkSubmitOperatorin YARN cluster mode submits the Spark driver as a YARN AM — it runs independently on the cluster. If the Airflow worker dies mid-poll, the YARN app keeps running but Airflow loses track of it. The only recovery today is to submit a brand new application, wasting already-done compute or causing conflicts if the job is not idempotent.Proposed change
Extends
ResumableJobMixincrash recovery (introduced in #67118 for Spark standalone cluster mode) to YARN cluster mode.On first run:
spark.yarn.submit.waitAppCompletion=falseso spark-submit exits after YARN accepts the apptask_state/ws/v1/cluster/apps/{appId}) until terminal, built on top of Spark: track YARN cluster apps via RM REST API to free JVM #65991On retry (worker crash):
task_stateStatus mapping:
The hook queries
GET /ws/v1/cluster/apps/{appId}and synthesizes YARN API's two-field response into a single string for the mixin interface:statefinalStatusNEW/NEW_SAVING/SUBMITTED/ACCEPTED/RUNNINGUNDEFINEDFINISHEDSUCCEEDEDSUCCEEDEDFINISHEDFAILED/KILLED/UNDEFINEDFAILEDFAILEDFAILED/KILLEDFAILEDKILLEDKILLEDFAILEDstatedetermines whether the app is still running;finalStatusresolves the outcome whenstate=FINISHED. UsingfinalStatusalone would be unreliable because YARN can reportUNDEFINEDfor a dead app if the RM recovered from a crash and lost the final status.Observability: Logs status transitions such as ACCEPTED → RUNNING and a heartbeat every 10 polls so users can see the job is alive without log spam.
on_kill: Since spark-submit has already exited (
waitAppCompletion=false), the hook's CLI kill has nothing to terminate. YARN cluster modeon_killuses the Resource Manager REST API instead (PUT /ws/v1/cluster/apps/{appId}/state).What changes from the standalone PR (#67118)
The standalone PR covered
spark://masters only. This PR adds the parallel YARN path. The two paths are kept as separate if branches inexecute()for readability.User impact and backcompat
New behaviour (opt-in by existing default):
SparkSubmitOperatorwith--master yarn --deploy-mode clusternow gets crash recovery automatically.reconnect_on_retry=Trueis the defaultextra: yarn_resourcemanager_webapp_address(e.g.http://rm.example.com:8088). Without it, the resumable path raisesValueErrorat submit time with a clear message.yarn logs -applicationId ...). Most users will not notice; users relying on orchestrator log streaming should setreconnect_on_retry=FalseUnaffected paths:
NotImplementedError(sister PR)reconnect_on_retry=False— skips crash recovery, submits and polls without task_state persistenceTesting Details
Setup
Using this docker compose setup to spin up a 4 node / container Hadoop 3.2.1 cluster: NN, DN, RM, NM.
Prepared a script to setup few things / bootstrap them for running spark on hadoop -- setup Java, stages Spark JARs to HDFS, write
core-site.xmlandyarn-site.xmlconfig, registerspark_yarnairflow connection with the rightyarn_resourcemanager_webapp_address:Connection
Using this DAG:
The DAG submits SparkPi with 10,000 iterations (~2 min runtime) — long enough to kill the worker mid-poll and verify reconnection on retry. Check the YARN UI to confirm only one application was submitted across multiple Airflow task attempt.
This is how a successful run will look like:
Steps to Verify reconnect behaviour
Log:
{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.