fix: make deduplicate_discovery_checks migration production-safe (#178)

- Add @disable_ddl_transaction and @disable_migration_lock for concurrent index
- Increase statement_timeout to 10min for DELETE operation on large tables
- Use concurrently: true on index creation to avoid table locks
- Refactor detect_airfiber_counter_overrides to fix credo nesting warning
- Prevents timeout errors in production deployment

Reviewed-on: graham/towerops-web#178
This commit is contained in:
Graham McIntire 2026-03-26 10:39:09 -05:00 committed by graham
parent 11f9c4450c
commit a9e82779e7
3 changed files with 23 additions and 14 deletions

View file

@ -1 +1 @@
/nix/store/4xlkmdd947h5qlhj2rmbyavq08grkz27-pre-commit-config.json
/nix/store/zx636v118rzzqzwafgrw2aybh7l0szrl-pre-commit-config.json

View file

@ -1059,17 +1059,13 @@ defmodule Towerops.Workers.DevicePollerWorker do
# needs proprietary counters. Use v1 since these devices may not support v2c.
v1_opts = Keyword.put(client_opts, :version, "1")
# Check for LTU first (more specific)
case Client.get(v1_opts, "1.3.6.1.4.1.41112.1.10.1.2.2.0") do
{:ok, val} when val != nil ->
:airfiber_ltu
_ ->
# Check for regular AirFiber stats table (index field)
case Client.get(v1_opts, "1.3.6.1.4.1.41112.1.3.3.1.1.1") do
{:ok, val} when val != nil -> :airfiber
_ -> nil
end
# Check for LTU first (more specific), fall back to regular AirFiber
with {:ltu, {:error, _}} <- {:ltu, Client.get(v1_opts, "1.3.6.1.4.1.41112.1.10.1.2.2.0")},
{:ok, val} when val != nil <- Client.get(v1_opts, "1.3.6.1.4.1.41112.1.3.3.1.1.1") do
:airfiber
else
{:ltu, {:ok, val}} when val != nil -> :airfiber_ltu
_ -> nil
end
end
end

View file

@ -1,7 +1,14 @@
defmodule Towerops.Repo.Migrations.DeduplicateDiscoveryChecks do
use Ecto.Migration
# Required for CREATE INDEX CONCURRENTLY
@disable_ddl_transaction true
@disable_migration_lock true
def up do
# Increase timeout for DELETE operation on large table
execute("SET statement_timeout = '10min'")
# Delete duplicate auto-discovered checks, keeping the oldest one per
# (device_id, check_type, source_id) combination.
execute("""
@ -21,11 +28,16 @@ defmodule Towerops.Repo.Migrations.DeduplicateDiscoveryChecks do
)
""")
# Reset timeout
execute("RESET statement_timeout")
# Prevent future duplicates for auto-discovered checks.
# Use concurrently: true to avoid locking table during index creation.
create(
unique_index(:checks, [:device_id, :check_type, :source_id],
where: "source_type = 'auto_discovery' AND source_id IS NOT NULL",
name: :checks_device_type_source_unique_index
name: :checks_device_type_source_unique_index,
concurrently: true
)
)
end
@ -33,7 +45,8 @@ defmodule Towerops.Repo.Migrations.DeduplicateDiscoveryChecks do
def down do
drop_if_exists(
index(:checks, [:device_id, :check_type, :source_id],
name: :checks_device_type_source_unique_index
name: :checks_device_type_source_unique_index,
concurrently: true
)
)
end