fix: handle duplicate MAC address entries gracefully
Problem: upsert_mac_address was using Repo.one() which crashes with Ecto.MultipleResultsError when duplicate MAC address records exist in the database. Root Cause: Race condition or historical data corruption created duplicate entries for the same (device_id, mac_address, vlan_id) tuple, violating the unique constraint. Fix: - Changed Repo.one() to Repo.all() to handle duplicates gracefully - When duplicates found: keep first entry, delete extras, log warning - Includes device_id, mac_address, vlan_id, and duplicate IDs in log - Auto-cleans corrupted data while maintaining service availability Example Log Output: Found 1 duplicate MAC address entries, cleaning up device_id=f3ff19d5-b519-4327-9f7d-dc416e494cfa mac_address=78:9a:18:52:b1:c6 vlan_id=nil duplicate_ids=[...] Impact: - Prevents polling crashes when duplicate MAC entries exist - Self-healing: automatically cleans up database corruption - Maintains data integrity while preserving service availability Related: Issue 2.9 from bugs analysis (ARP/MAC parsing issues) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
cedbc5abd1
commit
2b4cb5c3f5
1 changed files with 25 additions and 3 deletions
|
|
@ -1570,6 +1570,8 @@ defmodule Towerops.Snmp do
|
|||
def upsert_mac_address(attrs) do
|
||||
import Ecto.Query
|
||||
|
||||
require Logger
|
||||
|
||||
# Build query to find existing MAC address entry
|
||||
query =
|
||||
from m in MacAddress,
|
||||
|
|
@ -1583,16 +1585,36 @@ defmodule Towerops.Snmp do
|
|||
vlan_id -> where(query, [m], m.vlan_id == ^vlan_id)
|
||||
end
|
||||
|
||||
case Repo.one(query) do
|
||||
nil ->
|
||||
# Use Repo.all to handle potential duplicates gracefully
|
||||
# If duplicates exist, keep the first one and delete the rest
|
||||
case Repo.all(query) do
|
||||
[] ->
|
||||
%MacAddress{}
|
||||
|> MacAddress.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
|
||||
mac_address ->
|
||||
[mac_address] ->
|
||||
mac_address
|
||||
|> MacAddress.changeset(attrs)
|
||||
|> Repo.update()
|
||||
|
||||
[first | duplicates] ->
|
||||
# Handle duplicates: delete extras, update first
|
||||
Logger.warning("Found #{length(duplicates)} duplicate MAC address entries, cleaning up",
|
||||
device_id: attrs.device_id,
|
||||
mac_address: attrs.mac_address,
|
||||
vlan_id: attrs[:vlan_id],
|
||||
duplicate_ids: Enum.map(duplicates, & &1.id)
|
||||
)
|
||||
|
||||
# Delete duplicate entries
|
||||
duplicate_ids = Enum.map(duplicates, & &1.id)
|
||||
Repo.delete_all(from m in MacAddress, where: m.id in ^duplicate_ids)
|
||||
|
||||
# Update the remaining entry
|
||||
first
|
||||
|> MacAddress.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue