CandleClock (candle_clock v1.6.1) View Source

CandleClock manages timers that persist even across restarts.

Core concepts

Timer

A timer is a function call that will happen at some point in the future. It always contains an MFA and either a duration, an interval or a crontab expression.

MFA

An MFA is a tuple with 3 elements: the module, the function and the arguments to pass to the function. The 3 elements of the tuple are the same as the 3 arguments you'd pass to Kernel.apply/3.

Crontab

A crontab expression can be used when you want the timer to trigger at regular dates or times, such as

  • Every monday at 12 AM: 0 12 * * 1
  • Every 5 minutes: */5 * * * *
  • Every hour: 0 * * * *
  • Every day at 5 PM: 0 17 * * *

Timers with crontabs can be given an additional time zone so 0 17 * * * for Europe/Berlin is 16:00 UTC during winter time but 15:00 UTC during summer time.

Startup behaviour

At startup, all timers that have an expired_at date in the past will be executed.

When planning the next execution time of a single timer, it will take the last execution time as the basis. However, that might lead to a timer being called multiple times directly after a long downtime.

For that reason, if :skip_if_offline is true, interval and cron timers will instead be planned to the next time it would've happened as if the earlier intervals would've been triggered as well, but they won't be called more than once at startup.

:skip_if_offline is enabled by default.

Duration and intervals

All durations and intervals are integers in the unit millisecond.

Common options

These are the common options that can be passed to call_after/3, call_interval/4 and call_crontab/4:

  • :name (string) A name that makes this timer unique. Unique timers will be replaced when a new timer with the same name is created. That way, a defer can be implemented simply by always giving the same name.
  • :if_not_exists (bool, default: false) If set to true, will not create the timer if one already exists with that name. Requires a name to be set.
  • :skip_if_offline (bool, default: true) If set to false, interval and cron timers behave differently after a long downtime of the system. See also: Startup behaviour
  • :max_calls (int, default: nil or 1 for duration timers) Controls the maximum amount this timer can be called before it is cancelled. Not supported in duration timers started with call_after/3.

Link to this section Summary

Functions

Creates a timer that is executed after the duration in milliseconds.

Creates a timer that is executed according to the given crontab schema.

Creates a timer that is executed every interval ms.

Cancels all timers that call the given module and function.

Cancels a timer by its ID.

Cancels the timer with the given name.

Creates a list of timers at once (using insert_all).

Returns if a timer with the given id exists

Returns if a timer with the given name exists

Calculates the next expiry date for the given timer from the given date onwards.

Link to this section Types

Specs

alarm_spec() :: %{expires_at: DateTime.t(), max_calls: 1}

Specs

argument() :: any()

Specs

arguments() :: [argument()]

Specs

cron_timer_spec() :: %{
  crontab: Crontab.CronExpression.t(),
  crontab_timezone: binary()
}

Specs

duration_timer_spec() :: %{duration: non_neg_integer(), max_calls: 1}

Specs

interval() :: non_neg_integer()

Specs

interval_timer_spec() :: %{
  duration: non_neg_integer(),
  interval: non_neg_integer(),
  max_calls: nil | non_neg_integer()
}

Specs

mf_args() :: {module(), atom(), arguments()}

Specs

Link to this section Functions

Link to this function

call_after(mfa, duration, opts \\ [])

View Source

Specs

call_after(mf_args(), interval(), keyword()) ::
  {:ok, struct()} | {:error, any()}

Creates a timer that is executed after the duration in milliseconds.

Returns the timer in an ok-tuple if successful.

Link to this function

call_at(mfa, date, opts \\ [])

View Source

Specs

call_at(mf_args(), DateTime.t(), keyword()) :: {:ok, struct()} | {:error, any()}
Link to this function

call_crontab(mfa, crontab, timezone \\ "Etc/UTC", opts \\ [])

View Source

Specs

call_crontab(mf_args(), String.t(), String.t(), keyword()) ::
  {:ok, struct()} | {:error, any()}

Creates a timer that is executed according to the given crontab schema.

Returns the timer in an ok-tuple if successful.

Link to this function

call_interval(mfa, duration \\ nil, interval, opts \\ [])

View Source

Specs

call_interval(mf_args(), interval(), interval(), keyword()) ::
  {:ok, struct()} | {:error, any()}

Creates a timer that is executed every interval ms.

Additionally, the duration until the first trigger can be passed with the duration argument.

Returns the timer in an ok-tuple if successful.

Link to this function

cancel_all(module, function)

View Source

Specs

cancel_all(module(), atom()) :: {:ok, non_neg_integer()}

Cancels all timers that call the given module and function.

Returns {:ok, amount} if successful, where amount is the number of timers that were cancelled.

Specs

cancel_by_id(any()) :: {:ok, non_neg_integer()}

Cancels a timer by its ID.

Returns {:ok, 1} if the ID matched.

Specs

cancel_by_name(String.t()) :: {:ok, non_neg_integer()}

Cancels the timer with the given name.

Returns {:ok, 1} if a timer with that name was found.

Link to this function

create_many(timers, opts \\ [])

View Source

Specs

create_many([timer_spec()], keyword()) :: [struct()]

Creates a list of timers at once (using insert_all).

Schemas for the different timer types:

For all timer types

%{
  module: module(),
  function: atom(),
  arguments: list(),
  inserted_at: DateTime.t(),
  updated_at: DateTime.t()
}
as well as any custom fields defined in the candle clock timer schema.

Duration

%{
  duration: non_neg_integer(), # milliseconds
  max_calls: 1
}

Interval

%{
  duration: non_neg_integer(), # milliseconds
  interval: non_neg_integer(), # milliseconds
  max_calls: nil | non_neg_integer() # nil for unlimited calls
}

Once, at a specific date and time

%{
  expires_at: DateTime.t(),
  max_calls: 1
}

Cron

%{
  crontab: Crontab.CronExpression.t(),
  crontab_timezone: binary() # "Europe/Berlin" for example.
}

Specs

id_exists?(any()) :: boolean()

Returns if a timer with the given id exists

Specs

name_exists?(String.t()) :: boolean()

Returns if a timer with the given name exists

Link to this function

next_expiry(timer, date \\ DateTime.utc_now())

View Source

Specs

next_expiry(
  struct(),
  DateTime.t()
) :: {:ok, DateTime.t()} | {:error, any()}

Calculates the next expiry date for the given timer from the given date onwards.

Returns {:ok, datetime} or an error-tuple