Pro Background Tasks & Jobs
Viewing latest docs.
Switch version: v3

Background Tasks and Jobs

Once-off background tasks

When you just want to run some code in a background task, use PetalPro.BackgroundTask.run/1:

elixir
PetalPro.BackgroundTask.run(fn ->
  do_some_time_intensive_stuff()
end)

This is useful for speeding up responses in an HTTP request or LiveView action — delegate anything that takes time to a background task (e.g. sending emails, posting to Slack, creating audit logs). Since we want to respond to a user request as soon as possible, you run these non-essential tasks in the background and it won’t hold up the response.

Background jobs with Oban

A background job is something you want to run outside of a normal HTTP request/response cycle — things like sending weekly emails or processing images. We use Oban for this.

A background job is performed by a “worker”. Each worker gets its own file. Petal Pro comes with an example worker you can duplicate:

elixir
defmodule PetalPro.Workers.ExampleWorker do
  @moduledoc """
  Example of how to do async work with Oban.

  Run with:
  Oban.insert(PetalPro.Workers.ExampleWorker.new(%{}))
  """
  use Oban.Worker, queue: :default
  require Logger

  @impl Oban.Worker
  def perform(%Oban.Job{} = _job) do
    today = Timex.now() |> Timex.to_date()
    Logger.info("ExampleWorker: Today is #{today}")
    :ok
  end
end

You can run this worker by inserting a job into the database:

elixir
Oban.insert(PetalPro.Workers.ExampleWorker.new(%{}))

Once in the database, Oban will try running the task and retry it if it fails. You can see any failed jobs by checking the database: You can inspect job status directly in the oban_jobs database table.

In dev mode, you can use Oban Web:

http://localhost:4000/admin/oban

Jobs that have been completed are deleted every 24 hours so your database won’t swell over time.

Cron jobs

You can tell your workers to run at certain times with Oban’s cron functionality. See the examples in config.exs:

elixir
config :petal_pro, Oban,
  repo: PetalPro.Repo,
  queues: [default: 5],
  plugins: [
    {Oban.Plugins.Pruner, max_age: (3600 * 24)},
    {Oban.Plugins.Cron,
      crontab: [
        {"@daily", PetalPro.Workers.ExampleWorker}
        # {"* * * * *", PetalPro.EveryMinuteWorker},
        # {"0 * * * *", PetalPro.EveryHourWorker},
        # {"0 */6 * * *", PetalPro.EverySixHoursWorker},
        # {"0 0 * * SUN", PetalPro.EverySundayWorker},
        # More examples: https://crontab.guru/examples.html
      ]}
  ]

Daily Slack digest worker

Petal Pro v4 ships PetalPro.Workers.DailySlackDigestWorker — an Oban cron job that sends a summary of yesterday’s activity to a Slack channel. It pulls new user signups, audit log action counts, and top page views, then formats them as a single Slack message.

Enabling the digest

Add the worker to the cron schedule in config.exs:

elixir
{Oban.Plugins.Cron,
  crontab: [
    {"0 13 * * *", PetalPro.Workers.DailySlackDigestWorker}  # 8am ET / 1pm UTC
  ]}

Slack configuration

The digest uses PetalPro.Slack to post the message. You need two environment variables:

shell
# OAuth token from your Slack app's "OAuth and Permissions" page
fly secrets set SLACK_OAUTH_TOKEN="xoxb-..."

# The ID of the Slack channel to post to
fly secrets set SLACK_CHANNEL_ID="C0123456789"

To find your channel ID: run PetalPro.Slack.read_channels() in IEX — it returns a list of [id, name] pairs. If the digest is not configured (no SLACK_CHANNEL_ID), it silently skips sending and logs a message instead.

To test locally:

elixir
PetalPro.Slack.message("Hello from Petal Pro!")

Slow query telemetry (dev only)

In development, Petal Pro logs any database queries that exceed a configurable time threshold. This makes it easy to spot N+1 queries and unexpectedly slow operations without any additional tooling.

The threshold is set in config/dev.exs:

elixir
# Log queries slower than this many milliseconds (dev only)
config :petal_pro, :dev_query_threshold_ms, 100

Queries that exceed the threshold are logged to the console with their duration and SQL. To tighten the threshold and catch more queries, lower the value:

elixir
config :petal_pro, :dev_query_threshold_ms, 50

Set it to 0 to log every query. This config has no effect in production.