Getting Started: Scheduling Jobs
This article covers job schedulding in Immutant, and is part of our getting started series of tutorials.
Jobs in Immutant are simply functions that execute on a recurring schedule. They fire asynchronously, outside of the thread where they are defined, and fire in the same runtime as the rest of the application, so have access to any shared state.
Jobs are built on top of the Quartz library, and support scheduling via a cron-like specification.
Why would I use this over quartz-clj or calling Quartz directly?
I'm glad you asked! There are several reasons:
- Immutant abstracts away the complexity of Quartz's internals, so you don't have to worry about managing Schedulers and creating JobDetails, and provides enough functionality for a majority of use cases. For cases where you need advanced scheduling functionality, you can still use quartz-clj or the Quartz classes directly.
- If you are using Immutant in a cluster, jobs that should fire only once per cluster (aka 'singleton jobs') are handled automatically (see below).
- When your application is undeployed, your jobs are automatically unscheduled. Note that if you use quartz-clj or Quartz directly from your application, you'll need to clean up after yourself so you don't leave jobs lingering around since Immutant can't automatically unschedule them for you.
Scheduling a job is as simple as calling the
schedule function from the
(require '[immutant.jobs :as jobs]) (jobs/schedule "my-job-name" "*/5 * * * * ?" #(println "I was called!"))
schedule function requires three arguments:
- name - the name of the job.
- spec - the cron-style specification string (see below).
- f - the zero argument function that will be invoked each time the job fires.
Job scheduling is dynamic, and can occur anywhere in your application code.
Jobs that share the lifecycle of your application are idiomatically placed in
You can safely call
schedule multiple times with the same job name - the named job will
The spec attribute should contain a crontab-like entry. This is similar to cron specifications used by Vixie cron, anacron and friends, but includes an additional field for specifying seconds. It is composed of 7 fields (6 are required):
|Seconds||Minutes||Hours||Day of Month||Month||Day of Week||Year|
|0-59||0-59||0-23||1-31||1-12 or JAN-DEC||1-7 or SUN-SAT||1970-2099 (optional)|
For several fields, you may denote subdivision by using the forward-slash (/) character. To execute a job every 5 minutes, */5 in the minutes field would specify this condition.
Spans may be indicated using the dash (-) character. To execute a job Monday through Friday, MON-FRI should be used in the day-of-week field.
Multiple values may be separated using the comma (,) character. The specification of 1,15 in the day-of-month field would result in the job firing on the 1st and 15th of each month.
Either day-of-month or day-of-week must be specified using the ? character, since specifying both is contradictory.
See the Quartz cron specification for additional details.
Jobs can be unscheduled via the
(require '[immutant.jobs :as jobs]) (jobs/unschedule "my-job-name")
unschedule function requires one argument:
- name - the name of a previously scheduled job.
If the given name resolves to an existing job, that job will be unscheduled and the call will
nil is returned.
Jobs are automatically unscheduled when your application is undeployed.
When using Immutant in a cluster, you'll need to mark any jobs that should only be scheduled
once for the entire cluster with the
(require '[immutant.jobs :as jobs]) (jobs/schedule "my-job-name" "*/5 * * * * ?" #(println "I only fire on one node") :singleton true)
true, the job will be scheduled to run on only one node in the cluster
at a time. If that node goes down, the job will automatically be scheduled on another node, giving
you failover. If
false or not provided, the job will be scheduled to run on
all nodes where the
schedule call is executed.
Look for a future post in our Getting Started series on using Immutant in a cluster.
(require '[immutant.jobs :as jobs]) (jobs/schedule "my-at-job" (jobs/every "3s" :times 5) #(println "I fire 5 times, every 3 seconds"))
Since Immutant is still in a pre-alpha state, none of what I said above is set in stone. If anything does change, We'll update this post to keep it accurate.
If you have any feedback or questions, get in touch!