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.
| Expression | When |
|---|---|
0 0 3 * * ? | Every day at 03:00 |
0 */15 * * * ? | Every 15 minutes |
0 0 9 ? * MON-FRI | On 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 * * ?.