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.Tempus — Module
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
Tempus.Job — Type
JobRepresents 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).
Tempus.JobExecution — Type
JobExecutionRepresents 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).
Tempus.JobOptions — Type
JobOptionsDefines 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 ifretries > 0).retry_check: Custom function to determine retry behavior (checkargument fromBase.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.nothingmeans UTC.
Tempus.Scheduler — Type
SchedulerThe 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 toThreads.nthreads()logging::Bool: Whether to emit log messages during scheduler operations, defaults totrue.
Tempus.Store — Type
StoreTwo typed AbstractStore views used by a scheduler: jobs by name and bounded execution history by job name. Applications choose the shared backend.
Tempus.Store — Method
Store(jobs, executions; history_limit=100)Create a store from separate typed job and execution-history backends.
Tempus.Store — Method
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.
Base.close — Method
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.
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.
Tempus.FileStore — Method
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.
Tempus.InMemoryStore — Method
Create a process-local Tempus store.
Tempus.OneShotJob — Method
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.
Tempus.SQLiteStore — Method
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.
Tempus._function_ref — Method
Auto-extract fully qualified function reference string from a named function.
Tempus.addJob! — Method
Add or replace a job by name.
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.
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.
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.
Tempus.getJobs — Method
Return every stored job, regardless of disabled status.
Tempus.getNMostRecentJobExecutions — Method
Return at most n execution records, newest first.
Tempus.getnext — Function
getnext(cron::Cron, timezone::String, from::DateTime=Dates.now(UTC)) -> DateTimeCompute 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.
Tempus.isdisabled — Method
isdisabled(job::Job) -> BoolReturns true if the job is currently disabled.
Tempus.nextJobExecution — Function
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.
Tempus.purgeJob! — Method
Remove a job and all execution history for that job.
Tempus.resolve_function — Method
resolve_function(ref::String) -> FunctionResolve a function from its fully qualified reference string (e.g. "MyModule.my_handler"). The module must be loaded before calling this function.
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.
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.
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.
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.
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.
Tempus.withscheduler — Method
withscheduler(f, args...; kw...)Creates a scheduler, runs a function f with it, then calls close.