Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Scheduled tasks

Some work a service must do on its own, without a client request: delete old comments every night, recalculate statistics once an hour, log its state every few seconds. In Quarkus such scheduled tasks are described with the @Scheduled annotation on a bean method. Quarkus calls the method at the right time in a separate thread.

Adding it

./mvnw quarkus:add-extension -Dextensions="scheduler"

A task with an interval

// src/main/java/com/example/blog/BlogJobs.java
package com.example.blog;
 
import io.quarkus.logging.Log;
import io.quarkus.scheduler.Scheduled;
import jakarta.enterprise.context.ApplicationScoped;
 
@ApplicationScoped
public class BlogJobs {
 
    @Scheduled(every = "10s")
    void reportPosts() {
        Log.infof("Posts in the blog: %d", Post.count());
    }
}

every = "10s" means run every 10 seconds. You can write 30s, 5m, 1h. Log is a handy Quarkus logger: there is no need to declare a logger field in every class. The method takes no parameters and returns nothing. In dev mode a line appears in the terminal every 10 seconds:

INFO  [com.example.blog.BlogJobs] (vert.x-worker-thread-1) Posts in the blog: 2

A cron task

To run at a specific time, use a cron expression — a string of fields "seconds minutes hours day month day-of-week":

// src/main/java/com/example/blog/BlogJobs.java
@Inject
CommentRepository commentRepository;
 
@Scheduled(cron = "0 0 3 * * ?")
@Transactional
void deleteOldComments() {
    long deleted = commentRepository.delete("createdAt < ?1", LocalDateTime.now().minusDays(30));
    Log.infof("Old comments deleted: %d", deleted);
}

The task runs every day at 03:00:00 and deletes comments older than 30 days. The imports are jakarta.inject.Inject, jakarta.transaction.Transactional and java.time.LocalDateTime. The task changes data, so it needs @Transactional, just like a resource method.

ExpressionWhen
0 0 3 * * ?Every day at 03:00
0 */15 * * * ?Every 15 minutes
0 0 9 ? * MON-FRIOn weekdays at 09:00
0 0 0 1 * ?On the first day of every month at midnight

A schedule from the settings

The interval and the cron can be moved to application.properties so you can change them without a rebuild:

// src/main/java/com/example/blog/BlogJobs.java
@Scheduled(cron = "{blog.cleanup-cron}")
# src/main/resources/application.properties
blog.cleanup-cron=0 0 3 * * ?

Curly braces in the annotation mean "take the value from the settings". Our project has BlogConfig with the blog prefix, and it rejects unknown blog.* keys (the SRCFG00050 error from the config mapping lesson). So add a method for the new key to the interface:

// src/main/java/com/example/blog/BlogConfig.java
String cleanupCron();

Where to see the tasks

In the Dev UI the Scheduler card has a Scheduled methods section: a list of all tasks, their schedules and a button to run a task right now without waiting. This is handy for checking a nightly cleanup during the day.

Common mistake

Cron in Quarkus uses the Quartz format, where the first field is seconds and there are six or seven fields. A familiar five-field Unix cron expression such as 0 3 * * * does not fit, and the application does not start:

IllegalStateException: Invalid cron() expression: @Scheduled(cron = "0 3 * * *") declared on com.example.blog.BlogJobs#deleteOldComments()
Caused by: java.lang.IllegalArgumentException: Cron expression contains 5 parts but we expect one of [6, 7]

Add the seconds at the start and ? in one of the day fields: 0 0 3 * * ?.

Thanks for reading!

Donate to the site — we work hard for you.