Part of #3 (analysis and plan in the comment dated 2026-09-21 14:34 there).
asPaths in internal/routewatch/peeringhandler.go:50 keeps every distinct AS path of the last 30 minutes (measured 1.1 M entries, ~184 bytes each, still growing). Every 30 s processPeerings copies the whole map and calls RecordPeering for every peering in the whole window, 129-150 k transactions per run, so it runs almost continuously and drives a 300 MiB heap sawtooth.
Requirements
processPeerings takes the current map and replaces it with a fresh empty one under the lock (no copy), so each path is processed once. RecordPeering already upserts last_seen, so stored results are unchanged.
Add a constant maxTrackedPaths = 500000; when the map is full, new paths are dropped and counted; expose the count where the handler's other metrics are exposed, if there is such a place, otherwise log it once per run.
Remove the 30-minute prune and its ticker, which the swap makes dead code.
Files: internal/routewatch/peeringhandler.go and its test.
Definition of done
Tests: the map is empty after ProcessPeeringsNow; a path seen before and after a run is recorded in both runs; the cap is enforced and the drop counter counts.
make check green. Commit title ends (closes #N).
Model: fable-5-1
Part of https://git.eeqj.de/sneak/routewatch/issues/3 (analysis and plan in the comment dated 2026-09-21 14:34 there).
`asPaths` in `internal/routewatch/peeringhandler.go:50` keeps every distinct AS path of the last 30 minutes (measured 1.1 M entries, ~184 bytes each, still growing). Every 30 s `processPeerings` copies the whole map and calls `RecordPeering` for every peering in the whole window, 129-150 k transactions per run, so it runs almost continuously and drives a 300 MiB heap sawtooth.
## Requirements
- `processPeerings` takes the current map and replaces it with a fresh empty one under the lock (no copy), so each path is processed once. `RecordPeering` already upserts `last_seen`, so stored results are unchanged.
- Add a constant `maxTrackedPaths = 500000`; when the map is full, new paths are dropped and counted; expose the count where the handler's other metrics are exposed, if there is such a place, otherwise log it once per run.
- Remove the 30-minute prune and its ticker, which the swap makes dead code.
- Files: `internal/routewatch/peeringhandler.go` and its test.
## Definition of done
- Tests: the map is empty after `ProcessPeeringsNow`; a path seen before and after a run is recorded in both runs; the cap is enforced and the drop counter counts.
- `make check` green. Commit title ends ` (closes #N)`.
Model: fable-5-1
processPeerings now swaps the AS-path map for a fresh empty one under the lock instead of copying it, so each path is processed once and the memory is freed each run; RecordPeering still upserts, so stored peerings are unchanged. New paths are dropped and counted once the map reaches maxTrackedPaths (500000), with the drop count logged each run (the handler has no metrics sink). The 30-minute prune and its ticker are gone as dead code.
Tests: a run empties the map; a path seen before and after a run is recorded in both runs; the cap drops and counts new paths while refreshing an already-tracked one.
Disclosure: host make check cannot run here (host golangci-lint built with Go 1.25 panics on the Go 1.26 host toolchain); gated instead on the repo's authoritative Docker build, which is green.
Model: opus-4-8
PR: https://git.eeqj.de/sneak/routewatch/pulls/16
`processPeerings` now swaps the AS-path map for a fresh empty one under the lock instead of copying it, so each path is processed once and the memory is freed each run; `RecordPeering` still upserts, so stored peerings are unchanged. New paths are dropped and counted once the map reaches `maxTrackedPaths` (500000), with the drop count logged each run (the handler has no metrics sink). The 30-minute prune and its ticker are gone as dead code.
Tests: a run empties the map; a path seen before and after a run is recorded in both runs; the cap drops and counts new paths while refreshing an already-tracked one.
Disclosure: host `make check` cannot run here (host `golangci-lint` built with Go 1.25 panics on the Go 1.26 host toolchain); gated instead on the repo's authoritative Docker build, which is green.
Model: opus-4-8
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of #3 (analysis and plan in the comment dated 2026-09-21 14:34 there).
asPathsininternal/routewatch/peeringhandler.go:50keeps every distinct AS path of the last 30 minutes (measured 1.1 M entries, ~184 bytes each, still growing). Every 30 sprocessPeeringscopies the whole map and callsRecordPeeringfor every peering in the whole window, 129-150 k transactions per run, so it runs almost continuously and drives a 300 MiB heap sawtooth.Requirements
processPeeringstakes the current map and replaces it with a fresh empty one under the lock (no copy), so each path is processed once.RecordPeeringalready upsertslast_seen, so stored results are unchanged.maxTrackedPaths = 500000; when the map is full, new paths are dropped and counted; expose the count where the handler's other metrics are exposed, if there is such a place, otherwise log it once per run.internal/routewatch/peeringhandler.goand its test.Definition of done
ProcessPeeringsNow; a path seen before and after a run is recorded in both runs; the cap is enforced and the drop counter counts.make checkgreen. Commit title ends(closes #N).Model: fable-5-1
PR: #16
processPeeringsnow swaps the AS-path map for a fresh empty one under the lock instead of copying it, so each path is processed once and the memory is freed each run;RecordPeeringstill upserts, so stored peerings are unchanged. New paths are dropped and counted once the map reachesmaxTrackedPaths(500000), with the drop count logged each run (the handler has no metrics sink). The 30-minute prune and its ticker are gone as dead code.Tests: a run empties the map; a path seen before and after a run is recorded in both runs; the cap drops and counts new paths while refreshing an already-tracked one.
Disclosure: host
make checkcannot run here (hostgolangci-lintbuilt with Go 1.25 panics on the Go 1.26 host toolchain); gated instead on the repo's authoritative Docker build, which is green.Model: opus-4-8