Recurring jobs
Schedule recurring jobs with a single line of code using any CRON expression or interval.
Creating a recurring job (either a CRON job or a job with a fixed defined interval) is just as simple as creating a background job – you only need to write a single line of code (and it is even less if you use the jobrunr-spring-boot-starter
, jobrunr-micronaut-feature
or the jobrunr-quarkus-extension
).
On this page you can learn about:
- Create a recurring job using a CRON expression
- Create a recurring job using an Interval
- Managing recurring jobs
- Deleting recurring jobs
- JobRunr Pro Pause and Resume recurring jobs
- JobRunr Pro Recurring jobs with limited lifetime
- JobRunr Pro Advanced Cron Expressions
- JobRunr Pro Custom Recurring Job Schedules
- JobRunr Pro Recurring jobs missed during downtime
- JobRunr Pro Concurrent recurring jobs
- Important remarks!
Note that JobRunr OSS supports up to 100 recurring jobs (depending on the performance of your SQL or NoSQL database). Do you need to run more than 100 recurring jobs? This is supported in JobRunr Pro!
Note that recurring jobs may not be executed on the exact moment you specify using your CRON expression: Whenever JobRunr fetches all the jobs that are scheduled and need to be executed, it fetches all jobs that need to happen in the next poll interval and enqueues them immediately. This may result in a difference of a couple of seconds. If you need real-time scheduling, then have a look at JobRunr Pro.
Using a CRON expression
This line creates a new recurring CRON job entry in the StorageProvider
. A special component in BackgroundJobServer
checks the recurring jobs with a fixed interval (the pollIntervalInSeconds
) and then enqueues them as fire-and-forget jobs. This enables you to track them as usual.
The Cron
class contains different methods and overloads to run jobs on a minute, hourly, daily, weekly, monthly and yearly basis. You can also use a standard CRON expressions to specify a more complex schedule:
All these methods are also available on the JobScheduler
and JobRequestScheduler
bean:
You can also create a recurring job with a CRON expression using the builder pattern:
If you are using the jobrunr-spring-boot-starter
, the jobrunr-micronaut-feature
or the jobrunr-quarkus-extension
this becomes even easier: just add the @Recurring
annotation to any bean method and JobRunr will schedule it as a recurring job:
Using an Interval
Instead of giving a Cron expression, you can also give a duration. This will make sure that the recurring job will now be executed using a fixed interval starting the moment the recurring job was scheduled.
Also in this case, you can create a recurring job with a fixed interval using the builder pattern:
If you are using the jobrunr-spring-boot-starter
, the jobrunr-micronaut-feature
or the jobrunr-quarkus-extension
this stays as easy as with a CRON expression: add the @Recurring
annotation to any bean method with the interval
attribute where you can pass an ISO8601 duration. JobRunr will schedule it again as a recurring job using the provided interval:
Managing recurring jobs
Each recurring job has its own unique identifier. In the previous examples it was generated implicitly, using the type and method names of the given lambda expression (resulting in “System.out.println()
” as the identifier). The BackgroundJob
and JobScheduler
class contains overloads that take an explicitly defined job identifier. This way, you can refer to the job later on.
If you are using the JobBuilder
pattern, this becomes:
The methods above will create a new recurring job if no recurring job with that id exists or else update the existing job with the given identifier.
Identifiers should be unique - use unique identifiers for each recurring job, otherwise you’ll end with a single job.
Deleting recurring jobs
You can remove an existing recurring job either via the dashboard or by calling the BackgroundJob.delete
method with the id of the recurring job. It does not throw an exception when there is no such recurring job.
Pause and Resume recurring jobs
JobRunr ProUsing JobRunr Pro, you can pause and resume recurring jobs from the dashboard and using the API.
Recurring jobs with limited lifetime
JobRunr ProBy default, a RecurringJob
is active for the entire lifetime of an application (unless paused).
With JobRunr Pro you can provide an end time: each RecurringJob
with a deleteAt
in the passed will stop scheduling new jobs. JobRunr will also automatically remove the RecurringJob
from the DB.
You can enable the feature using @Job
:
If you are using the JobBuilder
, this is also possible:
Advanced CRON Expressions
JobRunr ProDo you need to run recurring jobs on some special moments like the first business day of the month or the last business day of the month? JobRunr Pro has a CRON expression parser on steroids and supports your really complex schedule requirements.
L
character
L
stands for “last”. When used in the day-of-week field, it allows specifying constructs such as “the last Friday” (5L
) of a given month. In the day-of-month field, it specifies the last day of the month.
Some examples:
0 0 * * 5L
: midnight the last Friday of each month0 0 * 2 1L
: midnight the last Monday of each February0 0 L * *
: midnight the last day of each month0 0 L 2 *
: midnight the last day of each February
#
character
#
is allowed for the day-of-week field, and must be followed by a number between one and five. It allows specifying constructs such as “the second Friday” of a given month.
Some examples:
0 0 * * 1#1
: midnight the first Monday of each month0 0 * 1 1#1
: midnight the first Monday of each January0 0 * * 5#3
: midnight the third Friday of each month
W
character
The W
character is allowed for the day-of-month field. This character is used to specify the weekday (Monday-Friday) nearest the given day. As an example, if “15W” is specified as the value for the day-of-month field, the meaning is: “the nearest weekday to the 15th of the month.” So, if the 15th is a Saturday, the trigger fires on Friday the 14th. If the 15th is a Sunday, the trigger fires on Monday the 16th. If the 15th is a Tuesday, then it fires on Tuesday the 15th. However, if “1W” is specified as the value for day-of-month, and the 1st is a Saturday, the trigger fires on Monday the 3rd, as it does not ‘jump’ over the boundary of a month’s days. The ‘W’ character can be specified only when the day-of-month is a single day, not a range or list of days.
Some examples:
0 0 1W * *
: midnight the first Monday of each month0 0 1W+2 * *
: midnight 2 days after the first Monday of each month0 0 20W * *
: midnight on the 20th or the closest workday to the 20th
Custom Recurring Job Schedules
JobRunr ProDo you have really complex recurring job schedule? Just extend the class org.jobrunr.scheduling.custom.CustomSchedule
and implement one method where you provide the next java.time.Instant
your job should run. For example:
Your
CustomSchedule
implementation must not throw an exception as this will result in an unexpected behavior, and in the worst case will kill the JobRunr background job processing server.
Recurring jobs missed during downtime
JobRunr ProIf for some reason all of your servers are down (e.g. deploying a new version / scheduled down time / …), JobRunr OSS skips these recurring jobs: as there is no background job server running, it will not be able to schedule these recurring jobs.
JobRunr Pro improves this and adds the capability to catch up all of the skipped recurring jobs: for each run that was skipped during the downtime for a certain recurring job, it will schedule a job.
This feature is disabled by default and can be enabled using the following setting:
If you are using the JobBuilder
, this is also possible:
Concurrent recurring jobs
JobRunr ProJobRunr by default does not allow concurrent recurring jobs - the reason being is that if your recurring jobs for some reason take longer than the given CRON expression or interval, you may create more jobs than you can process.
So, if a job instance created by a recurring job is still in state SCHEDULED
, ENQUEUED
or PROCESSING
and it’s time to again queue a new instance of the recurring job then this last instance will not be created.
Sometimes the business requirements define that recurring jobs may not be skipped in any case and your recurring job runs very fast in 95% of the cases. But you have some outliers (e.g. due to a lot of work to process) that may take longer than the given CRON expression or interval. JobRunr Pro comes to the rescue with the option maxConcurrentJobs
to create a certain amount of job instances of that recurring job that will run concurrently.
By default, maxConcurrentJobs
is set to 1 but this setting can be changed per recurring job:
If you are using the JobBuilder, this is also possible:
Important remarks!
Remark 1: for recurring jobs to work, at least one BackgroundJobServer should be running all the time.
Remark 2: also, for recurring job to work, they should be registered at application startup (which can either be a webapp, a console app, …). This is different for each application/environment. The easiest way to do so is via the
@Recurring
annotation that ships with thejobrunr-spring-boot-starter
, thequarkus-jobrunr
extension or thejobrunr-micronaut-feature
as shown above. If you are not using an integration with a certain framework, you will need to register these scheduled jobs yourselves using Container Startup Event listeners.
Remark 3: please note that the cron interval or duration for your recurring jobs must be more than your
pollIntervalInSeconds
. If yourpollIntervalInSeconds
is greater than your cron interval or duration of your recurring jobs, JobRunr will launch multiple instances of the same recurring job to keep up. This means that the same recurring job will be launched multiple times at the same moment.
Remark 4: also note that the smallest possible cron interval for your recurring jobs is every 5 seconds. JobRunr prevents creating recurring jobs with cron values less than every 5 seconds (e.g. every second) as it would generate too much load on your StorageProvider (SQL or noSQL database).