JBoss.orgCommunity Documentation

Chapter 7. Immutant Jobs

7.1. Introduction

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 or a more finer-grained at-style syntax.

7.2. Scheduling Jobs

Scheduling a job is as simple as calling immutant.jobs/schedule:

(ns my.ns
  (:require [immutant.jobs :as jobs]))

(jobs/schedule "my-cron-job-name"  
               #(println "I fire every 5s, forever.")
               "*/5 * * * * ?"
               :singleton false)

(jobs/schedule :my-at-job-name  
               #(println "I fire 4 times with a 10ms delay between each, starting in 500ms.")
               :in 500   ; ms
               :every 10 ; ms
               :repeat 3
               :singleton false)

The schedule function takes two or three arguments, and also accepts options:

  • name - the name of the job. If the given name is the same as the name of previously scheduled job in the application's runtime, the prior job will be unscheduled and the new job will be scheduled in its place. The name can be either a keyword or a string, but keywords will be converted to strings internally, so :foo and "foo" will refer to the same job.
  • f - the zero argument function that will be invoked each time the job fires.
  • spec - the optional cron-style specification string if you are scheduling a cron-style job. It is an error to provide a spec along with any of the at options. See Cron Syntax.
  • options - options are specified as alternating key & value literals. Valid options are:
    OptionDefaultDescription
    :atnone, now if no spec providedSpecifies when the at job should start firing. Can be a java.util.Date, ms since epoch, or a time in HH:mm format (see below). Can't be specified with a spec or :in.
    :innoneSpecifies when the at job should start firing, in ms from now, as a period alias, or as a period spec (see below). Can't be specified with a spec or :at.
    :everynoneSpecifies the delay interval between at job firings, in ms, as a period alias, or as a period spec (see below). If specified without a :repeat or :until, the job will fire indefinitely. Can't be specified with a spec.
    :repeatnoneSpecifies the number of times an at job should repeat beyond its initial firing. Can't be specified with a spec, and requires :every to be provided.
    :untilnoneSpecifies when the at job should stop firing. Can be a java.util.Date, ms since epoch, or a time in HH:mm format (see below). Can't be specified with a spec.
    :singletontrueMarks the job as a singleton in a cluster. Singleton jobs will only execute on one node. If false, the job will execute on every node on which it is scheduled (see below).

As a convenience, you can pass a time as a String (in "HH:mm" or "HHmm" format) for the :at and :until options. It will be interpreted as the next occurence of "HH:mm:00" in the currently active timezone.

You can also pass a period alias or period spec for :in or :repeat. A period alias is one of: :second, :minute, :hour, :day, or :week. A period spec is a vector or list with a number followed by a period alias (including plural variants): [1 :second], [5 :minutes]. Each will be converted into the corresponding milliseconds.

Example:

(ns my.ns
  (:require [immutant.jobs :as jobs]))

(jobs/schedule :my-at-job-name  
               #(println "I every minute until 4:30pm, starting in 2 hours.")
               :in [2 :hours]
               :every :minute
               :until "16:30")

If you specify a job without a cron spec or any at options, it will fire once, immediately:

(ns my.ns
  (:require [immutant.jobs :as jobs]))

(jobs/schedule "another-job"  
               #(println "I'll fire right now, once."))

Job scheduling is dynamic, and can occur anytime during your application's lifecycle.

You can safely call schedule multiple times with the same job name - the named job will be rescheduled.

In case you need it, the org.quartz.JobExecutionContext instance is bound to immutant.jobs/*job-execution-context* before invoking your job function.

7.2.1. Cron Syntax

The spec argument 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):

SecondsMinutesHoursDay of MonthMonthDay of WeekYear
0-590-590-231-311-12 or JAN-DEC1-7 or SUN-SAT1970-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.

7.2.2. A Note On Dynamic Singleton Jobs

The singleton functionality is ensures that a job that is scheduled on multiple nodes only runs on one at a time, and, for at jobs that have a finite execution count, ensures that the job only executes the intended number of times in a cluster.

The singleton makes no effort to distribute jobs across a cluster - for a job to take advantage of the singleton guarantees, it will need to be scheduled on every node of the cluster by your application code calling schedule.

For jobs that are started as part of your application initialization process, this should be no problem, since the application initialization will occur on every node where the application is deployed. However, for jobs that are scheduled dynamically after initialization based upon an external event, you will need to implement a mechanism to schedule that job on each node.

One option for doing so is to create a topic listener that runs on each node, and is responsible for scheduling the job when a message is received containing the job details.

7.2.3. Job MBeans

Each job scheduled gets its own mbean under the immutant.jobs namespace. This mbean can be used to stop, start, and reschedule the job.

7.3. Unscheduling Jobs

Jobs can be unscheduled via the immutant.jobs/unschedule function:

(require '[immutant.jobs :as jobs])

(jobs/unschedule "my-job-name")

The 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 return true, otherwise nil is returned.

Jobs are automatically unscheduled when your application is undeployed.

Immutant 1.1.1