fix(controller): cancel in-flight spark-submit on controller shutdown#2934
fix(controller): cancel in-flight spark-submit on controller shutdown#2934a7i wants to merge 3 commits into
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
🎉 Welcome to the Kubeflow Spark Operator! 🎉 Thanks for opening your first PR! We're happy to have you as part of our community 🚀 Here's what happens next:
Join the community:
Feel free to ask questions in the comments if you need any help or clarification! |
ae91caf to
71f74fc
Compare
There was a problem hiding this comment.
Pull request overview
This PR reduces the likelihood of losing the post-spark-submit status update during controller shutdown/restarts by (1) increasing the controller-runtime graceful shutdown window and (2) propagating reconcile cancellation into the spark-submit subprocess.
Changes:
- Add a
--graceful-shutdown-timeoutCLI flag (default60s) and wire it to controller-runtimeGracefulShutdownTimeout. - Make
spark-submitexecution cancellable by usingexec.CommandContext(ctx, ...)and threading the reconcile context into submission. - Extend the Helm chart to configure
controller.gracefulShutdownTimeout(default60s) andcontroller.terminationGracePeriodSeconds(default90), with unit tests and README updates.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/controller/sparkapplication/submission.go | Pass reconcile context into spark-submit via exec.CommandContext to support cancellation. |
| cmd/operator/controller/start.go | Add --graceful-shutdown-timeout flag and configure controller-runtime manager shutdown timeout. |
| charts/spark-operator-chart/values.yaml | Introduce default values for graceful shutdown timeout and pod termination grace period. |
| charts/spark-operator-chart/templates/controller/deployment.yaml | Render the new controller arg and set terminationGracePeriodSeconds on the controller Pod spec. |
| charts/spark-operator-chart/tests/controller/deployment_test.yaml | Add Helm unit tests for the new arg and pod termination grace period settings. |
| charts/spark-operator-chart/README.md | Document the new Helm values. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // cancellation) terminates the child process instead of letting it run to completion | ||
| // and create a driver pod whose status update will never be persisted. |
There was a problem hiding this comment.
Reworded in b41b544 to drop the implication that cancellation prevents a driver pod from being created; the comment now says cancellation only narrows the window for losing the post-submit status update.
tariq-hasan
left a comment
There was a problem hiding this comment.
Hi @a7i! I have left a few questions. Thanks!
/cc @nabuskey @ChenYi015
| # has time to drain in-flight reconciles, including any that are still writing status | ||
| # after a successful spark-submit. | ||
| terminationGracePeriodSeconds: 90 | ||
|
|
There was a problem hiding this comment.
I am wondering if in-flight reconciles can really finish if a termination signal is already propagated down the controller-runtime chain to the reconcile loop before it hits the spark-submit.
My understanding is that the real crux of the code changes here is that JVM processes are being terminated or killed off through signal propagation so we have a better chance of avoiding driver pod creation altogether. Reconciles seem to be cancelled immediately and are simply given a grace period for cleanup rather than being allowed to finish.
There was a problem hiding this comment.
Good point, you are right. The controller-runtime cancels the reconcile context as soon as SIGTERM arrives, and updateSparkApplicationStatus uses that same context, so the window does not let a normal reconcile finish writing status. The real value is interrupting the in-flight spark-submit JVM before it creates a driver pod the controller will never get to record. I reworded both gracefulShutdownTimeout and terminationGracePeriodSeconds in b41b544 to reflect that (cleanup/reap window, not "let reconciles finish"). Regenerated the chart README as well.
Production contextWe hit the same failure mode in production on 2026-06-03 (details in #2788). Two How this PR helps: SIGTERM during rollout interrupted an in-flight submit after the driver pod often already existed. Making What still needs #2932: In our trace the driver was already created before cancel, and the next leader logged We are also planning |
|
Hi @tariq-hasan @nabuskey @ChenYi015 - this is a pretty low risk PR, would you consider reviewing this? We continue to have issues in production when the operator pods are rotated or we do a new operator rollout. This causes pipelines that are mid-flight (in particular driver pod creation via spark-submit) to be cancelled. |
|
If you prefer, I can remove the |
b41b544 to
5524fb2
Compare
ended up doing this to reduce the changes and flags |
5524fb2 to
4cd9268
Compare
| LeaseDuration: &leaderElectionLeaseDuration, | ||
| RenewDeadline: &leaderElectionRenewDeadline, | ||
| RetryPeriod: &leaderElectionRetryPeriod, | ||
| GracefulShutdownTimeout: &driverPodCreationGracePeriod, |
There was a problem hiding this comment.
This changes the current (implicit) behaviour of GracefulShutdownTimeout. It currently sets to 30 seconds viasetOptionsDefaults in controller manager.
So creation grace period setting is applied to all controller runtime managed context.
Is this needed? As far as I can tell, it should be: driverPodCreationGracePeriod < GracefulShutdownTimeout < terminationGracePeriodSeconds
If this must be in exactly this order, we could either derive it from a value or validate it at startup to protect end users.
There was a problem hiding this comment.
yeah initially I added a new flag called gracefulShutdownTimeout but removed it thinking that it would simplify the implementation. let me take another look
There was a problem hiding this comment.
Addressed in 8a7a6a2: GracefulShutdownTimeout is now driverPodCreationGracePeriod + 1s, so the ordering is driverPodCreationGracePeriod < GracefulShutdownTimeout < terminationGracePeriodSeconds with defaults 10s / 11s / 60s. Chart values and flag help text document the constraint.
There was a problem hiding this comment.
Correction in 22469f5: we no longer override GracefulShutdownTimeout. It stays at controller-runtime's 30s default; driverPodCreationGracePeriod only sets runSparkSubmit WaitDelay. Startup rejects grace period >= 30s to preserve ordering.
| cmd.Cancel = func() error { | ||
| return cmd.Process.Signal(syscall.SIGTERM) | ||
| } | ||
| cmd.WaitDelay = s.ShutdownGracePeriod |
There was a problem hiding this comment.
Is this a race condition? We wait for spark-submit command to exit and ShutdownGracePeriod for exactly the same time. So there's no guarantee that one happens before the other?
There was a problem hiding this comment.
Good catch. WaitDelay and GracefulShutdownTimeout were both 10s before, so the manager could exit while runSparkSubmit was still in its WaitDelay window. GracefulShutdownTimeout is now driverPodCreationGracePeriod + 1s, and a test asserts we wait the full ShutdownGracePeriod before force-kill when SIGTERM is ignored.
When the controller leader receives SIGTERM during spark-submit, the reconcile context is cancelled immediately and the JVM subprocess may keep running until the process exits or the kubelet sends SIGKILL. That can create a driver pod the controller never records, contributing to duplicate submissions on restart. - Add SparkSubmitter.runSparkSubmit using exec.CommandContext; send SIGTERM on cancellation and wait up to ShutdownGracePeriod before force-killing. - Reuse --driver-pod-creation-grace-period for ShutdownGracePeriod and Manager.GracefulShutdownTimeout (no separate graceful-shutdown flag). - Helm: controller pod terminationGracePeriodSeconds default 60. - Add runSparkSubmit unit tests and chart deployment tests. Refs kubeflow#2788 Signed-off-by: Amir Alavi <amiralavi7@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Set GracefulShutdownTimeout to driverPodCreationGracePeriod plus one second so the manager outlives runSparkSubmit's WaitDelay window. Document the ordering constraint and add a test for force-kill after WaitDelay. Co-authored-by: Cursor <cursoragent@cursor.com>
4cd9268 to
8a7a6a2
Compare
Do not override GracefulShutdownTimeout with driverPodCreationGracePeriod. The manager default stays 30s; driverPodCreationGracePeriod only caps runSparkSubmit WaitDelay. Reject grace periods at or above 30s at startup. Co-authored-by: Cursor <cursoragent@cursor.com>
Purpose of this PR
Reduce duplicate driver submissions when the controller restarts mid-submit (refs #2788). Complementary to adoption in #2932: adoption recovers races this cannot avoid; this PR narrows the window by making in-flight
spark-submitcancellable on shutdown.On SIGTERM, controller-runtime cancels the reconcile context immediately. The post-submit status write uses that same context, so the real lever is interrupting the
spark-submitJVM before it creates a driver pod the controller will never record. PreviouslyrunSparkSubmitusedexec.Commandwithout context propagation.Proposed changes:
SparkSubmitter.runSparkSubmit: useexec.CommandContext, send SIGTERM on cancellation (not SIGKILL), and wait up toShutdownGracePeriodbefore force-killing the child.--driver-pod-creation-grace-periodforSparkSubmitter.ShutdownGracePeriod(WaitDelay after SIGTERM). LeaveManager.GracefulShutdownTimeoutat controller-runtime's 30s default (not overridden). No separate--graceful-shutdown-timeoutflag.controller.terminationGracePeriodSeconds(default60), must exceed the 30s manager shutdown window.driver-pod-creation-grace-period>= 30s so WaitDelay stays below GracefulShutdownTimeout.runSparkSubmit(SIGTERM on cancel, WaitDelay before force-kill) and chart deployment rendering.How the defaults were chosen
driverPodCreationGracePeriod10scmd.WaitDelayafter SIGTERM to spark-submitGracefulShutdownTimeout30s(controller-runtime default)terminationGracePeriodSeconds60Ordering:
driverPodCreationGracePeriod<GracefulShutdownTimeout(30s) <terminationGracePeriodSeconds.Typical
spark-submitin kubernetes mode withsubmission-wait-app-completion=falsetakes ~5-10s; worst case on a busy cluster ~15-20s. TunedriverPodCreationGracePeriodup if your cluster needs more headroom (must stay below 30s).Change Category
Rationale
Refs #2788. Complementary to #2932.
Checklist
Additional Notes
Locally validated:
go test ./internal/controller/sparkapplication/ -run TestRunSparkSubmit -timeout 120shelm unittest charts/spark-operator-chart -f 'tests/controller/deployment_test.yaml'