Categories addressed: - pattern_match / pattern_match_cov (32): remove dead case/with clauses that dialyzer proved unreachable from the caller types. - contract_supertype / extra_range / invalid_contract / contract_with_opaque (25): narrow @spec declarations to match actual success typings. - call / call_without_opaque (18): fix bad calls, narrow User.t to allow nil for in-memory changeset structs, suppress Ecto.Multi opaque-type false positives with targeted @dialyzer directives. - guard_fail / no_return / unused_fun / unknown_function (13): remove dead || fallbacks, simplify always-true params, cascade-resolve no_returns via the underlying pattern_match and call fixes. Real production bug fixed: StormDetector.handle_cast/2 had swapped `:queue.in` args (`queue |> :queue.in(ts)` which desugars to `:queue.in(queue, ts)` — wrong argument order). Alert timestamps were never being enqueued, so storm detection would fail at runtime. Corrected to `ts |> :queue.in(queue)`. .dialyzer_ignore.exs: suppress two genuine dep-PLT gaps (:ranch.get_addr/1 false positive from Bandit's transitive ranch, and the Cloak.Vault GenServer callback_info on the CI build path). `mix dialyzer` now: Total errors: 114, Skipped: 114 — passes clean. Warnings: 88 → 0.
47 lines
1.2 KiB
Elixir
47 lines
1.2 KiB
Elixir
defmodule Mix.Tasks.Oban.CancelStuckDiscovery do
|
|
@shortdoc "Cancels stuck DiscoveryWorker jobs"
|
|
|
|
@moduledoc """
|
|
Cancels all stuck DiscoveryWorker jobs that are in scheduled or retryable state.
|
|
|
|
## Usage
|
|
|
|
# In production via kubectl:
|
|
kubectl exec -n towerops deployment/towerops -- /app/bin/towerops rpc "Mix.Tasks.Oban.CancelStuckDiscovery.run([])"
|
|
|
|
# Locally:
|
|
mix oban.cancel_stuck_discovery
|
|
"""
|
|
use Mix.Task
|
|
|
|
import Ecto.Query
|
|
|
|
require Logger
|
|
|
|
@impl Mix.Task
|
|
def run(_args) do
|
|
Mix.Task.run("app.start")
|
|
|
|
cancelled_count =
|
|
Oban.Job
|
|
|> where([j], j.worker == "Towerops.Workers.DiscoveryWorker")
|
|
|> where([j], j.state in ["scheduled", "retryable", "executing"])
|
|
|> Towerops.Repo.all()
|
|
|> Enum.map(fn job ->
|
|
:ok = Oban.cancel_job(Oban, job.id)
|
|
|
|
Logger.info(
|
|
"Cancelled stuck discovery job",
|
|
job_id: job.id,
|
|
device_id: get_in(job.args, ["device_id"]),
|
|
state: job.state,
|
|
attempted_at: job.attempted_at
|
|
)
|
|
|
|
1
|
|
end)
|
|
|> Enum.sum()
|
|
|
|
Mix.shell().info("Cancelled #{cancelled_count} stuck discovery jobs")
|
|
end
|
|
end
|