Liking cljdoc? Tell your friends :D

Cronut: A Clojure Companion to Quartz

Cronut Test Clojars Project

Summary

Cronut provides a data-first Clojure wrapper for Quartz Scheduler version 2.5.0, compatible with Jakarta.

Cronut supports in-memory scheduling of jobs within a single JVM. JDBC and distributed jobstore are not supported.

Related Projects

ProjectDesriptionClojars Project
cronut-javaxCronut with Javax support (Legacy)Clojars Project
cronut-integrantIntegrant bindings for CronutClojars Project

Contents

Usage

A quartz scheduler runs a job on a schedule defined by a trigger.

Scheduler

Cronut provides access to the Quartz Scheduler, exposed via the cronut/scheduler function.

Create a scheduler with the following configuration:

  1. :concurrent-execution-disallowed?: run all jobs with @DisableConcurrentExecution
  2. :update-check?: check for Quartz updates on system startup.
(cronut/scheduler {:concurrent-execution-disallowed? true    ;; default false
                   :update-check?                    false}) ;; default false

Scheduler lifecycle

Once created, you can:

  • cronut/start: start the scheduler
  • cronut/start-delayed: start the scheduler with a delay
  • cronut/standy: temporarily halt the firing of triggers by the scheduler
  • cronut/shutdown: stop the scheduler
  • cronut/pause-all: pause all triggers
  • cronut/resume-all: resume all triggers
  • cronut/clear: clear all scheduling data of jobs and triggers

Scheduling jobs

To schedule jobs, you can

  • cronut/schedule-job: schedule a single job
  • cronut/schedule-jobs: schedule multiple jobs at once
  • cronut/pause-job: pause a job
  • cronut/resume-job: resume a paused job
  • cronut/delete-job: remove a job from the scheduler
  • cronut/pause-trigger: pause a trigger
  • cronut/resume-trigger: resume a paused trigger
  • cronut/unschedule-trigger: remove a trigger from the scheduler

Jobs

Each cronut job must implement the org.quartz.Job interface.

Cronut supports further Quartz configuration of jobs (identity, description, recovery, and durability) by passing an opts map when scheduling your job.

Concurrent execution can be controlled on a per-job bases with the disallow-concurrent-execution? flag.

Job example

(defrecord TestDefrecordJobImpl [identity description recover? durable?]
  Job
  (execute [this _job-context]
    (log/info "Defrecord Impl:" this)))


(let [scheduler     (cronut/scheduler {:concurrent-execution-disallowed? true
                                       :update-check?                    false})
      defrecord-job (map->TestDefrecordJobImpl {})
      reify-job     (reify Job
                      (execute [_this _job-context]
                        (let [rand-id (str (UUID/randomUUID))]
                          (log/info rand-id "Reified Impl"))))]

  (cronut/schedule-job scheduler
                       (trigger/interval 1000)
                       defrecord-job
                       {:name        "name1"
                        :group       "group1"
                        :description "test job 1"
                        :recover?    true
                        :durable?    false})

  (cronut/schedule-job scheduler
                       (trigger/builder {:type    :cron
                                         :cron    "*/5 * * * * ?"
                                         :misfire :do-nothing})
                       reify-job
                       {:name        "name2"
                        :group       "group1"
                        :description "test job 2"
                        :recover?    false
                        :durable?    true}))

Triggers

Cronut triggers are of type org.quartz.Trigger, the following functions are provided to simplify trigger creation:

cronut.trigger/cron: Simple Cron Scheduling

A job is scheduled to run on a cron by using the cronut.trigger/cron function with a valid cron expression.

The job will start immediately when the system is initialized, and runs in the default system time-zone

(cronut.trigger/cron "*/8 * * * * ?")

cronut.trigger/interval: Simple Interval Scheduling

A job is scheduled to run periodically by using the cronut.trigger/interval function with a milliseconds value

(cronut.trigger/interval 3500)

cronut.trigger/builder: Full trigger definition

Both cronut.trigger/cron and cronut.trigger/interval are effectively shortcuts to full trigger definition with sensible defaults.

The cronut.trigger/builder function supports the full set of Quartz configuration triggers:

;; interval
(cronut.trigger/builder {:name        "trigger1"
                         :group       "group3"
                         :type        :simple
                         :interval    3000
                         :repeat      :forever
                         :description "sample simple trigger"
                         :start       #inst "2019-01-01T00:00:00.000-00:00"
                         :end         #inst "2019-02-01T00:00:00.000-00:00"
                         :misfire     :ignore
                         :priority    5})

;;cron
(cronut.trigger/builder {:name        "trigger2"
                         :group       "group3"
                         :type        :cron
                         :cron        "*/6 * * * * ?"
                         :description "sample cron trigger"
                         :start       #inst "2018-01-01T00:00:00.000-00:00"
                         :end         #inst "2029-02-01T00:00:00.000-00:00"
                         :time-zone   "Australia/Melbourne"
                         :misfire     :fire-and-proceed
                         :priority    4})

Concurrent execution

Global concurrent execution

Set :concurrent-execution-disallowed? on the scheduler to disable concurrent execution of all jobs.

Job-specific concurrent execution

Set :disallow-concurrent-execution? on a specific job to disable concurrent execution of that job only.

Misfire configuration

If you disable concurrent job execution ensure you understand Quartz Misfire options and remember to set org.quartz.jobStore.misfireThreshold=[some ms value] in your quartz.properties file. See Quartz documentation for more information.

See our test-resources/config.edn and test-resources/org/quartz/quartz.properties for examples of misfire threshold and behaviour configuration.

Example system

See: integration test source: test/cronut/integration-test.clj.

(ns cronut.integration-test
  (:require [clojure.core.async :as async]
            [clojure.tools.logging :as log]
            [cronut :as cronut]
            [cronut.trigger :as trigger])
  (:import (java.util UUID)
           (org.quartz Job)))

(defrecord TestDefrecordJobImpl []
  Job
  (execute [this _job-context]
    (log/info "Defrecord Impl:" this)))

(def reify-job (reify Job
                 (execute [_this _job-context]
                   (let [rand-id (str (UUID/randomUUID))]
                     (log/info rand-id "Reified Impl (Job Delay 7s)")
                     (async/<!! (async/timeout 7000))
                     (log/info rand-id "Finished")))))

;(do (require '[cronut.integration-test :as it])
;    (it/test-system))
(defn test-system
  []
  (let [scheduler (cronut/scheduler {:concurrent-execution-disallowed? true})]
    (cronut/clear scheduler)

    (async/<!! (async/timeout 2000))

    (log/info "scheduling defrecord job on 1s interval")
    (cronut/schedule-job scheduler
                         (trigger/interval 1000)
                         (map->TestDefrecordJobImpl {})
                         {:name        "name1"
                          :group       "group2"
                          :description "test job"
                          :recover?    true
                          :durable?    false})

    ;; demonstrate scheduler can start with jobs, and jobs can start after scheduler
    (cronut/start scheduler)

    (async/<!! (async/timeout 2000))

    ;; demonstrates concurrency disallowed (every second job runs, 10s interval between jobs that should run every 5s)
    (log/info "scheduling reify/7s/no-misfire job on 5s interval")
    (cronut/schedule-job scheduler
                         (trigger/builder {:type    :cron
                                           :cron    "*/5 * * * * ?"
                                           :misfire :do-nothing})
                         reify-job
                         {:name        "name2"
                          :group       "group2"
                          :description "test job 2"
                          :recover?    false
                          :durable?    true})

    (async/<!! (async/timeout 15000))

    (log/info "deleting job group2.name1")
    (cronut/delete-job scheduler "name1" "group2")

    (async/<!! (async/timeout 15000))

    (cronut/shutdown scheduler)))

License

Distributed under the Apache 2.0 License.

Copyright (c) Factor House

Can you improve this documentation? These fine people already did:
Derek Troy-West & d-t-w
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close