fix(snmp): stop the pfsense module walking pfTablesAddrTable - #18
Merged
Conversation
morpheus reported down with "context deadline exceeded" at exactly 45.00s — Prometheus's scrape_timeout — while its SNMP community worked fine from the monitoring host. The walk could not finish. The module walked 1.3.6.1.4.1.12325.1, commented "BEGEMOT-PF-MIB::begemotPf". The comment states the intent correctly; the OID is one level too high. begemotPf is 12325.1.200, so 12325.1 also swept in begemotSnmpd (12325.1.1). The real cost, though, is inside pfTables (.1.9), which holds two tables of very different size. Measured against morpheus: .1.9.1 pfTablesTblNumber 1 row 0.1s .1.9.2 pfTablesTblTable 323 rows 0.4s .1.9.3 pfTablesAddrTable 50,917+ rows >20s, unfinished pfTablesAddrTable is one row per address in every pf table — every entry of every blocklist. A full walk of 12325.1 returned 116,360 rows in 90s without completing, against ~1,800 rows for all of begemotPf otherwise. So the walk enumerates begemotPf's subtrees and descends into .1.9 to take the table summaries while excluding the address table. Nothing queries the pfTablesAddr* metrics: the alerts use pfStatusRunning, pfStateTableCount, pfLimitsStates and pfCounterMemDrop; the dashboards add pfSrcNodes* and pfStatusRuntime. The 21 pfTablesTbl* metrics are retained. This could not be fixed by raising scrape_timeout: it was already 45s against a 60s scrape_interval, and the walk needed well over 90s. Regeneration drops exactly the 13 pfTablesAddr* metrics (1805 -> 1792), which is why `make snmp-generate` warns about metric loss here. That is intended. Result: pfSense scrape went from a 45.00s timeout to 0.19s. Also explains SnmpScrapeSlow (network.rules.yaml, 30s threshold) — the slowness was alerted on but never traced. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gerrrt
added a commit
that referenced
this pull request
Aug 19, 2026
fix(snmp): stop the pfsense module walking pfTablesAddrTable
Gerrrt
added a commit
that referenced
this pull request
Aug 19, 2026
…oad` `docker compose up -d` recreates a container only when its *service definition* changes — image, command, mounts, environment. The contents of a bind-mounted file are invisible to it. snmp-exporter parses its config once at startup, so `scripts/render-config.sh` could write a brand-new snmp.yaml, `make up` report success, and the exporter go on serving the config it parsed minutes earlier. That is not hypothetical. In PR #18 the corrected pfSense walk was rendered to disk, `make up` ran clean, and the exporter kept timing out at 45s until someone ran `docker compose restart snmp-exporter` by hand. The failure mode is the bad one: the tool says done, and only the target disagrees. The mechanism to fix it already existed. `make reload` has been POSTing /-/reload to all three config-reading services since the rotation tooling landed; `make up` simply never called it. So this is wiring, not a new capability — no unconditional `docker compose restart`, which would drop the TSDB head on Prometheus and is heavier than the reload each service already serves. Extracted into scripts/reload-config.sh because the retry does not fit in Makefile recipe lines, and because `up` and `reload` now need the same logic. Three outcomes, deliberately distinguished: wget exit 4 running but not yet listening — retry 1s, up to RELOAD_TIMEOUT (60s). `up -d` returns when containers are started, not when they are ready. wget exit 8 the service answered and refused: the config on disk does not parse. Waiting cannot fix that, so fail immediately and say it is still serving the previous config. not running stopped or crash-looping (inspect reports `restarting`) — fail immediately with `make ps` advice rather than burning the timeout rediscovering it. Failing is the point. The obvious way to stop a slow box breaking `make up` is to swallow reload errors, which rebuilds the exact defect this closes: `make up` reporting success over a stale config. A reload that never lands is a failed deploy and now says so. `compose ps -q` plus `docker inspect -f`, not `compose ps --format '{{.State}}'`: custom Go templates only reached `compose ps` in a later Compose v2, and on an older one the template is read as a literal format name, so every service looks stopped and `make up` fails on a healthy stack. Widened to prometheus and alertmanager as well. Their configs are bind mounts with the identical staleness problem, `make reload` already treated the three as a unit, and alertmanager.yaml changes do need a reload even though the rendered webhook_url does not (url_file is read at notify time). Docs carried the inverse of the truth. rotate-snmp-community.md said `make up` "also works and is not wrong, it just does more than is needed" — it did less than was needed, and would have walked the next operator into PR #18. deploy-stack.md's "recreates only what changed" was the misconception itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
morpheusreported down withcontext deadline exceededat exactly 45.00s — Prometheus'sscrape_timeout— while its SNMP community worked fine when tested directly from the monitoring host. The credential was never the problem; the walk simply could not finish.Cause
The module walked
1.3.6.1.4.1.12325.1, commented# BEGEMOT-PF-MIB::begemotPf. The comment states the intent correctly — the OID is one level too high.begemotPfis12325.1.**200**, so12325.1also swept inbegemotSnmpd(12325.1.1).The real cost is inside
pfTables(.1.9), which holds two tables of wildly different size. Measured against morpheus:.1.9.1.1.9.2.1.9.3pfTablesAddrTableis one row per address in every pf table — every entry of every blocklist. A full walk of12325.1returned 116,360 rows in 90s without completing, against ~1,800 rows for all ofbegemotPfotherwise. It grows with every blocklist added.This could not be fixed by raising
scrape_timeout: already 45s against a 60sscrape_interval, and the walk needed well over 90s.Fix
Enumerate
begemotPf's subtrees, descending into.1.9to keep the table summaries while excluding the address table.Nothing queries the dropped metrics — alerts use
pfStatusRunning,pfStateTableCount,pfLimitsStates,pfCounterMemDrop; dashboards addpfSrcNodes*andpfStatusRuntime. All 21pfTablesTbl*metrics are retained.Note on the metric-count guard
make snmp-generatewarns here:That is intended. The 13 are exactly the
pfTablesAddr*set — verified by diffing the metric-name sets, not by trusting the count:Result
Deployed and verified on the monitoring host:
up=0up=1Metrics arriving:
pfStatusRunning=1,pfStateTableCount=932,pfLimitsStates=3248000.Deploy note
snmp.yamlis a bind-mounted file andsnmp_exporterreads it at startup.docker compose up -ddoes not recreate a container when only a mounted file's contents change, somake upleft the old config running in memory.docker compose restart snmp-exporterwas required. Worth addressing separately —make upcurrently cannot apply a rendered-config change to this service.Also explains
SnmpScrapeSlow(network.rules.yaml:28) at its 30s threshold — the slowness was alerted on for a long time but never traced.🤖 Generated with Claude Code