sparkconnect: SIGTERM the foreground JVM in preStop instead of calling stop-connect-server.sh#2920
sparkconnect: SIGTERM the foreground JVM in preStop instead of calling stop-connect-server.sh#2920SAY-5 wants to merge 1 commit 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! |
There was a problem hiding this comment.
Pull request overview
Updates Spark Connect server pod shutdown behavior so the foreground JVM receives SIGTERM and can run Spark’s normal shutdown hooks (finalizing event logs, draining listeners), addressing #2903.
Changes:
- Replace
stop-connect-server.shin the podpreStophook with apkill -TERMtargetingSparkConnectServer. - Add detailed inline rationale explaining why daemon-style stop scripts fail with
SPARK_NO_DAEMONIZE=true.
Comments suppressed due to low confidence (1)
internal/controller/sparkconnect/reconciler.go:436
- There are existing unit tests for reconciler.go behavior (e.g.,
mutateServerService), but this change tomutateServerPodisn’t covered. Adding a test that asserts the generated container lifecycle preStop command matches the expected shutdown behavior would help prevent regressions (especially around the exact signal command/regex).
container.Lifecycle = &corev1.Lifecycle{
PreStop: &corev1.LifecycleHandler{
Exec: &corev1.ExecAction{
Command: []string{
"bash",
"-c",
"pkill -TERM -f org.apache.spark.sql.connect.service.SparkConnectServer || true",
},
},
},
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| container.Lifecycle = &corev1.Lifecycle{ | ||
| PreStop: &corev1.LifecycleHandler{ | ||
| Exec: &corev1.ExecAction{ | ||
| Command: []string{ | ||
| "bash", | ||
| "-c", | ||
| "${SPARK_HOME}/sbin/stop-connect-server.sh", | ||
| "pkill -TERM -f org.apache.spark.sql.connect.service.SparkConnectServer || true", | ||
| }, |
There was a problem hiding this comment.
The preStop hook sends SIGTERM and exits immediately. Kubernetes runs preStop before sending SIGTERM to PID 1, so right after this hook completes kubelet will SIGTERM the container’s PID 1 (currently bash -c ...), which can terminate the container while the JVM is still shutting down and prevent the graceful shutdown you’re trying to enable. Consider having preStop wait/poll until the SparkConnectServer process exits (bounded by the termination grace period), or restructure the container entrypoint so the JVM becomes PID 1 and receives kubelet’s SIGTERM directly.
| "bash", | ||
| "-c", | ||
| "${SPARK_HOME}/sbin/stop-connect-server.sh", | ||
| "pkill -TERM -f org.apache.spark.sql.connect.service.SparkConnectServer || true", |
There was a problem hiding this comment.
pkill -f treats the pattern as a regular expression; the unescaped dots in org.apache.spark.sql.connect.service.SparkConnectServer will match any character, which can unintentionally match and signal unrelated processes. Escape the dots (or otherwise anchor/constrain the regex) so it only targets the intended JVM command line.
| "pkill -TERM -f org.apache.spark.sql.connect.service.SparkConnectServer || true", | |
| `pkill -TERM -f 'org\.apache\.spark\.sql\.connect\.service\.SparkConnectServer' || true`, |
| "bash", | ||
| "-c", | ||
| "${SPARK_HOME}/sbin/stop-connect-server.sh", | ||
| "pkill -TERM -f org.apache.spark.sql.connect.service.SparkConnectServer || true", |
There was a problem hiding this comment.
|| true will also mask cases where pkill is missing from the image (exit 127), leading to silent non-graceful shutdowns. Consider guarding with a command -v pkill check (and/or only tolerating the "no process matched" exit code) so missing tooling is surfaced in logs.
| "pkill -TERM -f org.apache.spark.sql.connect.service.SparkConnectServer || true", | |
| "if ! command -v pkill >/dev/null 2>&1; then echo 'pkill not found; cannot gracefully stop Spark Connect server' >&2; exit 127; fi; pkill -TERM -f org.apache.spark.sql.connect.service.SparkConnectServer; status=$?; if [ \"$status\" -ne 0 ] && [ \"$status\" -ne 1 ]; then exit \"$status\"; fi", |
0e953b6 to
75f9df7
Compare
…g stop-connect-server.sh
The SparkConnect server container is started with SPARK_NO_DAEMONIZE=true
(foreground mode), so spark-daemon.sh does not write the PID file that
${SPARK_HOME}/sbin/stop-connect-server.sh uses to locate the process.
The preStop hook therefore always failed with `no org.apache.spark.sql.
connect.service.SparkConnectServer to stop`, and the JVM was left to be
SIGKILLed at the end of the termination grace period -- event logs
never finalized from `.inprogress`, the run showed as incomplete in
Spark History Server, and any other shutdown hooks ran only partially.
Replace the stop-connect-server.sh invocation with a targeted pkill
-TERM against the server class. That delivers SIGTERM to the
foreground JVM, which triggers Spark's normal shutdown hook so event
logs finalize and the listener bus drains before the container exits.
Fixes kubeflow#2903.
Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com>
Signed-off-by: Sai Asish Y <say.apm35@gmail.com>
75f9df7 to
3325ca9
Compare
Description
The SparkConnect server container is started with
SPARK_NO_DAEMONIZE=true(foreground mode), sospark-daemon.shdoes not write the PID file that${SPARK_HOME}/sbin/stop-connect-server.shuses to locate the process. ThepreStophook therefore always fails with:The JVM is left to be SIGKILLed at the end of the termination grace period, event logs never finalize from
.inprogress, the run shows as incomplete in Spark History Server, and any other shutdown hooks only run partially.Fix
Replace the
stop-connect-server.shinvocation with a targetedpkill -TERMagainst the server class. That delivers SIGTERM to the foreground JVM, which triggers Spark's normal shutdown hook so event logs finalize and the listener bus drains before the container exits.Fixes #2903.
Test
go build ./internal/controller/sparkconnect/...green.go test ./internal/controller/sparkconnect/...failure is pre-existing (envtest required) and unchanged by this PR (confirmed by stashing the diff and rerunning).Signed-off-by: SAY-5 SAY-5@users.noreply.github.com