erlcron (erlcron v1.3.6)

Erlcron is a testable cron-like job scheduler for Erlang applications.

Each scheduled job runs in its own lightweight process that sleeps for exactly the computed duration until its next execution time, giving millisecond precision instead of the one-minute polling interval used by Unix cron.

The scheduler clock can be manipulated at runtime with set_datetime/1 and multi_set_datetime/1. When the clock is advanced, all jobs whose scheduled time falls within the elapsed interval are executed in order, which makes it straightforward to test time-based behaviour in automated test suites.

Schedule types

  • {once, When} — Run a job exactly once at the given time or after the given number of seconds.
  • {daily, Period} — Run a job every day at the given time, list of times, or on a recurring schedule optionally bounded by a time window.
  • {weekly, DOW, Period} — Run a job on the specified day(s) of the week. DOW is a day atom (mon..sun) or a list of day atoms.
  • {monthly, DOM, Period} — Run a job on the specified day(s) of the month. Positive integers count from the start of the month; 0 and negative integers count backwards from the last day (0 = last day, -1 = second-to-last, and so on).
  • CronExpr — A standard 5-field Unix cron expression as a string or binary (e.g. "30 9 * * 1", <<"*/5 * * * *">>). The expression is parsed via ecrn_util:from_cron/1 into an equivalent erlcron tuple schedule at job-submission time. See ecrn_util:from_cron/1 for supported syntax and the mapping to erlcron schedule forms.

When

When specifies a point in time. It is used as the argument to {once, When} and to the convenience functions at/2, daily/2, weekly/3, and monthly/3.

A When value can be any of:

FormExampleMeaning
{H, am|pm}{3, pm}3:00:00 PM (15:00:00)
{H, M, am|pm}{3, 30, pm}3:30:00 PM (15:30:00)
{H, M, S, am|pm}{3, 30, 15, pm}3:30:15 PM (15:30:15)
{H, M, S}{15, 30, 0}24-hour clock time
Seconds3600Number of seconds from now (for once only)

Period

Period is used wherever a repeating or multi-time schedule is needed (daily, weekly, monthly). It can take any of these forms:

FormExampleMeaning
When{3, 30, pm}Run once per day at the given time
[When][{9, am}, {5, pm}]Run at each listed time per day
{every, Duration}{every, {30, min}}Repeat every duration, all day
{every, Duration, {between, From, To}}{every, {5, min}, {between, {9, am}, {5, pm}}}Repeat every duration within the time window

Duration units: hr / h, min / m, sec / s.

Quick start

%% Start the application first
application:ensure_all_started(erlcron),

%% Run a fun once at 3:30 PM
erlcron:cron({{once, {3, 30, pm}}, fun() -> io:fwrite("Hello!~n") end}),

%% Run a fun every 5 minutes using a Unix cron expression
erlcron:cron({"*/5 * * * *", fun() -> poll() end}),

%% Run a fun every day at 9 AM, identified by a named reference
erlcron:daily(my_daily_job, {9, am}, fun() -> do_work() end),

%% Cancel the daily job
erlcron:cancel(my_daily_job).

Summary

Types

A 5-field Unix cron expression string or binary.

Alias for job_opts/0. Default options applied to all jobs in the crontab.

Called after a job has finished executing.

Per-job options map.

A job reference. Can be a reference(), atom, or binary.

Called before a job is executed.

Functions

Schedule a job to run once at When.

Cancel the job identified by JobRef. Returns true if found, false otherwise.

Add a new job to the scheduler.

Schedule a job identified by JobRef.

Schedule a job with an explicit reference, schedule, task, and options.

Schedule a job to run every day at When.

Return the current erlcron datetime (universal) together with the corresponding millisecond epoch value.

Return the current erlcron time as milliseconds since the Unix epoch.

Return the current erlcron time as seconds since the Unix epoch.

Return the references of all currently running jobs.

Schedule a job to run every month on day DOM at When.

Set the scheduler clock on all connected nodes. See multi_set_datetime/2.

Set the scheduler clock to DateTime on the given Nodes.

Return the reference datetime used as the scheduler's clock base.

Reset the scheduler clock to the real system time.

Set the scheduler clock to DateTime (local time).

Set the scheduler clock to DateTime.

Validate a schedule spec without scheduling the job.

Schedule a job to run every week on DOW (day-of-week) at When.

Types

callable()

-type callable() :: fun(() -> term()) | fun((JobRef :: job_ref(), calendar:datetime()) -> term()).

constraint()

-type constraint() :: {between, cron_time(), cron_time()}.

cron_expr()

-type cron_expr() :: string() | binary().

A 5-field Unix cron expression string or binary.

Examples: \"*/5 * * * *\", \"30 9 * * 1\", ~\"0 18 * * fri\".
Parsed at job-submission time by ecrn_util:from_cron/1.

cron_opts()

-type cron_opts() :: job_opts().

Alias for job_opts/0. Default options applied to all jobs in the crontab.

cron_time()

-type cron_time() :: {integer(), am | pm} | {integer(), integer(), am | pm} | calendar:time().

dom()

-type dom() :: integer().

dow()

-type dow() :: dow_day() | [dow_day()].

dow_day()

-type dow_day() :: mon | tue | wed | thu | fri | sat | sun.

duration()

-type duration() :: {integer(), hr | h | min | m | sec | s}.

job()

-type job() ::
          {schedule(), task()} |
          {schedule(), task(), job_opts()} |
          #{id => job_ref(), schedule => schedule(), task => task(), _ => any()}.

job_end()

-type job_end() ::
          fun((JobRef :: job_ref(), Res :: {ok, term()} | {error, {Reason :: term(), Stack :: list()}}) ->
                  term()).

Called after a job has finished executing.

Res is {ok, Result} on success, or {error, {Reason, StackTrace}} if the job raised an exception.

job_opts()

-type job_opts() ::
          #{hostnames => [binary() | string()],
            id => term(),
            on_job_start => {Mod :: atom(), Fun :: atom()} | job_start(),
            on_job_end => {Mod :: atom(), Fun :: atom()} | job_end()}.

Per-job options map.

  • hostnames — List of hostnames on which the job is allowed to run. The job is silently ignored on any other host.
  • id — An arbitrary identifier passed to on_job_start and on_job_end callbacks.
  • on_job_start{Mod, Fun} or a fun/1 called before the job executes. Return ignore to skip this execution.
  • on_job_end{Mod, Fun} or a fun/2 called after the job finishes. Receives (JobRef, {ok, Result} | {error, {Reason, Stack}}).

job_ref()

-type job_ref() :: reference() | atom() | binary().

A job reference. Can be a reference(), atom, or binary.

When the reference is an atom, the job process is locally registered under that name, making it easy to cancel it by name later.

job_start()

-type job_start() :: fun((JobRef :: job_ref()) -> ignore | any()).

Called before a job is executed.

If the function returns ignore, the job will not be executed and on_job_end will not be called.

milliseconds()

-type milliseconds() :: integer().

period()

-type period() :: cron_time() | {every, duration()} | {every, duration(), constraint()}.

schedule()

-type schedule() ::
          {once, cron_time()} |
          {once, seconds()} |
          {daily, period()} |
          {weekly, dow(), period()} |
          {monthly, dom() | [dom()], period()} |
          cron_expr().

seconds()

-type seconds() :: integer().

task()

-type task() :: {M :: module(), F :: atom()} | {M :: module(), F :: atom(), Args :: list()} | callable().

Functions

at(When, Fun)

-spec at(cron_time() | seconds(), task()) -> job_ref() | ignored | already_started | {error, term()}.

Schedule a job to run once at When.

When can be a cron_time/0 (e.g. {3, 30, pm}) or an integer number of seconds from now.

at(JobRef, When, Fun)

-spec at(job_ref(), cron_time() | seconds(), function()) ->
            job_ref() | ignored | already_started | {error, term()}.

at/4

-spec at(job_ref(), cron_time() | seconds(), function(), job_opts()) ->
            job_ref() | ignored | already_started | {error, term()}.

cancel(JobRef)

-spec cancel(job_ref()) -> boolean().

Cancel the job identified by JobRef. Returns true if found, false otherwise.

cron(Job)

-spec cron(job()) -> job_ref() | ignored | already_started | {error, term()}.

Add a new job to the scheduler.

The job is described using a job/0 spec. Returns the job_ref/0 that can be used to cancel the job later.

cron/2

-spec cron(job() | job_ref(), job() | cron_opts() | task()) ->
              job_ref() | ignored | already_started | {error, term()}.

cron/3

-spec cron(job_ref(), job(), job_opts()) -> job_ref() | ignored | already_started | {error, term()}.

Schedule a job identified by JobRef.

The reference can be a reference(), atom, or binary. When it is an atom the job process is locally registered under that name. Returns ignored when the job is not permitted to run on the current host.

When called as cron(Sched, Task, Opts) a fresh reference is generated automatically and Opts is a job_opts/0 map.

cron(JobRef, Sched, Task, Opts)

-spec cron(job_ref(), schedule(), task(), job_opts()) ->
              job_ref() | ignored | already_started | {error, term()}.

Schedule a job with an explicit reference, schedule, task, and options.

erlcron:cron(my_job, {daily, {9, am}}, fun() -> ok end, #{}).
erlcron:cron(my_job, \"30 9 * * 1-5\", fun() -> standup() end, #{}).

daily(When, Fun)

-spec daily(cron_time() | seconds(), function()) ->
               job_ref() | ignored | already_started | {error, term()}.

Schedule a job to run every day at When.

daily(JobRef, When, Fun)

-spec daily(job_ref(), cron_time() | seconds(), function()) ->
               job_ref() | ignored | already_started | {error, term()}.

daily/4

-spec daily(job_ref(), cron_time() | seconds(), function(), job_opts()) ->
               job_ref() | ignored | already_started | {error, term()}.

datetime()

-spec datetime() -> {calendar:datetime(), milliseconds()}.

Return the current erlcron datetime (universal) together with the corresponding millisecond epoch value.

epoch()

-spec epoch() -> milliseconds().

Return the current erlcron time as milliseconds since the Unix epoch.

epoch_seconds()

-spec epoch_seconds() -> seconds().

Return the current erlcron time as seconds since the Unix epoch.

get_all_jobs()

-spec get_all_jobs() -> [job_ref()].

Return the references of all currently running jobs.

monthly(DOM, When, Fun)

-spec monthly(dom(), cron_time() | seconds(), function()) ->
                 job_ref() | ignored | already_started | {error, term()}.

Schedule a job to run every month on day DOM at When.

Positive DOM values count from the start of the month; 0 means the last day of the month, and negative values count backwards from the last day (e.g. -1 is the second-to-last day).

monthly(JobRef, DOM, When, Fun)

-spec monthly(job_ref(), dom(), cron_time() | seconds(), function()) ->
                 job_ref() | ignored | already_started | {error, term()}.

monthly/5

-spec monthly(job_ref(), dom(), cron_time() | seconds(), function(), job_opts()) ->
                 job_ref() | ignored | already_started | {error, term()}.

multi_set_datetime/1

-spec multi_set_datetime(calendar:datetime()) -> {Replies, BadNodes}
                            when Replies :: [{node(), ok | {error, term()}}], BadNodes :: [node()].

Set the scheduler clock on all connected nodes. See multi_set_datetime/2.

multi_set_datetime(Nodes, DateTime)

-spec multi_set_datetime([node()], calendar:datetime()) -> {Replies, BadNodes}
                            when Replies :: [{node(), ok | {error, term()}}], BadNodes :: [node()].

Set the scheduler clock to DateTime on the given Nodes.

Any jobs scheduled between the old and new time on each node are executed immediately. Returns {Replies, BadNodes} in the same format as gen_server:multi_call/3.

ref_datetime()

-spec ref_datetime() -> {calendar:datetime(), milliseconds()}.

Return the reference datetime used as the scheduler's clock base.

reset_datetime()

-spec reset_datetime() -> ok.

Reset the scheduler clock to the real system time.

set_datetime(DateTime)

-spec set_datetime(calendar:datetime()) -> ok.

Set the scheduler clock to DateTime (local time).

set_datetime/2

-spec set_datetime(calendar:datetime(), local | universal) -> ok.

Set the scheduler clock to DateTime.

TZ controls how DateTime is interpreted: local (default) or universal. When the clock is advanced, all jobs scheduled between the old and new time are executed immediately.

validate(Spec)

-spec validate(schedule()) -> ok | {error, term()}.

Validate a schedule spec without scheduling the job.

weekly(DOW, When, Fun)

-spec weekly(dow(), cron_time() | seconds(), function()) ->
                job_ref() | ignored | already_started | {error, term()}.

Schedule a job to run every week on DOW (day-of-week) at When.

weekly(JobRef, DOW, When, Fun)

-spec weekly(job_ref(), dow(), cron_time() | seconds(), function()) ->
                job_ref() | ignored | already_started | {error, term()}.

weekly/5

-spec weekly(job_ref(), dow(), cron_time() | seconds(), function(), job_opts()) ->
                job_ref() | ignored | already_started | {error, term()}.