VBT.FixedJob (vbt v0.1.0) View Source
Helper for running jobs with fixed schedules (e.g. once a day at midnight).
Basic usage
In most cases it's advised to define a dedicated module. For example, the following module defines a cleanup job which runs once a day at midnight UTC:
defmodule DailyCleanup do
def child_spec(_) do
VBT.FixedJob.child_spec(
id: __MODULE__,
name: __MODULE__,
run: &cleanup/0,
# configures desired time
when: %{hour: 0, minute: 0},
# prevents periodic job from running automatically in test mode
mode: unquote(if Mix.env() == :test, do: :manual, else: :auto)
)
end
defp cleanup() do
# ...
end
end
Now, you can include DailyCleanup
as a child of some supervisor.
Testing
The job can be tested as follows:
test "cleanup scheduler" do
# The job is registered under a name so we can find its pid
scheduler_pid = Process.whereis(DailyCleanup)
# Needed only if the scheduler works with the database
Sandbox.allow(Repo, self(), scheduler_pid)
# mock the current time in the scheduler
VBT.FixedJob.set_time(scheduler_pid, %{hour: 0, minute: 0})
# ticks the scheduler, waits for the job to finish, and asserts that it exited normally
assert Periodic.Test.sync_tick(scheduler_pid) == {:ok, :normal}
# verify side-effects of the job here
end
You can test multiple different jobs from separate async ExUnit cases. However, the same
job should either be tested from a single case (preferred), or all cases testing the scheduler
should be synchronous (async: false
).
Options
:when
- parts of theDateTime.t
struct which you want to match. In addition, the:day_of_week
key is supported with values of theday_of_week/0
type.:now_fun
- Optional zero arity function (or MFA) which is invoked by the scheduler to get the current date/time. By default,DateTime.utc_now/0
is used.- The remaining options in the
opts/0
type are specific toPeriodic
. See the corresponding docs for details.
Link to this section Summary
Functions
Returns the supervisor child specification for the scheduler process.
Sets the time of the scheduler process.
Starts the scheduler process.
Link to this section Types
Specs
day_of_week() :: :monday | :tuesday | :wednesday | :thursday | :friday | :saturday | :sunday
Specs
filter() :: %{ optional(:minute) => Calendar.minute(), optional(:hour) => Calendar.hour(), optional(:day_of_week) => day_of_week(), optional(:day) => Calendar.day(), optional(:month) => Calendar.month(), optional(:year) => Calendar.year() }
Specs
opts() :: [ id: any(), name: GenServer.name(), telemetry_id: term(), run: (() -> any()) | {module(), atom(), [any()]}, when: filter(), now_fun: (() -> any()) | {module(), atom(), [any()]}, on_overlap: :run | :ignore | :stop_previous, timeout: pos_integer() | :infinity, job_shutdown: :brutal_kill | :infinity | non_neg_integer(), mode: :auto | :manual ]
Link to this section Functions
Specs
child_spec(opts()) :: Supervisor.child_spec()
Returns the supervisor child specification for the scheduler process.
Specs
set_time(GenServer.server(), filter()) :: :ok
Sets the time of the scheduler process.
This function should only be used in tests, and will only work if the scheduler mode is set to
:manual
. Typically you want to invoke this function before calling Periodic.Test.tick/2. You
don't need to provide the complete date/time. Only the parts which are of interest can be passed.
The scheduler will fill in the rest via the now function (see the :now_fun
option).
Specs
start_link(opts()) :: GenServer.on_start()
Starts the scheduler process.