diff --git a/lib/oban/job.ex b/lib/oban/job.ex index 970842ce..cebd03e5 100644 --- a/lib/oban/job.ex +++ b/lib/oban/job.ex @@ -221,7 +221,7 @@ defmodule Oban.Job do Insert a job, ensuring that it is unique within the past minute: - MyApp.Worker.new(%{id: 1}, unique: [period: 60]) + MyApp.Worker.new(%{id: 1}, unique: [period: {1, :minute}]) Insert a unique job where the period is compared to the `scheduled_at` timestamp rather than `inserted_at`: @@ -366,12 +366,35 @@ defmodule Oban.Job do end end + @doc false + @spec cast_period(pos_integer() | {atom(), pos_integer()}) :: pos_integer() + def cast_period({value, unit}) do + unit = to_string(unit) + + cond do + unit in ~w(second seconds) -> value + unit in ~w(minute minutes) -> value * 60 + unit in ~w(hour hours) -> value * 60 * 60 + unit in ~w(day days) -> value * 24 * 60 * 60 + true -> unit + end + end + + def cast_period(period), do: period + + @valid_period_units ~w(second seconds minute minutes hour hours day days)a + @doc false @spec valid_unique_opt?({:fields | :period | :states, [atom()] | integer()}) :: boolean() def valid_unique_opt?({:fields, [_ | _] = fields}), do: fields -- [:meta | @unique_fields] == [] def valid_unique_opt?({:keys, []}), do: true def valid_unique_opt?({:keys, [_ | _] = keys}), do: Enum.all?(keys, &is_atom/1) def valid_unique_opt?({:period, :infinity}), do: true + + def valid_unique_opt?({:period, {period, unit}}) do + is_integer(period) and period > 0 and unit in @valid_period_units + end + def valid_unique_opt?({:period, period}), do: is_integer(period) and period > 0 def valid_unique_opt?({:states, [_ | _] = states}), do: states -- states() == [] def valid_unique_opt?({:timestamp, stamp}), do: stamp in ~w(inserted_at scheduled_at)a @@ -447,7 +470,7 @@ defmodule Oban.Job do |> Map.new() |> Map.put_new(:fields, @unique_fields) |> Map.put_new(:keys, []) - |> Map.put_new(:period, @unique_period) + |> Map.update(:period, @unique_period, &cast_period/1) |> Map.put_new(:states, @unique_states) |> Map.put_new(:timestamp, @unique_timestamp) diff --git a/test/oban/job_test.exs b/test/oban/job_test.exs index 4ca6ca9c..81fb3798 100644 --- a/test/oban/job_test.exs +++ b/test/oban/job_test.exs @@ -89,6 +89,12 @@ defmodule Oban.JobTest do } end + test ":unique will translate period" do + changeset = Job.new(%{}, worker: Fake, unique: [period: {1, :hour}]) + + assert %{period: 3600} = changeset.changes[:unique] + end + test ":unique does not accept other types of values or options" do assert Job.new(%{}, worker: Fake, unique: true).errors[:unique] assert Job.new(%{}, worker: Fake, unique: []).errors[:unique] diff --git a/test/oban/worker_test.exs b/test/oban/worker_test.exs index f672a1f5..4a8c1443 100644 --- a/test/oban/worker_test.exs +++ b/test/oban/worker_test.exs @@ -23,7 +23,7 @@ defmodule Oban.WorkerTest do max_attempts: @max_attempts, priority: 1, tags: ["scheduled", "special"], - unique: [fields: [:queue, :worker], period: 60, states: [:scheduled]] + unique: [fields: [:queue, :worker], period: {1, :minute}, states: [:scheduled]] @impl Worker def perform(%{attempt: attempt}) when attempt > 1, do: attempt