diff --git a/lib/towerops/workers/discovery_worker.ex b/lib/towerops/workers/discovery_worker.ex index 48f891f5..09a381d9 100644 --- a/lib/towerops/workers/discovery_worker.ex +++ b/lib/towerops/workers/discovery_worker.ex @@ -16,7 +16,9 @@ defmodule Towerops.Workers.DiscoveryWorker do If no agent is assigned or the agent is offline, discovery falls back to direct SNMP operations from the Phoenix cluster. """ - use Oban.Worker, queue: :discovery + use Oban.Worker, + queue: :discovery, + unique: [period: 60, states: [:available, :scheduled, :executing]] alias Towerops.Agents alias Towerops.Devices @@ -94,11 +96,49 @@ defmodule Towerops.Workers.DiscoveryWorker do @doc """ Enqueues a discovery job for a device. + + Uses Oban unique constraint to prevent duplicate jobs within 60 seconds. """ def enqueue(device_id) do - %{device_id: device_id} - |> new() - |> Oban.insert() + # Log who's enqueueing this to track down runaway triggers + caller_module = + self() + |> Process.info(:current_stacktrace) + |> elem(1) + |> Enum.at(1) + |> elem(0) + + result = + %{device_id: device_id} + |> new() + |> Oban.insert() + + case result do + {:ok, %Oban.Job{conflict?: true}} -> + Logger.debug( + "Discovery job already enqueued for device, skipping duplicate", + device_id: device_id, + caller: caller_module + ) + + {:ok, _job} -> + Logger.debug( + "Discovery job enqueued for device", + device_id: device_id, + caller: caller_module + ) + + {:error, _} = error -> + Logger.warning( + "Failed to enqueue discovery job", + device_id: device_id, + caller: caller_module + ) + + error + end + + result end # Private helpers