PhoenixKit.ObanCrons (phoenix_kit v2.63.0)

Copy Markdown View Source

The Oban cron jobs installed modules declare, so a host does not have to copy them into its config by hand.

A module declares its scheduled workers with the optional PhoenixKit.Module.oban_crons/0 callback, next to PhoenixKit.Module.oban_queues/0:

@impl PhoenixKit.Module
def oban_crons, do: [{"0 3 * * *", MyModule.Workers.NightlyRebuild}]

mix phoenix_kit.install writes every declared entry into the new crontab and mix phoenix_kit.update adds the ones an existing crontab is missing (PhoenixKit.Install.ObanConfig.ensure_module_cron_entries/3). The host's say wins, as it does for core's own workers: a schedule the host wrote is never changed, and an entry the host commented out of its crontab is a declined entry that is not offered again.

Entries are read from every installed module, enabled or not, and a module is found without the registry when the installer runs before it exists (the same rule as PhoenixKit.ObanQueues). A worker should do nothing while its module is switched off.

One entry per worker

The installer decides whether an entry is already there by the worker's name, so a worker has one schedule and no args. If two modules declare the same worker with different schedules, the first (modules sorted by name) wins and the other is reported as a conflict.

What is dropped

An entry is ignored, with a warning, when the cron expression is not one Oban's own parser accepts (Oban.Cron.parse/1, or Oban.Plugins.Cron.parse/1 before Oban 2.24 renamed the plugin) or the worker is not a loaded Elixir module exporting perform/1. That is deliberate: Oban refuses to start when its crontab names something that is not a worker, so one bad declaration must not reach the host's config.

Summary

Types

Two modules declaring one worker with different schedules.

One cron entry: the schedule and the worker.

Functions

The cron entries to configure, one per worker, and the conflicts found on the way.

A one-line description of a conflict, for the installer's output.

Types

conflict()

@type conflict() :: %{
  worker: module(),
  kept: entry(),
  kept_owner: module(),
  ignored: entry(),
  ignored_owner: module()
}

Two modules declaring one worker with different schedules.

entry()

@type entry() :: {String.t(), module()}

One cron entry: the schedule and the worker.

Functions

declared(modules \\ nil)

@spec declared([module()] | nil) :: {[entry()], [conflict()]}

The cron entries to configure, one per worker, and the conflicts found on the way.

modules defaults to the registered modules when the registry is running and to a .beam scan otherwise (the install and update tasks run before the registry exists).

describe_conflict(conflict)

@spec describe_conflict(conflict()) :: String.t()

A one-line description of a conflict, for the installer's output.