Tempus.jl

Quartz-inspired cron job scheduling for Julia: cron expressions (with optional seconds, month/day names, and @-aliases), overlap policies, retries, execution caps, timezone-aware schedules, and pluggable persistent state through AbstractStores.jl.

using Tempus

scheduler = Tempus.Scheduler()          # in-memory state
Tempus.run!(scheduler)                  # dispatch loop runs in the background

push!(scheduler, Tempus.Job(
    () -> println("Hello from Tempus!"),
    "hello_job",
    "* * * * *",                        # every minute
))

# ... later
close(scheduler)

See the README for a walkthrough of state backends, job options, and the full cron syntax.

API Reference

Tempus.TempusModule

Tempus provides a cron-style job scheduling framework for Julia, inspired by Quartz in Java.

Features:

  • Define jobs with cron-like scheduling expressions
  • Supports job execution policies (overlap handling, retries, and failure strategies)
  • Pluggable persistent state via AbstractStores.jl backends (memory, file, SQL, Redis)
  • Concurrency-aware execution with configurable retry logic
  • Supports disabling, enabling, and unscheduling jobs dynamically
  • Thread-safe scheduling with a background execution loop
source
Tempus.JobType
Job

Represents a single job/unit of work. Can be scheduled to repeat.

Fields:

  • name::String: Unique identifier for the job.
  • schedule::Union{Cron, Nothing}: The cron-style schedule expression.
  • action::Function: The function to execute when the job runs.
  • action_ref::Union{Nothing, String}: Fully qualified function reference for persistence (e.g. "MyModule.my_handler").
  • action_data::Union{Nothing, String}: JSON-encoded parameters for persistence. Splatted as kwargs: action(; JSON.parse(action_data)...).
  • options::JobOptions: Execution options for retries, failures, and overlap handling.
  • disabledAt::Union{DateTime, Nothing}: Timestamp when the job was disabled (if applicable).
source
Tempus.JobExecutionType
JobExecution

Represents an instance of a job execution.

Fields:

  • jobExecutionId::String: Unique identifier for this job execution.
  • job::Job: The job being executed.
  • scheduledStart::DateTime: When the job was scheduled to run.
  • runConcurrently::Bool: Whether this execution is running concurrently with another.
  • actualStart::DateTime: The actual start time.
  • finish::DateTime: The completion time.
  • status::Symbol: The execution result (:succeeded, :failed).
source
Tempus.JobOptionsType
JobOptions

Defines options for job execution behavior.

Fields:

  • overlap_policy::Union{Symbol, Nothing}: Determines job execution behavior when the same job is already running (:skip, :queue, :concurrent).
  • retries::Int: Number of retries allowed on failure.
  • retry_delays::Union{Base.ExponentialBackOff, Nothing}: Delay strategy for retries (defaults to exponential backoff if retries > 0).
  • retry_check: Custom function to determine retry behavior (check argument from Base.retry).
  • max_failed_executions::Union{Int, Nothing}: Maximum number of failed executions allowed for a job before it will be disabled.
  • max_executions::Union{Int, Nothing}: Maximum number of executions allowed for a job.
  • expires_at::Union{DateTime, Nothing}: Expiration time for a job.
  • timezone::Union{Nothing, String}: IANA timezone name (e.g. "America/Denver"). When set, the job's cron schedule is interpreted in this timezone. nothing means UTC.
source
Tempus.SchedulerType
Scheduler

The main scheduling engine that executes jobs according to their schedules.

Fields:

  • lock::ReentrantLock: Ensures thread-safe access.
  • jobExecutions::Vector{JobExecution}: List of scheduled job executions.
  • store::Store: Job storage backend.
  • jobExecutionFinished::Threads.Event: Signals all job executions have finished when shutting down.
  • executingJobExecutions::Set{JobExecution}: Tracks currently executing jobs.
  • running::Bool: Scheduler state (running/stopped).
  • loopActive::Bool: Whether the dispatch-loop task has finished stopping.
  • jobOptions::JobOptions: Default job execution options.
  • max_concurrent_executions::Int: Limit on how many total executions can be running concurrently for this scheduler, defaults to Threads.nthreads()
  • logging::Bool: Whether to emit log messages during scheduler operations, defaults to true.
source
Tempus.StoreType
Store

Two typed AbstractStore views used by a scheduler: jobs by name and bounded execution history by job name. Applications choose the shared backend.

source
Tempus.StoreMethod
Store(jobs, executions; history_limit=100)

Create a store from separate typed job and execution-history backends.

source
Tempus.StoreMethod
Store(backend; prefix="tempus/", history_limit=100)

Create job and execution-history views over one AbstractStore backend. Namespacing lets Tempus share a physical store with other libraries and application state.

The backend must accept both Job and Vector{JobExecution} values — i.e. be an AbstractStore{Any} — and it must return them as those types. That rules out codecs that decode at the backend's own eltype: FileStore{Any}(dir; codec=JSONCodec()) hands back Dict{String,Any}, not a Job. Use a type-preserving backend (MemoryStore(), or any store with the default SerializedCodec), or pass separately typed stores to Store(jobs, executions).

history_limit bounds the execution history kept per job. Note that max_executions/max_failed_executions are evaluated against that history, so a limit below either of them means the corresponding cap can never be reached.

source
Base.closeMethod
close(scheduler::Scheduler; timeout::Real=5)

Closes the scheduler, stopping job execution; waits up to timeout seconds (5 by default) for any currently executing jobs to finish before returning.

source
Base.push!Method
push!(scheduler::Scheduler, job::Job)

Adds a job to the scheduler and underlying Store, scheduling its next execution based on its cron schedule. Pushing a job whose name is already scheduled replaces the stored job and any queued (not yet running) executions.

source
Base.waitMethod
wait(scheduler::Scheduler)

Waits for the scheduler to finish executing all jobs. Note the scheduler must be explicitly closed to stop the scheduler loop or pass close_when_no_jobs=true to run! to automatically close the scheduler when no jobs are left.

source
Tempus.FileStoreMethod
FileStore(directory; kw...)

Create a Tempus store backed by an AbstractStores.FileStore. directory is a directory, not the single JSON file used by Tempus 2.

source
Tempus.OneShotJobMethod
OneShotJob(action, name; kw...)

A Job with no cron schedule that runs once, as soon as the scheduler picks it up, and is then disabled. A failed attempt is re-run (with the job's retry options applied within each attempt) until it succeeds or reaches max_failed_executions. Accepts the same keyword options as Job. max_executions is always set to 1.

Note the run-once bookkeeping is based on the job's stored execution history, so re-adding a one-shot job whose name has already succeeded will not run it again; use a fresh name (or purgeJob!) to re-run one.

source
Tempus.SQLiteStoreMethod
SQLiteStore(connection; table="tempus_state", kw...)

Compatibility constructor for a Tempus store backed by one AbstractStores.SQLStore table. The same Store(SQLStore(...)) form works for SQLite, Postgres, and other DBInterface drivers.

source
Tempus.disable!Method
disable!(job::Job)

Disables a job, preventing it from being scheduled for execution.

This mutates the Job object only. A persisting store holds a copy of the job, so use disableJob!(store, job) when the change must survive a restart.

source
Tempus.disableJob!Method
disableJob!(store, job; at=Dates.now(UTC))

Disable a stored job by reference or name. The update uses the backend atomic read-modify-write operation.

source
Tempus.enable!Method
enable!(job::Job)

Enables a previously disabled job, allowing it to be scheduled again.

Like disable!, this mutates the Job object only; call addJob!(store, job) afterwards to write the re-enabled job back to a persisting store.

source
Tempus.getnextFunction
getnext(cron::Cron, timezone::String, from::DateTime=Dates.now(UTC)) -> DateTime

Compute the next trigger time for cron interpreted in timezone (IANA name, e.g. "America/Denver"). from is a UTC DateTime. Returns a UTC DateTime. DST handling: spring-forward gaps are skipped, fall-back ambiguities use the first occurrence.

source
Tempus.nextJobExecutionFunction
nextJobExecution(store::Store, job::Job) -> Union{JobExecution, Nothing}

For a job persisted in store, check the job's status and execution history and return a JobExecution for the next time it should run, or nothing if the job shouldn't be scheduled again. As a side effect, jobs that have expired or reached their execution caps are disabled in the store.

source
Tempus.resolve_functionMethod
resolve_function(ref::String) -> Function

Resolve a function from its fully qualified reference string (e.g. "MyModule.my_handler"). The module must be loaded before calling this function.

source
Tempus.run!Method
run!(scheduler::Scheduler; close_when_no_jobs::Bool=false)

Starts the scheduler, executing jobs at their scheduled times. The dispatch loop runs on a background task; run! returns the scheduler immediately. With close_when_no_jobs=true, the loop shuts down on its own once no executions are queued or running (see runJobs!). Throws if the scheduler is already running or a previous timed-out close still has loop or job tasks in flight.

source
Tempus.runJobs!Method
runJobs!(store::Store, jobs; kw...)

Add each job in jobs to store, run a scheduler with kw options, wait for all jobs to finish, then close the scheduler.

source
Tempus.scheduleNextExecution!Method
scheduleNextExecution!(scheduler::Scheduler, job::Job)

Schedule job's next execution, returning it, or nothing when no execution was scheduled: the job is done (see nextJobExecution), it has been removed from the store, an execution at the same time is already queued, or — for one-shot jobs, which are rescheduled only when an attempt fails — an execution is already queued or running. scheduler.lock must be held.

The new execution is appended without re-sorting (so callers iterating scheduler.jobExecutions or holding indexes into it stay valid); callers must restore scheduledStart order before the scheduler loop scans the list again.

source
Tempus.storeJobExecution!Method
storeJobExecution!(store, execution)

Prepend one execution to the job history and keep at most history_limit records. The update is atomic when the selected backend provides atomic modify!.

A serializing backend encodes the whole JobExecution, including the value the job returned and any exception it threw, so those must be encodable by the backend's codec.

source
Tempus.unschedule!Method
unschedule!(scheduler::Scheduler, job::Union{Job, AbstractString})

Removes a job (by reference or name) from the scheduler and the underlying store, canceling any queued executions and deleting the job's execution history. An already running execution finishes but is not rescheduled.

See disable!/disableJob! to keep a job (and its history) around while preventing it from running.

source
Tempus.withschedulerMethod
withscheduler(f, args...; kw...)

Creates a scheduler, runs a function f with it, then calls close.

source