Skip to content

[WIP] feat: schedule polling and retries on managed executors instead of Timers - #3620

Draft
csviri wants to merge 1 commit into
nextfrom
scheduled-executor-service
Draft

csviri wants to merge 1 commit into
nextfrom
scheduled-executor-service

Conversation

@csviri

@csviri csviri commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

PollingEventSource and TimerEventSource each created a java.util.Timer,
and PerResourcePollingEventSource a ScheduledThreadPoolExecutor of its
own, so every polling event source and every controller cost a thread
that the operator neither sized nor shut down.

They now schedule on executors managed by ExecutorServiceManager, which
builds them from ConfigurationService:

  • getScheduledExecutorService() backs the polling event sources
  • getRetryAndRescheduleExecutorService() backs the retry and reschedule
    timer of every controller, kept separate so that a slow poll cannot
    delay a retry

Both are sized by concurrentScheduledTaskThreads() (4 by default) and
concurrentRetryAndRescheduleThreads() (2 by default), overridable
programmatically or through the josdk.scheduled-tasks.concurrent-threads
and josdk.retry-and-reschedule.concurrent-threads keys. Both default to
daemon threads, discard the tasks scheduled for later on shutdown so
they don't hold up the termination of the operator, and drop cancelled
tasks eagerly. The previous Executors.newScheduledThreadPool(0) was
effectively single threaded and created non daemon threads.

An event source resolves its executor on every start rather than at
creation time, since the manager replaces its pools when the operator is
restarted, and only shuts one down if it created it itself. That also
fixes PerResourcePollingEventSource shutting down a user supplied
executor and not registering its tasks again after a restart.

PollingEventSource gains a constructor taking an EventSourceContext, the
one without it is deprecated, and both polling configurations accept an
executor to run a single event source on a pool of its own.

…mers

PollingEventSource and TimerEventSource each created a java.util.Timer,
and PerResourcePollingEventSource a ScheduledThreadPoolExecutor of its
own, so every polling event source and every controller cost a thread
that the operator neither sized nor shut down.

They now schedule on executors managed by ExecutorServiceManager, which
builds them from ConfigurationService:

- getScheduledExecutorService() backs the polling event sources
- getRetryAndRescheduleExecutorService() backs the retry and reschedule
  timer of every controller, kept separate so that a slow poll cannot
  delay a retry

Both are sized by concurrentScheduledTaskThreads() (4 by default) and
concurrentRetryAndRescheduleThreads() (2 by default), overridable
programmatically or through the josdk.scheduled-tasks.concurrent-threads
and josdk.retry-and-reschedule.concurrent-threads keys. Both default to
daemon threads, discard the tasks scheduled for later on shutdown so
they don't hold up the termination of the operator, and drop cancelled
tasks eagerly. The previous Executors.newScheduledThreadPool(0) was
effectively single threaded and created non daemon threads.

An event source resolves its executor on every start rather than at
creation time, since the manager replaces its pools when the operator is
restarted, and only shuts one down if it created it itself. That also
fixes PerResourcePollingEventSource shutting down a user supplied
executor and not registering its tasks again after a restart.

PollingEventSource gains a constructor taking an EventSourceContext, the
one without it is deprecated, and both polling configurations accept an
executor to run a single event source on a pool of its own.
Copilot AI lite review requested due to automatic review settings September 17, 2026 07:45
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Sep 17, 2026
@openshift-ci
openshift-ci Bot requested review from metacosm and xstefank September 17, 2026 07:45
@csviri
csviri marked this pull request as draft September 17, 2026 07:45
@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Retain the removed public constant and prevent late polling tasks from re-registering after shutdown.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Refactors polling, retry, and rescheduling onto configurable operator-managed executors, with lifecycle improvements, tests, and documentation.

Changes:

  • Adds configurable scheduled-task and retry/reschedule executor pools.
  • Updates timer and polling event sources for shared or custom executors.
  • Adds configuration, lifecycle tests, and documentation.
  • Two critical issues remain unresolved: API compatibility and a polling restart race.
File summaries
File Description
operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java Updates test construction for the context-aware API.
operator-framework/src/test/java/io/javaoperatorsdk/operator/config/loader/ConfigLoaderTest.java Tests executor configuration loading.
operator-framework/src/main/java/io/javaoperatorsdk/operator/config/loader/ConfigLoader.java Loads executor thread settings.
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSourceTest.java Tests timer executor behavior and lifecycle.
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java Tests polling cancellation and shared scheduling.
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ExecutorServiceManagerTest.java Tests executor lifecycle management.
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverriderTest.java Tests executor overrides and defaults.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSource.java Replaces timers with scheduled executors.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java Supports managed and custom polling executors.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java Adds polling executor configuration.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java Stores polling executor configuration.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java Migrates per-resource polling to managed executors.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java Updates per-resource executor configuration.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java Injects retry executor suppliers.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java Connects controllers to managed retry executors.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java Passes event-source context to polling sources.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java Provides daemon scheduled-executor utilities.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ExecutorServiceManager.java Manages scheduled executor lifecycles.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java Supports executor customization.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java Defines executor defaults and factories.
docs/content/en/docs/documentation/operations/configuration.md Documents executor configuration keys.
docs/content/en/docs/documentation/eventing.md Documents polling and retry executor behavior.
Review details
  • Files reviewed: 22/22 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

executorService == null
? new ScheduledThreadPoolExecutor(DEFAULT_EXECUTOR_THREAD_NUMBER)
: executorService;
this.executorService = executorService;
Comment on lines +250 to +253
// the tasks have to be cancelled explicitly now that the executor can be shared with the rest
// of the operator, and the map cleared so that they are registered again on a restart
scheduledFutures.values().forEach(future -> future.cancel(true));
scheduledFutures.clear();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants