Honker.Scheduler (honker v0.1.4)

Copy Markdown

Time-trigger task registry. Thin wrapper over honker_scheduler_* SQL functions, plus a blocking run/3 loop with leader election via the honker-scheduler advisory lock.

:ok = Honker.Scheduler.add(db,
  name: "hourly-health",
  queue: "health",
  schedule: "0 * * * *",
  payload: %{}
)

stop = :atomics.new(1, [])
:ok = :atomics.put(stop, 1, 0)
Task.async(fn ->
  Honker.Scheduler.run(db, "worker-1", fn -> :atomics.get(stop, 1) == 1 end)
end)

Summary

Functions

Register a recurring scheduled task. Idempotent by :name.

Return every registered schedule with current state. Each entry is a map with: name, queue, cron_expr, payload, priority, expires_s, next_fire_at, enabled, max_attempts.

Pause a registered schedule. Returns {:ok, true} if a row was paused.

Unregister a task by name. Returns {:ok, count}.

Resume a paused schedule. Returns {:ok, true} if a row was resumed.

Run the scheduler loop with leader election. Blocks until stop_fun returns true. Only the process holding the honker-scheduler lock fires ticks; standbys poll for the lock to expire.

Soonest unix timestamp across all tasks, or 0 if none.

Fire any tasks whose boundaries are due. Returns {:ok, [%{name, queue, fire_at, job_id}, ...]} — a list of maps with string keys as decoded from the JSON returned by the extension.

Mutate fields in place. opts is a keyword list with any of: cron: / schedule:, payload:, priority:, expires_s:, max_attempts:. Omit a key to leave its field alone. payload: nil writes JSON null; max_attempts: nil resets to default 3. Cron change recomputes next_fire_at from now. Returns {:ok, true} iff a row was updated.

Functions

add(database, opts)

Register a recurring scheduled task. Idempotent by :name.

Options (all required except :priority and :expires_s):

  • :name — unique task name
  • :queue — queue the payload is enqueued onto each fire
  • :schedule — canonical schedule expression:
                5-field cron, 6-field cron, or `@every <n><unit>`
  • :cron — backward-compatible alias for :schedule
  • :payload — any JSON-encodable term
  • :priority — integer (default 0)
  • :expires_s — seconds; the fired job expires this many seconds
                after its scheduled fire time. Default `nil`.
  • :max_attempts — attempt budget for each fired job. Default 3.

list(database)

Return every registered schedule with current state. Each entry is a map with: name, queue, cron_expr, payload, priority, expires_s, next_fire_at, enabled, max_attempts.

pause(database, name)

Pause a registered schedule. Returns {:ok, true} if a row was paused.

remove(database, name)

Unregister a task by name. Returns {:ok, count}.

resume(database, name)

Resume a paused schedule. Returns {:ok, true} if a row was resumed.

run(db, owner, stop_fun)

Run the scheduler loop with leader election. Blocks until stop_fun returns true. Only the process holding the honker-scheduler lock fires ticks; standbys poll for the lock to expire.

Lock TTL is 60 seconds; we refresh every 20 seconds. If a refresh returns false (TTL expired, someone else took it), we exit the leader loop before firing another tick so we never double-fire with the new leader.

On any tick error we release the lock before returning the error so a standby can take over immediately.

soonest(database)

Soonest unix timestamp across all tasks, or 0 if none.

tick(database)

Fire any tasks whose boundaries are due. Returns {:ok, [%{name, queue, fire_at, job_id}, ...]} — a list of maps with string keys as decoded from the JSON returned by the extension.

update(database, name, opts \\ [])

Mutate fields in place. opts is a keyword list with any of: cron: / schedule:, payload:, priority:, expires_s:, max_attempts:. Omit a key to leave its field alone. payload: nil writes JSON null; max_attempts: nil resets to default 3. Cron change recomputes next_fire_at from now. Returns {:ok, true} iff a row was updated.