Skip to content

Add task startup overrun warning to ThreadPoolTaskScheduler#36963

Open
itsmehotpants wants to merge 1 commit into
spring-projects:mainfrom
itsmehotpants:feature/task-scheduler-overrun-warning
Open

Add task startup overrun warning to ThreadPoolTaskScheduler#36963
itsmehotpants wants to merge 1 commit into
spring-projects:mainfrom
itsmehotpants:feature/task-scheduler-overrun-warning

Conversation

@itsmehotpants

Copy link
Copy Markdown

What this PR does

Closes #33856

Adds a configurable taskStartupOverrunThreshold property to ThreadPoolTaskScheduler. When set, the scheduler emits a WARN-level log message whenever a scheduled task starts significantly later than its intended fire time, making it straightforward to diagnose thread starvation caused by a too-small thread pool or long-running tasks holding scheduler threads.


Motivation

A common Spring Boot gotcha: spring.task.scheduling.pool.size defaults to 1. If any scheduled task runs long, all subsequent tasks queue up behind it and start late — sometimes by seconds or minutes. Currently this is silent; there is no built-in diagnostic. Users discover the problem only after significant delay or by reading the documentation carefully.

This PR introduces an opt-in warning threshold so the issue surfaces immediately in logs.


Changes

ThreadPoolTaskScheduler

Addition Detail
taskStartupOverrunThreshold field volatile @Nullable Duration, null by default (warnings disabled unless opted in)
setTaskStartupOverrunThreshold(Duration) Setter with full Javadoc; accepts null to disable
getTaskStartupOverrunThreshold() Accessor
beforeExecute(Thread, Runnable) override Checks RunnableScheduledFuture.getDelay(NANOSECONDS); logs WARN when overrun ≥ threshold

The hook uses the already-wired beforeExecute callback that the anonymous ScheduledThreadPoolExecutor subclass created in createExecutor() delegates to, so no changes to the executor creation logic are needed.

ThreadPoolTaskSchedulerOverrunTests (new)

7 JUnit 5 tests covering:

  • Default threshold is null
  • Getter/setter round-trip
  • Reset to null
  • Normal execution with threshold set
  • Normal execution without threshold
  • Overrun detection under thread starvation (pool size 1, blocking first task)
  • No spurious warnings when threads are sufficient (pool size 2)

Usage

// Via API
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(1);
scheduler.setTaskStartupOverrunThreshold(Duration.ofSeconds(1));
scheduler.initialize();

// Or via Spring Boot configuration (with a custom customizer)

When a task starts 1 450 ms after its scheduled time:

WARN --- ThreadPoolTaskScheduler : Task [org.springframework.scheduling...] is starting 1450ms late
         (threshold: 1000ms). Consider increasing the thread pool size or reducing task duration.

Checklist

  • Unassigned issue with no maintainer claim
  • Opt-in by default (null threshold = no change in behaviour for existing users)
  • Uses the inherited logger from ExecutorConfigurationSupport (no new dependency)
  • 7 integration tests
  • Full Javadoc on all new public API

Closes spring-projects#33856

Adds a configurable `taskStartupOverrunThreshold` property to
ThreadPoolTaskScheduler. When set, the scheduler checks in
`beforeExecute()` whether a RunnableScheduledFuture is starting
significantly later than its intended fire time, and logs a WARN
message that includes the actual overrun in milliseconds and the
configured threshold.

This makes it straightforward to diagnose thread starvation caused
by a single-threaded pool (the common default) being held by a
long-running task, preventing subsequent tasks from starting on time.

Changes
-------
* New `taskStartupOverrunThreshold` volatile field (null by default,
  meaning warnings are disabled unless opted in).
* `setTaskStartupOverrunThreshold(Duration)` setter with full Javadoc.
* `getTaskStartupOverrunThreshold()` accessor.
* `beforeExecute(Thread, Runnable)` override that inspects the task's
  remaining delay via `RunnableScheduledFuture.getDelay(NANOSECONDS)`
  and logs a WARN when the overrun meets or exceeds the threshold.
* 7 JUnit 5 integration tests covering: default/null threshold,
  getter/setter round-trip, reset to null, normal execution with and
  without threshold, overrun detection under thread starvation, and
  no spurious warnings when threads are sufficient.
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jun 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged or decided on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Emit warning when ThreadPoolTaskScheduler is unable to meet task delay

2 participants