Timelier v0.9.2 Timelier

A cron-style scheduling library for Elixir.

Overview

Timelier is a cron style scheduling application for Elixir. It will match a list of time ‘patterns’ against the current time and start any tasks associated with each matching pattern.

By default, the set of scheduled tasks is sourced from the crontab environment variable within the :timelier application or [] if not present. For example, the following snippet from config.exs defines a task that runs every 15 minutes and dumps a message via :error_logger.info_msg/1.

config :timelier,
       crontab: [{{[0,15,30,45],:any,:any,:any,:any}, {:error_logger,:info_msg,['Hello world.~n']}}]

Starting Timelier

The crontab for Timelier will often contain tasks whose execution is dependent upon particular applications running. To ensure timelier can successfully start tasks defined in your application (or its dependencies), add it as an included application:

def application do
  [included_applications: [:timelier]]
end

and append it’s root supervisor to the list of children that your own top-level supervisor starts, e.g.

def start(_type, _args) do
  import Supervisor.Spec, warn: false

  # Define workers and child supervisors to be supervised
  children = [
    worker(YourApp.YourWorker, []),
    # Other children in your supervision tree...

    supervisor(Timelier.Supervisor, []) # Add timelier's top-level supervisor
  ]

  opts = [strategy: :one_for_one, name: YourApp.Supervisor]
  Supervisor.start_link(children, opts)
end

This should ensure that all appropriate supervision trees should be started before Timelier begins starting tasks.

Crontab format

The format of the crontab is a list of tuple pairs, where the first tuple describes the time pattern and the second specifies the task to be invoked when the pattern matches the current time.

The task is 3-tuple of {mod, func, args}, as would be passed to Kernel.apply/3 and is run by a temporary GenServer under a simple-one-for-one strategy. The return result of applying this triple is ignored, but any crashes will generate a crash report, if sasl is also running.

The time pattern is 5-tuple specifying the minute, hour, day, weekday, and month. Each element may be:

  • A single specific value (see the types for the valid ranges).
  • Wildcarded with :any, which will always match.
  • Described as a list, in which case the elements describe the set of valid values.

In addition, the day and weekday elements support some special ranges:

  • The day element may take negative values in the range -31 .. -1, where -1 indicates the last day of the month, -2 the penultimate day of the month and so on.
  • The weekday element may take negative values in the range -7 .. -1 where -1 means ‘the last Monday of the month’ and -7 means ‘the last Sunday of the month’.

Example patterns

PatternRuns
{:any, :any, :any, :any, :any}every minute
{[0, 15, 30, 45], :any, :any, :any, :any}every 15 minutes
{0, 9, :any, [1, 2, 3, 4, 5], :any}9am on weekdays
{30, 22, -1, :any, :any}10.30pm on the last day of every month
{0, :any, :any, -5, [12,1,2]}every hour on the last friday of Jan, Feb, Dec

Time evaluation

Every minute, the application converts the current timestamp (from :erlang.timestamp/0) into an internal datetime representation that may either be in UTC or local time, before matching against the crontab entries.

This representation is controlled by the timezone environment variable, which may either be :utc or :local. The default, if unspecified, is :local.

Alternative Providers

To use an alternative source of scheduled tasks, specify the provider environment variable and ensure that the specified callback returns a tuple of the type {:ok, Timelier.crontab}, e.g.

config :timelier,
       provider: {mod, func, [arg1, arg2 ...]}

Summary

Types

A list of crontab entries

Valid ranges for the day of the month element

Represents an individual entry in the crontab

Valid range for the hour element

Valid range for the minute element

Valid range for the month element

Represents a time pattern

Represents the type of a task

Represents actual time

Valid ranges for the day of the week element

Valid range for the year element

Functions

Check the current crontab for any pending tasks

Provide a default crontab from configuration

Called when an application is started

Updates the current crontab configuration

Types

crontab()
crontab() :: [entry]

A list of crontab entries.

day()
day() :: -31..-1 | 1..31

Valid ranges for the day of the month element.

Negative values indicate days from the end of the month i.e. -1 means last day of the month, -2 means the penultimate day of the month.

entry()
entry() :: {pattern, task}

Represents an individual entry in the crontab.

Composed of a pattern and a task. When the pattern matches the time, the task is started.

hour()
hour() :: 0..23

Valid range for the hour element.

minute()
minute() :: 0..59

Valid range for the minute element.

month()
month() :: 1..12

Valid range for the month element.

pattern()
pattern() :: {minute | [minute] | :any, hour | [hour] | :any, day | [day] | :any, weekday | [weekday] | :any, month | [month] | :any}

Represents a time pattern.

A 5-tuple whose elements represent the minute, hour, day, weekday and month. Each element may a distinct value, a list of alternatives or a wildcard.

task()
task() :: {atom, atom, [any]}

Represents the type of a task.

A 3-tuple holding the module, function and any arguments to be applied.

time()
time() :: {minute, hour, day, weekday, month, year}

Represents actual time.

weekday()
weekday() :: -7..-1 | 1..7

Valid ranges for the day of the week element.

Positive values start at 1 (Monday) and run through 7 (Sunday). Negative values have a different meaning. -1 means ‘the last Monday of the month’ and -7 means ‘the last Sunday of the month.’

year()
year() :: 1970..10000

Valid range for the year element.

Notwithstanding the BEAM’s reputation for reliability and longevity, 10000 is probably enough.

Functions

check()
check() :: :ok

Check the current crontab for any pending tasks.

This function will translate the current timestamp into either utc or local time and check the crontab for matches on the values.

get_crontab()
get_crontab() :: {:ok, term}

Provide a default crontab from configuration.

start(type, args)

Called when an application is started.

This function is called when an the application is started using Application.start/2 (and functions on top of that, such as Application.ensure_started/2). This function should start the top-level process of the application (which should be the top supervisor of the application’s supervision tree if the application follows the OTP design principles around supervision).

start_type defines how the application is started:

  • :normal - used if the startup is a normal startup or if the application is distributed and is started on the current node because of a failover from another mode and the application specification key :start_phases is :undefined.
  • {:takeover, node} - used if the application is distributed and is started on the current node because of a failover on the node node.
  • {:failover, node} - used if the application is distributed and is started on the current node because of a failover on node node, and the application specification key :start_phases is not :undefined.

start_args are the arguments passed to the application in the :mod specification key (e.g., mod: {MyApp, [:my_args]}).

This function should either return {:ok, pid} or {:ok, pid, state} if startup is successful. pid should be the PID of the top supervisor. state can be an arbitrary term, and if omitted will default to []; if the application is later stopped, state is passed to the stop/1 callback (see the documentation for the c:stop/1 callback for more information).

use Application provides no default implementation for the start/2 callback.

Callback implementation for Application.start/2.

update(crontab)
update(crontab :: crontab) :: :ok

Updates the current crontab configuration.

If the crontab is invalid, this function will crash.