- TOCTOU race: add partial unique index + rescue constraint violation on concurrent contact insert (radio.ex:737-768) - Mass assignment: remove :user_id from @optional_fields (contact.ex:85) - Stale path_live result: clear result when URL params change (path_live.ex:105) - Blocking migrations: wrap Release.migrate() in Task.start (application.ex:79) - Rate limiter: fix guard for monitor tokens (not is_nil vs is_binary) - findings.md: document remaining non-critical bugs and improvements
44 lines
1.4 KiB
Elixir
44 lines
1.4 KiB
Elixir
defmodule Microwaveprop.Repo.Migrations.AddContactsDedupIndex do
|
|
use Ecto.Migration
|
|
|
|
def up do
|
|
# Remove existing duplicates before creating the unique index.
|
|
# Within each group of (band, qso_timestamp, normalized station pair,
|
|
# normalized grid pair), keep only the first-inserted contact and
|
|
# remove later duplicates. Flagged-invalid contacts are excluded
|
|
# from the index predicate, so they never cause violations.
|
|
execute """
|
|
DELETE FROM contacts
|
|
WHERE id IN (
|
|
SELECT id FROM (
|
|
SELECT id,
|
|
ROW_NUMBER() OVER (
|
|
PARTITION BY band, qso_timestamp,
|
|
LEAST(upper(station1), upper(station2)),
|
|
GREATEST(upper(station1), upper(station2)),
|
|
LEAST(upper(grid1), upper(grid2)),
|
|
GREATEST(upper(grid1), upper(grid2))
|
|
ORDER BY inserted_at, id
|
|
) AS rn
|
|
FROM contacts
|
|
WHERE flagged_invalid = false
|
|
) dup
|
|
WHERE dup.rn > 1
|
|
)
|
|
"""
|
|
|
|
execute """
|
|
CREATE UNIQUE INDEX contacts_dedup_idx ON contacts
|
|
(band, qso_timestamp,
|
|
LEAST(upper(station1), upper(station2)),
|
|
GREATEST(upper(station1), upper(station2)),
|
|
LEAST(upper(grid1), upper(grid2)),
|
|
GREATEST(upper(grid1), upper(grid2)))
|
|
WHERE flagged_invalid = false
|
|
"""
|
|
end
|
|
|
|
def down do
|
|
execute "DROP INDEX contacts_dedup_idx"
|
|
end
|
|
end
|