dongjoon-hyun opened a new pull request, #59712:
URL: https://github.com/apache/spark/pull/59712
### What changes were proposed in this pull request?
This PR aims to make `TaskSchedulerImpl.stop` discard the pending tasks of
`abortTimer` without waiting, by calling `shutdownNow()` instead of
`ThreadUtils.shutdown`.
```diff
ThreadUtils.shutdown(starvationTimer)
- ThreadUtils.shutdown(abortTimer)
+ // Discard pending abort tasks, delayed by
`UNSCHEDULABLE_TASKSET_TIMEOUT`, without waiting.
+ abortTimer.shutdownNow()
```
When excludeOnFailure is enabled and a task set becomes unschedulable
because all executors are excluded, `TaskSchedulerImpl` puts a one-shot task
into `abortTimer` with the delay of
`spark.scheduler.excludeOnFailure.unschedulableTaskSetTimeout` (default:
`120s`). The task is not cancelled when the task set becomes schedulable again,
so it stays in the queue until the delay elapses.
`DAGScheduler.stop` stops its `messageScheduler`, which also holds one-shot
delayed tasks, with `shutdownNow()` in the same way. If an abort task happens
to be running, `shutdownNow()` interrupts it, but the task only acquires the
scheduler lock and posts an event to the `DAGScheduler`, and neither is
interruptible.
### Why are the changes needed?
Since Apache Spark
[v4.0.0](https://github.com/apache/spark/releases/tag/v4.0.0) (2025-05-19),
`SparkContext.stop` can take up to 30 seconds longer because
`TaskSchedulerImpl.stop` waits for such a pending abort task.
Previously, `abortTimer` was a `java.util.Timer`, and `stop` called
`abortTimer.cancel()`, which discards all scheduled tasks without waiting.
SPARK-46895 (apache/spark#44718) replaced it with a
`ScheduledThreadPoolExecutor` and `ThreadUtils.shutdown(abortTimer)`. However,
a `ScheduledThreadPoolExecutor` still executes the existing delayed tasks after
`shutdown()` by default, so `ThreadUtils.shutdown` waits for its whole
30-second grace period.
The existing test cases are affected too. Before this PR, `afterEach` of
`TaskSchedulerImplSuite` waited 70 seconds in total for the pending abort tasks
of the following test cases.
- `SPARK-22148 try to acquire a new executor when task is unschedulable with
1 executor`: 10 seconds until its 10-second abort task runs.
- `SPARK-22148 abort timer should clear unschedulableTaskSetToExpiryTime for
all TaskSets`: 60 seconds because `TaskSchedulerImpl.stop` is called twice,
directly and via `DAGScheduler.stop`.
### Does this PR introduce _any_ user-facing change?
Yes, `SparkContext.stop` no longer waits up to 30 seconds for the pending
abort timer tasks. This restores the behavior of Apache Spark 3.5. It only
matters when excludeOnFailure is enabled and a task set became unschedulable
within `spark.scheduler.excludeOnFailure.unschedulableTaskSetTimeout` before
stopping.
### How was this patch tested?
Pass the CIs with the newly added test case.
Without this fix, the new test case fails because `stop` waits for 30
seconds.
```
[info] - SPARK-60474: stop should not wait for pending abort timer tasks ***
FAILED *** (30 seconds, 60 milliseconds)
[info] 30005183833 was not less than 10000000000
(TaskSchedulerImplSuite.scala:1173)
```
With this fix, `TaskSchedulerImplSuite` passes in about 7 seconds. Without
this fix, the existing test cases alone took about 77 seconds.
```
$ build/sbt "core/testOnly org.apache.spark.scheduler.TaskSchedulerImplSuite"
...
[info] - SPARK-60474: stop should not wait for pending abort timer tasks (27
milliseconds)
...
[info] Run completed in 6 seconds, 739 milliseconds.
[info] Total number of tests run: 122
[info] Tests: succeeded 122, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
```
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Opus 5.5
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]