Home > database >  Spring @Scheduled job that will run every second but at a specific number of milliseconds after the
Spring @Scheduled job that will run every second but at a specific number of milliseconds after the

Time:01-27

I'm trying to run a scheduled job in spring boot that will run every second. That in itself is easy with a cron job or with the delay/rate attributes. The issue is that I don't want it to run on the second. I want it to run at a specified number of milliseconds after the second.

For example, run every second at 900 ms past the second. So the logs would look like this:
System started at 20:00:00:000.
20:00:00:900 - log
20:00:01:900 - log
20:00:02:900 - log

It's important that it is not dependent on when the system starts. It has to be at that specified time every second.

Cron jobs are too imprecise to be able to do this but surely something already exists that can do this? The top answer in the following thread mentions creating a custom Trigger for that issue. Would that be a possible for this? Spring's @Scheduled cron job is triggering a few milliseconds before the scheduled time

CodePudding user response:

Cron supports resolution up to the second, so i think the only option is implementing Trigger on your own, as mentioned in other question.

My idea is to write a trigger, which recalculates the next execution time with the required milliseconds after the second. Somewhat naive implementation, just to illustrate the idea, by reusing spring's CronTrigger, may look like this:

public class DelayingCronTrigger extends CronTrigger {

  private final long delayMs;

  public DelayingCronTrigger(String expression, long delayMs) {
    super(expression);
    this.delayMs = delayMs;
  }

  @Override
  public Date nextExecutionTime(TriggerContext triggerContext) {
    Date next = super.nextExecutionTime(triggerContext);
    if (next == null) {
      return null;
    }
    long delayedNext = next.getTime()   this.delayMs;
    return new Date(delayedNext);
  }
}

Keep in mind that the parent class implementation i reuse here is based on the completion time of the previous execution in order to avoid overlapping executions. If the actual tasks you are running take longer, you may need to rewrite the logic from scratch basing it on lastScheduledExecutionTime() or lastActualExecutionTime().

After implementing the required trigger, you need to register the tasks programmatically:

@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {

  @Override
  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    String cron = "* * * ? * *";
    long delayMs = 900;
    taskRegistrar.addTriggerTask(() -> System.out.println("Time - "   LocalDateTime.now()), new DelayingCronTrigger(cron, delayMs));
  }
}
  • Related