Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Scheduled jobs

Some work an application must do on its own, without a request: collect statistics every minute, clean up old drafts at night, send a digest every hour. Micronaut has the @Scheduled annotation for this. The framework runs a method marked with it on a schedule, in a separate thread pool. Nothing needs to be added: the scheduler is part of the core.

The first job

Create the class by hand or with mn create-job com.example.StatsJob:

// src/main/java/com/example/StatsJob.java
package com.example;
 
import io.micronaut.scheduling.annotation.Scheduled;
import jakarta.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
@Singleton
public class StatsJob {
 
    private static final Logger LOG = LoggerFactory.getLogger(StatsJob.class);
 
    private final ArticleRepository articleRepository;
 
    public StatsJob(ArticleRepository articleRepository) {
        this.articleRepository = articleRepository;
    }
 
    @Scheduled(fixedDelay = "${blog.stats-interval:1m}", initialDelay = "5s")
    void logStats() {
        LOG.info("Articles in the blog: {}", articleRepository.count());
    }
}

The job is an ordinary bean: it gets the repository through its constructor. initialDelay = "5s" means the first run happens 5 seconds after startup, then the pause comes from the blog.stats-interval setting, or one minute if it is missing. Durations are written as 10s, 1m, 2h.

Trying it out

To see it quickly, shorten the interval in application.properties:

# src/main/resources/application.properties
blog.stats-interval=10s

Start the application, create an article and watch the log:

12:10:52.113 [scheduled-executor-thread-1] INFO  com.example.StatsJob - Articles in the blog: 1
12:11:02.114 [scheduled-executor-thread-2] INFO  com.example.StatsJob - Articles in the blog: 1

The lines appear every 10 seconds. The thread name scheduled-executor-thread shows that the job does not run on an HTTP request thread.

Kinds of schedules

ParameterHow it works
fixedDelay = "1m"A 1-minute pause after the previous run ends
fixedRate = "1m"A run every minute from the start of the previous one
cron = "0 0 3 * * *"By a cron expression: every day at 03:00
initialDelay = "5s"A delay before the first run

A cron expression in Micronaut has six fields: seconds, minutes, hours, day of month, month, day of week. A nightly job looks like this:

// src/main/java/com/example/StatsJob.java
@Scheduled(cron = "0 0 3 * * *")
void nightly() {
    LOG.info("Nightly cleanup");
}

If a job can take longer than its interval, choose fixedDelay: the runs will not overlap.

Common mistake

A @Scheduled method runs only if its class is a bean. If you forget @Singleton, the application starts without errors or warnings, but the job never runs: its lines simply never appear in the log. When a job stays "silent", first check the scope annotation on the class.

Thanks for reading!

Donate to the site — we work hard for you.