← Build logs
BackendJune 24, 2026

Troubleshooting Scheduler Task Delays and Missed Executions: The Limits of `misfire_grace_time`

Have you ever faced issues where scheduled jobs get delayed or don't run at all, leading to missed critical updates? In this post, I'll share how I tackled this by configuring the misfire_grace_time setting.

Trials and Tribulations

I encountered a situation where the daily update tasks for my observer feature were getting stuck. Initially, I thought simply increasing the misfire_grace_time in the scheduler settings would prevent delayed jobs from being missed.

So, I set misfire_grace_time to 6 hours.

// Example: Quartz scheduler configuration (may vary depending on the actual library used)
scheduler.getListenerManager().addJobListener(new JobListener() {
    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        // Logic before job execution
    }
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
    // Logic for cancelled job execution
}

@Override
public String getName() {
    return "MyJobListener";
}

@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
    // Logic after job execution
    if (jobException != null) {
        // Error handling
    }
}

});

// Setting misfire_grace_time (this part is applied during scheduler initialization) // For example, in a Spring Boot environment, configure it in properties file or Java Config // org.quartz.jobStore.misfireThreshold = 21600000 // 6 hours (in milliseconds)

However, just increasing misfire_grace_time didn't solve the root cause. I realized that updates could still be missed when jobs were delayed. After about 3 hours of head-scratching, I understood this wasn't the whole story.

The Cause

When scheduler jobs are delayed, a situation can arise where the next execution time arrives before the previous one has finished. misfire_grace_time defines how long the scheduler should wait if a job can't run at its scheduled time. However, if the job can't start for other reasons even within this grace period, it can ultimately lead to a missed execution.

The Solution

To fix this, I not only increased misfire_grace_time to 6 hours but also applied the coalesce option. The coalesce option ensures that the scheduler doesn't run multiple instances of the same job concurrently.

// Example: Quartz scheduler JobDetail configuration
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
    .withIdentity("myJob", "group1")
    .usingJobData("key", "value")
    .storeDurably() // Whether to store JobDetail
    .build();

// Trigger configuration Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("myTrigger", "group1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInHours(24) // 24-hour interval .repeatForever() .withMisfireHandlingInstructionDoNothing() // Do nothing if misfire occurs (then apply coalesce) ) .build();

// Schedule the job scheduler.scheduleJob(jobDetail, trigger);

// Setting misfire_grace_time (in Quartz.properties or JobStore configuration) // org.quartz.jobStore.misfireThreshold = 21600000 // 6 hours (in milliseconds)

// The coalesce option is enabled in Quartz.properties or JobStore configuration. // org.quartz.jobStore.useProperties = true (generally true) // org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate (depends on DB type) // The behavior of the coalesce option can vary depending on the JobStore implementation, and it's more influenced by the JobStore's default behavior than an explicit setting. // Generally, in Quartz 2.x versions and above, the JobStore includes coalesce logic.

With the coalesce option enabled, even if a job is delayed, if an instance of the same job is already running, the scheduler will skip or wait for the new job execution, thus preventing missed updates.

The Result

  • Even when daily update tasks experienced delays, missed updates no longer occurred.
  • The stability of the scheduler was secured, maintaining data consistency for the observer feature.
  • The issue was resolved solely through scheduler configuration without altering existing logic, reducing development burden.

Summary — To Avoid the Same Pitfall

  • [ ] Monitor for missed updates when scheduler jobs are delayed.
  • [ ] Ensure sufficient allowance for job delay by configuring misfire_grace_time (e.g., 6 hours).
  • [ ] Verify that the coalesce option is enabled or that your JobStore supports this functionality.
  • [ ] Periodically check scheduler logs for unexpected misfires or delays.