This is an automated email from the ASF dual-hosted git repository.
LuciferYang pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.x by this push:
new 1b5cab533ea4 [SPARK-58674][CORE] Assign a name to the error condition
`_LEGACY_ERROR_TEMP_3021-3023,3026,3029`
1b5cab533ea4 is described below
commit 1b5cab533ea4b3a7c400c0d9267b9b5e02f8fbaa
Author: YangJie <[email protected]>
AuthorDate: Mon Aug 10 12:53:32 2026 +0800
[SPARK-58674][CORE] Assign a name to the error condition
`_LEGACY_ERROR_TEMP_3021-3023,3026,3029`
### What changes were proposed in this pull request?
This PR converts five `_LEGACY_ERROR_TEMP_*` conditions in
`SparkCoreErrors` -- `_3021`, `_3022`, `_3023`, `_3026` and `_3029` -- into
proper error conditions, continuing the cleanup under
[SPARK-37935](https://issues.apache.org/jira/browse/SPARK-37935). Four get
user-facing names; one is an unreachable defensive check and becomes an
internal error. Note the set is not a contiguous range: `_3028` sits inside it
and is deliberately left out (see below).
| Legacy | Builder | Now | SQLSTATE |
|---|---|---|---|
| `_LEGACY_ERROR_TEMP_3021` |
`askStandaloneSchedulerToShutDownExecutorsError` |
`SCHEDULER_BACKEND_SHUTDOWN_FAILED.EXECUTORS` | 58030 |
| `_3022` | `stopStandaloneSchedulerDriverEndpointError` |
`SCHEDULER_BACKEND_SHUTDOWN_FAILED.DRIVER_ENDPOINT` | 58030 |
| `_3023` | `cannotRunSubmitMapStageOnZeroPartitionRDDError` |
`INTERNAL_ERROR` (entry deleted) | XX000 |
| `_3026` | `durationCalledOnUnfinishedTaskError` |
`UNSUPPORTED_CALL.TASK_NOT_FINISHED` | 0A000 |
| `_3029` | `clusterSchedulerError` | `CLUSTER_MANAGER_APPLICATION_FAILURE`
| 56000 |
`SCHEDULER_BACKEND_SHUTDOWN_FAILED` is a new umbrella: both conditions are
the same exception type thrown at the same altitude in adjacent methods of
`CoarseGrainedSchedulerBackend`, differing only in which RPC failed.
`TASK_NOT_FINISHED` joins the existing `UNSUPPORTED_CALL` umbrella, whose
message frame (`Cannot call the method "<methodName>" of the class
"<className>".`) is exactly the shape this throw site needs.
`durationCalledOnUnfinishedTaskError` now takes `className`/`methodName`
from its call site rather than deriving them internally, matching how the other
`UNSUPPORTED_CALL` throw sites build their parameters (`DiskBlockObjectWriter`,
`DataTypeErrors`). The no-arg form would have silently misreported the class if
a second caller ever appeared.
The remaining conditions in the `_3021-3042` range — `_3028` (inside this
PR's numeric span, hence the non-contiguous title), plus `_3033`, `_3035`,
`_3036`, `_3037` — are left for follow-up: they are operational failures (a
barrier stage getting partial offers, external shuffle service registration
exhausting its retries, a replica failing to store, a `DiskStore` file
vanishing) or reachable only through a third-party `ShuffleManager`, and each
needs its own reachability judgement.
### Reachability, per condition
- **`_3021`/`_3022` — cluster/environment failure, log-only.** Both wrap an
`askSync` to the driver endpoint during teardown, so they fire when an RPC
times out or the endpoint is already dead. Every path into
`CoarseGrainedSchedulerBackend.stop()` is wrapped in
`Utils.tryLogNonFatalError` (`SparkContext.stop` → `DAGScheduler.stop` →
`TaskSchedulerImpl.stop`), and `KubernetesClusterSchedulerBackend.stop` wraps
`super.stop()` explicitly, so the exception reaches a driver log and never
[...]
- **`_3023` — unreachable defensive check.** `DAGScheduler.submitMapStage`
rejects a dependency whose RDD has zero partitions. Its only production caller,
`ShuffleExchangeExec.mapOutputStatisticsFuture`, already short-circuits with
`if (inputRDD.getNumPartitions == 0) Future.successful(null)`, and the two
guards test the same number: the dependency's RDD is
`prepareShuffleDependency(inputRDD, ...)`'s output, derived through
`mapPartitionsWithIndexInternal`, which preserves partition c [...]
- **`_3026` — reachable from user code.** `TaskInfo` is `DeveloperApi` and
a `TaskInfo` for a *running* task is handed to listeners via
`SparkListenerTaskStart`. Any custom listener calling `taskInfo.duration` in
`onTaskStart` hits this. All in-tree callers are guarded or run after
`markFinished`, which is why it never fires in the build.
- **`_3029` — operator-facing, standalone only.** `TaskSchedulerImpl.error`
throws when the cluster manager reports a failure and no task set is active to
abort. Its only caller is `StandaloneSchedulerBackend.dead`, i.e. the
standalone Master removed the application or all Masters were unresponsive. The
condition name is deliberately deployment-agnostic because
`TaskSchedulerImpl.error` is, but today standalone is the only producer.
### Why are the changes needed?
The error-conditions
[README](https://github.com/apache/spark/blob/master/common/utils/src/main/resources/error/README.md)
disallows new `_LEGACY_ERROR_TEMP_*` entries and asks existing ones to be
resolved. This clears five of them.
Two of the five were also weak in their own right: `_3021`/`_3022` carried
no indication that the failure happens during shutdown, so an operator seeing
the log line had no way to tell whether the application's results were affected.
### Does this PR introduce _any_ user-facing change?
Yes, to error messages — no API change.
Converting any legacy condition changes the rendered string in two
mechanical ways: `SparkThrowableHelper.formatErrorMessage` suppresses the
`[CONDITION] ` prefix only for `_LEGACY_ERROR_`-prefixed names, and appends `
SQLSTATE: xxxxx` when a sqlState exists (legacy entries have none). Beyond that:
- `_3021`: `Error asking standalone scheduler to shut down executors` →
`Failed to shut down the scheduler backend while the application was
terminating: the driver could not confirm that the executors were asked to shut
down, so the later shutdown steps were skipped.` The added clause is factual:
`stop()` calls `stopExecutors()` unguarded, so a throw there skips
`stopTokenManager()` and the `StopDriver` ask. The wording says "could not
confirm" rather than "could not tell" because th [...]
- `_3022`: `Error stopping standalone scheduler's driver endpoint` → `... :
the driver endpoint did not stop cleanly.`
- `_3026`: `duration() called on unfinished task` → `Cannot call the method
"duration" of the class "org.apache.spark.scheduler.TaskInfo". The task has not
finished yet, so its duration is not available.`
- `_3029`: `Exiting due to error from cluster scheduler: <message>` →
`Exiting due to an error reported by the cluster manager: <message>`. This one
is genuinely user-visible in logs, so log-scraping on the old text would need
updating.
- `_3023` renders as an internal error. Note this does **not** change any
job-failure message shape: the throw happens on the driver during
`submitMapStage`, before `eventProcessLoop.post`, so
`DAGScheduler.abortStage`'s `isInternalError` filter — which only applies to
exceptions arriving from task-set failures — is not involved.
### How was this patch tested?
New assertions, four of which fail against the old code (the condition name
and the SQLSTATE both differ, since legacy entries carry no sqlState):
- `TaskInfoSuite` (new file) — `checkError` on
`UNSUPPORTED_CALL.TASK_NOT_FINISHED` including SQLSTATE and both message
parameters, plus a companion case asserting `duration` still works after
`markFinished`.
- `TaskSchedulerImplSuite` — `error()` with no active task set asserts
`CLUSTER_MANAGER_APPLICATION_FAILURE`; a companion case covers the other branch
(with an active task set it aborts rather than throwing) and asserts the reason
reaches the `DAGScheduler` intact.
- `KubernetesClusterSchedulerBackendSuite` — a failed `StopExecutors` RPC
asserts `SCHEDULER_BACKEND_SHUTDOWN_FAILED.EXECUTORS` and that the original
exception survives as the cause.
- `DAGSchedulerSuite` — `submitMapStage` on a zero-partition RDD asserts
`INTERNAL_ERROR`, so the converted branch is pinned rather than merely deleted
from the JSON.
Coverage caveat worth a reviewer's attention: the only assertion on
`SCHEDULER_BACKEND_SHUTDOWN_FAILED` lives in the `kubernetes` module and so
runs only under `-Pkubernetes`. Both throw sites are in `core`, but `core`'s
`CoarseGrainedSchedulerBackendSuite` uses a real `local-cluster` context with
no way to make `askSync` fail, whereas the k8s suite already has the mocked
`RpcEndpointRef` plumbing. The `DRIVER_ENDPOINT` subclass has no assertion for
the same reason — its throw site si [...]
Ran locally: `SparkThrowableSuite`, `TaskInfoSuite`,
`TaskSchedulerImplSuite`, `DAGSchedulerSuite` (367 tests) and
`KubernetesClusterSchedulerBackendSuite` (12 tests), all passing;
`core/compile`, `core/Test/compile` and `kubernetes/Test/compile` clean. The
JSON was regenerated with `SPARK_GENERATE_GOLDEN_FILES=1` and produced no diff.
Each new SQLSTATE assertion was verified to actually bite by temporarily
setting a wrong value and watching the test go red.
On the SQLSTATE choice for `SCHEDULER_BACKEND_SHUTDOWN_FAILED`: 58030 is
nominally "I/O error", and a failed RPC is not literally I/O. It was chosen
because 58030 is already Spark's de-facto system-operation-failure code,
carrying 11 conditions of which several are not literal I/O
(`FAILED_UPDATE_VIEW_SCHEMA`, `FAILED_TO_CREATE_PLAN_FOR_DIRECT_QUERY`,
`CANNOT_RESTORE_PERMISSIONS_FOR_PATH`), whereas `58000` ("System error") and
the `08xxx` connection class exist in `error-states.json` [...]
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.8)
Closes #57879 from LuciferYang/assign-name-legacy-3021-3037.
Authored-by: YangJie <[email protected]>
Signed-off-by: yangjie01 <[email protected]>
(cherry picked from commit bf03f518fdd65b32bff32adf9104dc0456e43a12)
Signed-off-by: yangjie01 <[email protected]>
---
.../src/main/resources/error/error-conditions.json | 54 ++++++++++++----------
.../org/apache/spark/errors/SparkCoreErrors.scala | 18 ++++----
.../org/apache/spark/scheduler/TaskInfo.scala | 3 +-
.../apache/spark/scheduler/DAGSchedulerSuite.scala | 16 +++++++
.../org/apache/spark/scheduler/TaskInfoSuite.scala | 53 +++++++++++++++++++++
.../spark/scheduler/TaskSchedulerImplSuite.scala | 24 ++++++++++
.../KubernetesClusterSchedulerBackendSuite.scala | 18 +++++++-
7 files changed, 150 insertions(+), 36 deletions(-)
diff --git a/common/utils/src/main/resources/error/error-conditions.json
b/common/utils/src/main/resources/error/error-conditions.json
index a575551475be..3813e0b546d2 100644
--- a/common/utils/src/main/resources/error/error-conditions.json
+++ b/common/utils/src/main/resources/error/error-conditions.json
@@ -1048,6 +1048,12 @@
],
"sqlState" : "42000"
},
+ "CLUSTER_MANAGER_APPLICATION_FAILURE" : {
+ "message" : [
+ "Exiting due to an error reported by the cluster manager: <message>"
+ ],
+ "sqlState" : "56000"
+ },
"CODEC_NOT_AVAILABLE" : {
"message" : [
"The codec <codecName> is not available."
@@ -6846,6 +6852,24 @@
],
"sqlState" : "21000"
},
+ "SCHEDULER_BACKEND_SHUTDOWN_FAILED" : {
+ "message" : [
+ "Failed to shut down the scheduler backend while the application was
terminating:"
+ ],
+ "subClass" : {
+ "DRIVER_ENDPOINT" : {
+ "message" : [
+ "the driver endpoint did not stop cleanly."
+ ]
+ },
+ "EXECUTORS" : {
+ "message" : [
+ "the driver could not confirm that the executors were asked to shut
down, so the later shutdown steps were skipped."
+ ]
+ }
+ },
+ "sqlState" : "58030"
+ },
"SCHEMA_ALREADY_EXISTS" : {
"message" : [
"Cannot create schema <schemaName> because it already exists.",
@@ -8350,6 +8374,11 @@
"The row shall have a schema to get an index of the field
<fieldName>."
]
},
+ "TASK_NOT_FINISHED" : {
+ "message" : [
+ "The task has not finished yet, so its duration is not available."
+ ]
+ },
"WITHOUT_SUGGESTION" : {
"message" : [
""
@@ -11449,36 +11478,11 @@
"Checkpoint dir must be specified."
]
},
- "_LEGACY_ERROR_TEMP_3021" : {
- "message" : [
- "Error asking standalone scheduler to shut down executors"
- ]
- },
- "_LEGACY_ERROR_TEMP_3022" : {
- "message" : [
- "Error stopping standalone scheduler's driver endpoint"
- ]
- },
- "_LEGACY_ERROR_TEMP_3023" : {
- "message" : [
- "Can't run submitMapStage on RDD with 0 partitions"
- ]
- },
- "_LEGACY_ERROR_TEMP_3026" : {
- "message" : [
- "duration() called on unfinished task"
- ]
- },
"_LEGACY_ERROR_TEMP_3028" : {
"message" : [
"<errorMsg>"
]
},
- "_LEGACY_ERROR_TEMP_3029" : {
- "message" : [
- "Exiting due to error from cluster scheduler: <message>"
- ]
- },
"_LEGACY_ERROR_TEMP_3033" : {
"message" : [
"Unable to register with external shuffle server due to : <message>"
diff --git a/core/src/main/scala/org/apache/spark/errors/SparkCoreErrors.scala
b/core/src/main/scala/org/apache/spark/errors/SparkCoreErrors.scala
index 873254e491f2..a114246c8978 100644
--- a/core/src/main/scala/org/apache/spark/errors/SparkCoreErrors.scala
+++ b/core/src/main/scala/org/apache/spark/errors/SparkCoreErrors.scala
@@ -201,13 +201,15 @@ private[spark] object SparkCoreErrors {
def askStandaloneSchedulerToShutDownExecutorsError(e: Exception): Throwable
= {
new SparkException(
- errorClass = "_LEGACY_ERROR_TEMP_3021", messageParameters = Map.empty,
cause = e
+ errorClass = "SCHEDULER_BACKEND_SHUTDOWN_FAILED.EXECUTORS",
+ messageParameters = Map.empty, cause = e
)
}
def stopStandaloneSchedulerDriverEndpointError(e: Exception): Throwable = {
new SparkException(
- errorClass = "_LEGACY_ERROR_TEMP_3022", messageParameters = Map.empty,
cause = e
+ errorClass = "SCHEDULER_BACKEND_SHUTDOWN_FAILED.DRIVER_ENDPOINT",
+ messageParameters = Map.empty, cause = e
)
}
@@ -242,9 +244,7 @@ private[spark] object SparkCoreErrors {
}
def cannotRunSubmitMapStageOnZeroPartitionRDDError(): Throwable = {
- new SparkException(
- errorClass = "_LEGACY_ERROR_TEMP_3023", messageParameters = Map.empty,
cause = null
- )
+ SparkException.internalError("Can't run submitMapStage on RDD with 0
partitions.")
}
def accessNonExistentAccumulatorError(id: Long): Throwable = {
@@ -260,8 +260,10 @@ private[spark] object SparkCoreErrors {
new TimeoutException(s"The event queue is not empty after $timeoutMillis
ms.")
}
- def durationCalledOnUnfinishedTaskError(): Throwable = {
- new SparkUnsupportedOperationException("_LEGACY_ERROR_TEMP_3026")
+ def durationCalledOnUnfinishedTaskError(className: String, methodName:
String): Throwable = {
+ new SparkUnsupportedOperationException(
+ errorClass = "UNSUPPORTED_CALL.TASK_NOT_FINISHED",
+ messageParameters = Map("className" -> className, "methodName" ->
methodName))
}
def sparkError(errorMsg: String): Throwable = {
@@ -274,7 +276,7 @@ private[spark] object SparkCoreErrors {
def clusterSchedulerError(message: String): Throwable = {
new SparkException(
- errorClass = "_LEGACY_ERROR_TEMP_3029",
+ errorClass = "CLUSTER_MANAGER_APPLICATION_FAILURE",
messageParameters = Map("message" -> message),
cause = null
)
diff --git a/core/src/main/scala/org/apache/spark/scheduler/TaskInfo.scala
b/core/src/main/scala/org/apache/spark/scheduler/TaskInfo.scala
index 9ed95870d240..1eca39c26fcc 100644
--- a/core/src/main/scala/org/apache/spark/scheduler/TaskInfo.scala
+++ b/core/src/main/scala/org/apache/spark/scheduler/TaskInfo.scala
@@ -149,7 +149,8 @@ class TaskInfo(
def duration: Long = {
if (!finished) {
- throw SparkCoreErrors.durationCalledOnUnfinishedTaskError()
+ throw SparkCoreErrors.durationCalledOnUnfinishedTaskError(
+ classOf[TaskInfo].getName, "duration")
} else {
finishTime - launchTime
}
diff --git
a/core/src/test/scala/org/apache/spark/scheduler/DAGSchedulerSuite.scala
b/core/src/test/scala/org/apache/spark/scheduler/DAGSchedulerSuite.scala
index a0428d8d8bd4..7d51ce58449c 100644
--- a/core/src/test/scala/org/apache/spark/scheduler/DAGSchedulerSuite.scala
+++ b/core/src/test/scala/org/apache/spark/scheduler/DAGSchedulerSuite.scala
@@ -8067,6 +8067,22 @@ class DAGSchedulerSuite extends SparkFunSuite with
TempLocalSparkContext with Ti
s"expected reason to mention '$reasonSubstring', got: $msg")
}
+ test("submitMapStage on an RDD with 0 partitions reports an internal error")
{
+ val shuffleMapRdd = new MyRDD(sc, 0, Nil)
+ val shuffleDep = new ShuffleDependency(shuffleMapRdd, new
HashPartitioner(1))
+ // `ShuffleExchangeExec` guards this case, so reaching it means an
internal invariant broke.
+ checkError(
+ exception = intercept[SparkException] {
+ scheduler.submitMapStage(shuffleDep, (_: MapOutputStatistics) => (),
CallSite("", ""),
+ new Properties())
+ },
+ condition = "INTERNAL_ERROR",
+ sqlState = Some("XX000"),
+ parameters = scala.collection.immutable.Map(
+ "message" -> "Can't run submitMapStage on RDD with 0 partitions."))
+ assertDataStructuresEmpty()
+ }
+
}
class DAGSchedulerAbortStageOffSuite extends DAGSchedulerSuite {
diff --git a/core/src/test/scala/org/apache/spark/scheduler/TaskInfoSuite.scala
b/core/src/test/scala/org/apache/spark/scheduler/TaskInfoSuite.scala
new file mode 100644
index 000000000000..7467f25f933d
--- /dev/null
+++ b/core/src/test/scala/org/apache/spark/scheduler/TaskInfoSuite.scala
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.spark.scheduler
+
+import org.apache.spark.{SparkFunSuite, SparkUnsupportedOperationException,
TaskState}
+
+class TaskInfoSuite extends SparkFunSuite {
+
+ private def newTaskInfo(): TaskInfo = new TaskInfo(
+ taskId = 42L,
+ index = 0,
+ attemptNumber = 0,
+ partitionId = 0,
+ launchTime = 100L,
+ executorId = "exec-1",
+ host = "host-1",
+ taskLocality = TaskLocality.PROCESS_LOCAL,
+ speculative = false)
+
+ test("duration is not available before the task finishes") {
+ val info = newTaskInfo()
+ checkError(
+ exception = intercept[SparkUnsupportedOperationException] {
+ info.duration
+ },
+ condition = "UNSUPPORTED_CALL.TASK_NOT_FINISHED",
+ sqlState = Some("0A000"),
+ parameters = Map(
+ "className" -> "org.apache.spark.scheduler.TaskInfo",
+ "methodName" -> "duration"))
+ }
+
+ test("duration is available once the task has finished") {
+ val info = newTaskInfo()
+ info.markFinished(TaskState.FINISHED, 300L)
+ assert(info.duration === 200L)
+ }
+}
diff --git
a/core/src/test/scala/org/apache/spark/scheduler/TaskSchedulerImplSuite.scala
b/core/src/test/scala/org/apache/spark/scheduler/TaskSchedulerImplSuite.scala
index 81782df283ee..118730d93168 100644
---
a/core/src/test/scala/org/apache/spark/scheduler/TaskSchedulerImplSuite.scala
+++
b/core/src/test/scala/org/apache/spark/scheduler/TaskSchedulerImplSuite.scala
@@ -2866,4 +2866,28 @@ class TaskSchedulerImplSuite extends SparkFunSuite with
LocalSparkContext
assert(taskScheduler.outstandingTasksForOtherWorkInProfile(defaultRp,
Set(0)) === 0)
}
+ test("error() with no active task sets reports a cluster manager failure") {
+ val taskScheduler = setupScheduler()
+ // No task set has been submitted, so `error` cannot abort anything and
throws instead.
+ checkError(
+ exception = intercept[SparkException] {
+ taskScheduler.error("Master removed our application: FAILED")
+ },
+ condition = "CLUSTER_MANAGER_APPLICATION_FAILURE",
+ sqlState = Some("56000"),
+ parameters = Map("message" -> "Master removed our application: FAILED"))
+ }
+
+ test("error() with an active task set aborts it instead of throwing") {
+ val taskScheduler = setupScheduler()
+ taskScheduler.submitTasks(FakeTask.createTaskSet(1))
+ val tsm = taskScheduler.taskSetManagerForAttempt(0, 0).get
+ taskScheduler.error("Master removed our application: FAILED")
+ assert(tsm.isZombie)
+ // The zombie flag cannot show whether the reason survived the trip, so
assert that the
+ // DAGScheduler was notified with the message verbatim.
+ assert(failedTaskSet)
+ assert(failedTaskSetReason === "Master removed our application: FAILED")
+ }
+
}
diff --git
a/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/scheduler/cluster/k8s/KubernetesClusterSchedulerBackendSuite.scala
b/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/scheduler/cluster/k8s/KubernetesClusterSchedulerBackendSuite.scala
index cf172eb096d4..97228871942a 100644
---
a/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/scheduler/cluster/k8s/KubernetesClusterSchedulerBackendSuite.scala
+++
b/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/scheduler/cluster/k8s/KubernetesClusterSchedulerBackendSuite.scala
@@ -31,14 +31,14 @@ import org.mockito.ArgumentMatchers.{any, eq => mockitoEq}
import org.mockito.Mockito.{atLeastOnce, mock, never, spy, verify, when}
import org.scalatest.BeforeAndAfter
-import org.apache.spark.{SparkConf, SparkContext, SparkEnv, SparkFunSuite}
+import org.apache.spark.{SparkConf, SparkContext, SparkEnv, SparkException,
SparkFunSuite}
import org.apache.spark.deploy.k8s.Config._
import org.apache.spark.deploy.k8s.Constants._
import org.apache.spark.deploy.k8s.Fabric8Aliases._
import org.apache.spark.resource.{ResourceProfile, ResourceProfileManager}
import org.apache.spark.rpc.{RpcCallContext, RpcEndpoint, RpcEndpointRef,
RpcEnv}
import org.apache.spark.scheduler.{ExecutorKilled, ExecutorLossReason,
LiveListenerBus, TaskSchedulerImpl}
-import
org.apache.spark.scheduler.cluster.CoarseGrainedClusterMessages.{RegisterExecutor,
RemoveExecutor, StopDriver}
+import
org.apache.spark.scheduler.cluster.CoarseGrainedClusterMessages.{RegisterExecutor,
RemoveExecutor, StopDriver, StopExecutors}
import org.apache.spark.scheduler.cluster.CoarseGrainedSchedulerBackend
import
org.apache.spark.scheduler.cluster.k8s.ExecutorLifecycleTestUtils.TEST_SPARK_APP_ID
@@ -306,6 +306,20 @@ class KubernetesClusterSchedulerBackendSuite extends
SparkFunSuite with BeforeAn
verify(kubernetesClient).close()
}
+ test("stopExecutors() reports a failed StopExecutors RPC as
SCHEDULER_BACKEND_SHUTDOWN_FAILED") {
+ val rpcFailure = new RuntimeException("StopExecutors timed out")
+
when(driverEndpointRef.askSync[Boolean](StopExecutors)).thenThrow(rpcFailure)
+ val e = intercept[SparkException] {
+ schedulerBackendUnderTest.stopExecutors()
+ }
+ checkError(
+ exception = e,
+ condition = "SCHEDULER_BACKEND_SHUTDOWN_FAILED.EXECUTORS",
+ sqlState = Some("58030"),
+ parameters = Map.empty[String, String])
+ assert(e.getCause === rpcFailure)
+ }
+
test("SPARK-34469: Ignore RegisterExecutor when SparkContext is stopped") {
when(sc.isStopped).thenReturn(true)
val endpoint = schedulerBackendUnderTest.createDriverEndpoint()
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]