Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring's @Scheduled cron job is triggering a few milliseconds before the scheduled time

I have setup Spring's @Scheduled with a cron expression for every hour as below where trend.olap.local.loading.cron.expression is 0 0 * * * ?.

@Scheduled(cron = "${trend.olap.local.loading.cron.expression}")
public void loadHoulyDataToLocalOlap() {
    try {
        // To calculate prev hour;
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.HOUR, -1);
        Date date = cal.getTime();
        int hour = cal.get(Calendar.HOUR_OF_DAY);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Loading hourly data into local olap :" + date
                    + ", and hour :" + hour);
        }

        dataIntegrationProcessor.loadHourlyDataToLocalOlap(hour);

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Loading hourly data into local olap :" + date
                    + ", and hour :" + hour + " Completed.");
        }
    } catch (Exception e) {
        LOGGER.error("Error occured at loadHoulyDataToLocalOlap", e);
    }
}

Here my intent is to take the hour value from current date and time and pass it to executor methods.

Basically I am taking current hour minus one so that at 17:00, the hour value should be 16.

But if you see the logs, the hour value is 15. This is because the Scheduler ran at around 16:59:59,831. See the log4j logs below. It Looks like the cron job is rounding off the milliseconds and getting triggered a few milliseconds before 17:00:00,000.

Because of this, I am getting wrong values and my business case is failing.

How do I make the cron run exactly at the zero'th milliseconds for every hour, instead of few milliseconds before?

DEBUG 2013-09-29 16:59:59,831 (TrendScheduler.java loadHoulyDataToLocalOlap:57) - Loading hourly data into local olap :Sun Sep 29 15:59:59 IST 2013, and hour :15
DEBUG 2013-09-29 16:59:59,831 (DataIntegrationProcessor.java loadHourlyDataToLocalOlap:57) - Loading hourly data for hour :15
INFO 2013-09-29 17:00:00,054 (KettleJobExecutor.java executeJob:73) - Job (24hr_populate_hour_data_job.kjb) executed successfully
DEBUG 2013-09-29 17:00:00,054 (TrendScheduler.java loadHoulyDataToLocalOlap:64) - Loading hourly data into local olap :Sun Sep 29 15:59:59 IST 2013, and hour :15 Completed.
like image 471
PShetty Avatar asked Sep 29 '13 12:09

PShetty


2 Answers

The short answer is that you can't with the cron option. The resolution of the cron expressions is to the second so it's going to round to the nearest second. You may be able to guarantee that it happens at least after the specific time but not exactly on it with cron.

Your best bet would be to implement your own Trigger. The Trigger interface has a single method that returns the java.util.Date for the next execution (which is in milliseconds). From there you can register the task and trigger as shown in the documentation here: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableScheduling.html

like image 178
Michael Minella Avatar answered Sep 20 '22 12:09

Michael Minella


If you intent is to insert the correct hour value, the I think better solution would be to calculate the hour based on the less predictable cron schedule. As long as it falls within a second, you could simply get currenttime and convert it to the nearest hour, and then derive the hour value from there. You could also add warnings/alerts to let you know if the cron is behaving erratic by listening in on tasks that start more than a minute late, or 10 minutes late, etc.

Although you could adjust the cron schedule instead, I feel that's more of a hack than handling it in code since you are approximating an approximation at that point. You have more control in code to handle more than a simple hundred millisecond offset

like image 29
coffeeaddict Avatar answered Sep 18 '22 12:09

coffeeaddict