Oban unique recurring weekly job per user

Hi there, oban question here. I’m trying to schedule a weekly job for each user. This job should only be inserted once per user (per week). On the first attempt, the job will reschedule itself for the next week. My problem is that this job is inserted many times, probably due to incorrect unique options.

Worker code:

defmodule MyApp.WeeklyWorker do
  use Oban.Worker,
    queue: :events,
    unique: [fields: [:args, :worker], states: [:available, :scheduled], period: 60],
    max_attempts: 3

  @one_week 60 * 60 * 24 * 7

  @impl Oban.Worker
  def perform(%Oban.Job{args: %{"user_id" => user_id} = args, attempt: 1}) do
    args
    |> new(schedule_in: @one_week)
    |> Oban.insert!()

    do_heavy_stuff(user_id)
    :ok
  end

  def perform(%Oban.Job{args: %{"user_id" => user_id} = _args}) do
    do_heavy_stuff(user_id)
    :ok
  end

  def do_heavy_stuff(user_id) do
    # lots of things...
  end
end

This worker should be inserted when the user does a certain update action on an Item for the first time, so I’ve opted for inserting the weekly job from the corresponding ItemController action, thinking that the job would be inserted only once, no matter how many times the user would call this action:

  def update(%Plug.Conn{assigns: %{current_user: %{id: user_id}}} = conn, %{"id" => id, "item" => item_params}) do
    with %UserData.Item{user_id: ^user_id} = item <- UserData.get_item(id),
      {:ok, item} <- UserData.rate_item(item, item_params),
      {:ok, _job} <- Oban.insert(MyApp.WeeklyWorker.new(%{user_id: user_id}))
    do
      render(conn, "show.json", item: item)
    else
      {:error, changeset} -> {:error, changeset}
      _item -> {:error, :not_found}
    end
  end

But unfortunately this is not working; I can see that the WeeklyWorker is inserted and scheduled more than once (per user, per week). It’s probably due to incorrect unique options, but I can’t seem to get them right…

Does the unique period has to be changed to one week? If so, how can this be tested (with ex_unit) to function correctly (or do I just trust oban here)?

Thank you for your time!

You’re correct—it is because the unique period is only set to 60 seconds. You’ll need to set it for a week to match the scheduling time.

Oban provides this out of the box with extensive property testing, I wouldn’t test that unique jobs work yourself.

2 Likes