Skip to content

fix(dhcp): de-duplicate DHCP lease label sets - #76

Open
woobins wants to merge 1 commit into
henrywhitaker3:mainfrom
woobins:fix-dhcp-leases-dedup
Open

fix(dhcp): de-duplicate DHCP lease label sets#76
woobins wants to merge 1 commit into
henrywhitaker3:mainfrom
woobins:fix-dhcp-leases-dedup

Conversation

@woobins

@woobins woobins commented Jun 13, 2026

Copy link
Copy Markdown

When AdGuard returns duplicate DHCP lease rows, /metrics fails with HTTP 500:

collected metric "adguard_dhcp_leases" { ... } was collected before with the same name and label values

A single duplicate lease takes down the entire scrape (every metric, not just leases), so Prometheus reads the exporter as down even though AdGuard itself is healthy. I have seen AdGuard emit duplicate rows after an in-place self-update (lease-table rebuild) and when several clients behind a MAC-NAT'ing wifi repeater share one upstream MAC.

Fix

De-duplicate on the full label set in DhcpLeasesServer.Collect before emitting. The metric set is built under the existing mutex (Collect previously read the leases map unlocked while the worker's Record writes it, which go test -race flags as a data race) and sent to the channel after unlocking so a slow scrape cannot stall Record.

Adds a regression test using a pedantic registry; it fails on the current collector and passes with the fix.

AdGuard can return duplicate DHCP lease rows (e.g. an in-place self-update
rebuilds the lease table, or several clients behind a MAC-NAT'ing wifi
repeater share one upstream MAC). DhcpLeasesServer.Collect emitted one
metric per lease with no de-duplication, so a repeated label tuple made the
registry fail the entire /metrics scrape with:

    collected metric "adguard_dhcp_leases" { ... } was collected before with
    the same name and label values

i.e. a single duplicate lease 500s the whole exporter and reads as down,
even though AdGuard itself is healthy.

De-duplicate on the full label set before emitting. The metric set is built
under the existing mutex (Collect previously read the leases map unlocked
while the worker's Record writes it, which is a data race) and sent to the
channel after unlocking so a slow scrape cannot stall Record. Adds a
regression test using a pedantic registry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant