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

3

9

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.
Leola answered 29/9, 2013 at 12:58 Comment(1)
One solution is to put Thread.sleep(1000) inside loadHoulyDataToLocalOlap() method at the beginning. Is there any better solution than this?Leola
M
6

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

Methodist answered 19/8, 2014 at 16:39 Comment(0)
R
1

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

Renege answered 23/8, 2014 at 0:19 Comment(0)
T
0

Cron is going to ignore milliseconds and by implication only be accurate to within a second, assuming there aren't any other timing problems like a sudden load on the system.

You've got two choices: 1. Start the cron job a few second after the hour 2. Change your calculation of the hour to allow for the job triggering early.

Of the two, the first is the easiest and I wouldn't call it any more hackerish than allowing for rounding problems when using floating point numbers.

Talca answered 21/8, 2014 at 20:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.