Previous commit (3c988a5f) switched oban_pro to the licensed hex repo at deploy time. Reverting that — keep all three Pro packages vendored so prod image builds don't depend on oban.pro reachability or auth on every CI run. oban_pro 1.7.0 dropped into vendor/oban_pro via `mix hex.package fetch oban_pro 1.7.0 --repo=oban --unpack`. mix.exs goes back to `path: "vendor/oban_pro"` (matching oban_met / oban_web, which were already vendored — and stay vendored since the licensed repo only has older versions of those: 0.1.11 / 2.10.6 vs the 1.1.0 / 2.12.1 we vendor). Schema migration from3c988a5fstays — 1.7.0 tables/indexes are already applied. No code changes.
88 lines
2.5 KiB
Elixir
88 lines
2.5 KiB
Elixir
defmodule Oban.Pro.Unique do
|
|
@moduledoc false
|
|
|
|
alias Ecto.Changeset
|
|
alias Oban.Job
|
|
alias Oban.Pro.Utils
|
|
|
|
@states_to_ints %{
|
|
"scheduled" => 0,
|
|
"available" => 1,
|
|
"executing" => 2,
|
|
"retryable" => 3,
|
|
"completed" => 4,
|
|
"cancelled" => 5,
|
|
"discarded" => 6,
|
|
"suspended" => 7
|
|
}
|
|
|
|
@spec replace?(Job.changeset()) :: boolean()
|
|
def replace?(changeset), do: match?(%{changes: %{replace: _}}, changeset)
|
|
|
|
@spec unique?(Job.changeset()) :: boolean()
|
|
def unique?(changeset), do: match?(%{changes: %{unique: %{}}}, changeset)
|
|
|
|
@spec get_key(Job.changeset(), any()) :: any()
|
|
def get_key(changeset, default \\ nil)
|
|
|
|
def get_key(%{changes: %{meta: %{uniq_key: uniq_key}}}, _default), do: uniq_key
|
|
def get_key(_changeset, default), do: default
|
|
|
|
@spec get_states(Job.changeset()) :: [atom()]
|
|
def get_states(%{changes: %{unique: %{states: states}}}), do: states
|
|
|
|
# For backward compatibility, `unique` works with non-pro workers, so it can't be injected into
|
|
# the meta through a stage.
|
|
@spec with_uniq_meta(Job.changeset()) :: Job.changeset()
|
|
def with_uniq_meta(%{changes: %{unique: %{period: _}}} = changeset) do
|
|
uniq_meta = %{uniq: true, uniq_bmp: gen_bmp(changeset), uniq_key: gen_key(changeset)}
|
|
|
|
meta =
|
|
changeset
|
|
|> Changeset.get_change(:meta, %{})
|
|
|> Map.merge(uniq_meta)
|
|
|
|
Changeset.put_change(changeset, :meta, meta)
|
|
end
|
|
|
|
def with_uniq_meta(changeset), do: changeset
|
|
|
|
@spec gen_bmp(Job.changeset()) :: [integer()]
|
|
def gen_bmp(%{changes: %{unique: %{states: states}}}) do
|
|
for state <- states, do: Map.fetch!(@states_to_ints, to_string(state))
|
|
end
|
|
|
|
@spec gen_key(Job.changeset()) :: String.t()
|
|
def gen_key(%{changes: %{unique: unique}} = changeset) do
|
|
%{fields: fields, keys: keys, period: period, timestamp: timestamp} = unique
|
|
|
|
data =
|
|
fields
|
|
|> Enum.sort()
|
|
|> Enum.map(fn
|
|
:args -> Utils.take_keys(changeset, :args, keys)
|
|
:meta -> Utils.take_keys(changeset, :meta, keys)
|
|
field -> Changeset.get_field(changeset, field)
|
|
end)
|
|
|
|
data =
|
|
if period == :infinity do
|
|
data
|
|
else
|
|
[truncate(period, Changeset.get_field(changeset, timestamp)) | data]
|
|
end
|
|
|
|
Utils.hash64(data)
|
|
end
|
|
|
|
defp truncate(period, timestamp) do
|
|
now = timestamp || DateTime.utc_now()
|
|
|
|
now
|
|
|> DateTime.to_unix(:second)
|
|
|> rem(period)
|
|
|> then(&DateTime.add(now, -&1))
|
|
|> DateTime.truncate(:second)
|
|
|> DateTime.to_iso8601()
|
|
end
|
|
end
|