Bumps vendored Oban Pro from 1.6.13 to 1.7.0, which transitively bumps oban core to 2.22.1 (requires migrations v14) and db_connection to 2.10.0. - mix.exs constraint: ~> 1.6 → ~> 1.7 - vendor/oban_pro/ replaced with v1.7.0 hex package - migration 20260430142200: Oban core v12 → v14 (must run first) - migration 20260430142241: Oban Pro 1.7.0 schema additions Also corrects a pre-existing constraint name in AgentAssignment that was left stale by the equipment→devices rename migration (20260117190134). The unique index is named agent_assignments_device_id_index, but the schema's unique_constraint/3 still pointed at the old equipment_id name, causing the test suite to surface Ecto.ConstraintError instead of a changeset error. Required to get the suite green for this upgrade.
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
|