Class RoundRobinRetryScheduler

java.lang.Object
com.broadleafcommerce.common.messaging.notification.RoundRobinRetryScheduler
All Implemented Interfaces:
org.springframework.beans.factory.Aware, org.springframework.context.ApplicationEventPublisherAware

public class RoundRobinRetryScheduler extends Object implements org.springframework.context.ApplicationEventPublisherAware
A specialized scheduler designed to manage the execution of multiple RetryHandler instances in a fair, round-robin fashion using a fixed-size thread pool.

Problem Statement:
In a system with many distinct RetryHandler instances (often one per repository/message type combination), relying on individual threads or standard thread pools can lead to two main issues:

  • Resource Exhaustion: Creating a dedicated thread or scheduler for each handler scales poorly as the number of handlers grows.
  • Starvation / Unfairness: If handlers share a common thread pool, a "noisy" handler (one with a very short retry interval or long processing time) can flood the task queue, preventing other handlers from getting CPU time.

Solution:
This scheduler addresses these concerns by decoupling the scheduling of tasks from their execution.

  • Fixed Resource Usage: It uses a single ThreadPoolTaskExecutor with a fixed maximum pool size (configurable via properties). This puts a hard cap on the number of concurrent retry threads, regardless of how many handlers are registered.
  • Fairness via Round-Robin: A lightweight "heartbeat" thread runs periodically (e.g., every 100ms) to check for ready handlers. It iterates through the registered handlers in a round-robin order. If a handler is ready to run (its interval has elapsed) and is not currently running, it is submitted to the worker pool.
  • No Monopolization: Because the scheduler iterates through the list and only submits one task per handler at a time, a single handler cannot fill the worker pool with its own tasks. Even if a handler is ready to run every 1ms, the scheduler will visit all other handlers before considering it again.
  • Non-Blocking: If the worker pool is full, the scheduler simply skips the current cycle and tries again on the next heartbeat, ensuring that the scheduling logic itself never blocks.

Burst Mode:
To handle scenarios where a specific handler has a large backlog of records (e.g., after a prolonged outage), this scheduler supports a "Burst Mode".

  • Trigger: When a RetryHandler detects that it has processed a full page of records (indicating more data is likely available), it can request a burst execution.
  • Dedicated Resources: Burst requests are submitted to a separate, dedicated ThreadPoolTaskExecutor (the "Burst Pool"). This ensures that high-load handlers can drain their backlogs quickly without consuming threads from the main "Fair Pool" and starving other handlers.
  • Mechanism: If the Burst Pool has capacity, the handler is scheduled to run again after a short delay (the "Burst Interval"). While bursting, the handler is marked as "running" so the main round-robin loop skips it, preventing double execution. If the Burst Pool is full, the request is ignored, and the handler falls back to the standard round-robin schedule.

  • Constructor Details

    • RoundRobinRetryScheduler

      public RoundRobinRetryScheduler(org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor workerPool, org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor burstPool, boolean enabled)
  • Method Details

    • setApplicationEventPublisher

      public void setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher applicationEventPublisher)
      Specified by:
      setApplicationEventPublisher in interface org.springframework.context.ApplicationEventPublisherAware
    • registerHandler

      public void registerHandler(RetryHandler handler, long initialDelaySeconds, long intervalSeconds)
    • unregisterHandler

      public void unregisterHandler(RetryHandler handler)
    • requestBurst

      public void requestBurst(RetryHandler handler, Duration burstInterval)
    • shutdown

      @PreDestroy public void shutdown()