[SPARK-58616][CORE][PYTHON] Attribute a reason for context-wide job cancellation in cancelAllJobs - #57824
Open
ganeshashree wants to merge 1 commit into
Open
[SPARK-58616][CORE][PYTHON] Attribute a reason for context-wide job cancellation in cancelAllJobs#57824ganeshashree wants to merge 1 commit into
ganeshashree wants to merge 1 commit into
Conversation
…ancellation in cancelAllJobs ### What changes were proposed in this pull request? When a context-wide cancellation fires, every job caught by it fails with the fixed message `Job <id> cancelled as part of cancellation of all jobs`. That says what happened but not who triggered it or why, and it is reported identically on jobs that were healthy and are only being aborted as collateral. This threads an optional reason through the context-wide cancel path (`SparkContext.cancelAllJobs` -> `DAGScheduler.cancelAllJobs` -> `AllJobsCancelled` -> `doCancelAllJobs` -> `handleJobCancellation`) so it surfaces in the `SPARK_JOB_CANCELLED` error of every cancelled job. This mirrors what SPARK-48900 already did for `cancelJobGroup` and `cancelJobsWithTag`. - `AllJobsCancelled` gains `reason: Option[String]` (default `None`), matching the sibling events `JobCancelled` / `JobGroupCancelled` / `JobTagCancelled`. - `doCancelAllJobs` falls back to `DAGScheduler.DEFAULT_CANCEL_ALL_JOBS_REASON`, the historical wording verbatim, so the default-path message is byte-identical. - New `cancelAllJobs(reason: String)` overloads on `SparkContext` and `JavaSparkContext`, following the shape of the existing `cancelJobGroup` / `cancelJobsWithTag` reason overloads. The no-arg versions are unchanged, so this is additive and binary-compatible. - PySpark's `SparkContext.cancelAllJobs` gains an optional `reason` parameter that delegates to the new JVM overload, so Python callers can attribute a cancel too. - `cancelAllJobs` now also logs that it was invoked, which its siblings already did and it did not. - Internal callers now pass a reason: the REPL SIGINT handler, the Spark SQL CLI SIGINT handler, and the DAGScheduler event-loop `onError` shutdown path. The error-message template is already `Job <jobId> cancelled <reason>`, so no `error-conditions.json` change is needed. ### Why are the changes needed? A cancellation with no attribution cannot be told apart from a genuine query failure when reading logs, which makes investigating this class of failure slow. ### Does this PR introduce _any_ user-facing change? Yes, additively. New `cancelAllJobs(reason)` overloads in Scala/Java and a new optional `reason` parameter in PySpark. The error condition (`SPARK_JOB_CANCELLED`) and SQLSTATE (`XXKDA`) are unchanged, and callers that supply no reason still produce the original message text. ### How was this patch tested? - Two new cases in `DAGSchedulerSuite` covering both branches of the fallback, asserting via `checkError` on condition, SQLSTATE, and message parameters. `DAGSchedulerSuite` and `JobCancellationSuite` pass in full (234 tests). - A new end-to-end case in `python/pyspark/tests/test_context.py` that cancels a real running job and asserts the supplied reason reaches the job's error while the generic "as part of cancellation of all jobs" does not appear. Confirmed to fail when the reason is not forwarded to the JVM. Co-authored-by: Isaac
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.
What changes were proposed in this pull request?
cancelAllJobsis the only cancellation entry point with no way to say why the cancel happened. Every job it aborts fails with the same fixed message:This threads an optional reason through the path
SparkContext.cancelAllJobs->DAGScheduler.cancelAllJobs->AllJobsCancelled->doCancelAllJobs->handleJobCancellation, so it lands in theSPARK_JOB_CANCELLEDerror of every cancelled job. This follows the shape SPARK-48900 established forcancelJobGroupandcancelJobsWithTag.AllJobsCancelledgainsreason: Option[String](defaultNone), matching the sibling eventsJobCancelled/JobGroupCancelled/JobTagCancelled.cancelAllJobs(reason: String)overloads onSparkContextand JavaSparkContext; PySpark'scancelAllJobsgains an optionalreason`. The existing no-arg forms are untouched, so the change is purely additive.doCancelAllJobsfalls back toDAGScheduler.DEFAULT_CANCEL_ALL_JOBS_REASON— the historical wording verbatim, so the default-path message is byte-identical.cancelAllJobsnow logs that it was invoked, which its siblings already did.DAGSchedulerevent-looponErrorshutdown path.The message template is already
Job <jobId> cancelled <reason>, soerror-conditions.jsonis unchanged.Why are the changes needed?
The fixed message says what happened but not who triggered it or why, and it is reported identically on jobs that were perfectly healthy and are only being aborted as collateral of someone else's cancel. When reading logs there is no way to tell a context-wide cancellation from a genuine failure of the job in question, which makes this class of failure slow to investigate.
Does this PR introduce any user-facing change?
Yes, additively:
cancelAllJobs(reason)in Scala and Java, and a new optionalreasonparameter in PySpark.SPARK_JOB_CANCELLEDbecomes caller-dependent for the internal callers that now supply one. A shell interrupt previously producedJob 7 cancelled as part of cancellation of all jobs; it now producesJob 7 cancelled because the driver process received an interrupt signal (SIGINT).Callers that supply no reason still produce the original text, and the error condition (
SPARK_JOB_CANCELLED) and SQLSTATE (XXKDA) are unchanged, so consumers matching on the error class are unaffected. A consumer string-matching the full message of an attributed cancel would see the new wording.How was this patch tested?
DAGSchedulerSuitecovering both branches of the fallback, asserting viacheckErroron condition, SQLSTATE, and message parameters.DAGSchedulerSuiteandJobCancellationSuitepass in full (234 tests).python/pyspark/tests/test_context.pythat cancels a real running job with a reason and asserts it reaches the job's error while the generic wording does not appear, this covers the py4j round trip that unit-level stubs cannot. Confirmed to fail when the reason is not forwarded to the JVM.Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 5)