From e2eaed5c7474a04babeb1a699d1456d86869bab0 Mon Sep 17 00:00:00 2001 From: sneak Date: Tue, 20 Oct 2026 01:20:21 +0000 Subject: [PATCH] Reset the peering handler's dropped_paths figure after each log line (closes #116) processPeerings replaced the path map every run but kept the count of paths dropped while the map was full, so every later "Processed AS peerings" line reported all drops since start next to that run's own figures. The count is now set to 0 where the map is replaced, under the same lock. A test fills the map, has one path dropped, runs the processing and finds the count back at 0. Model: opus-5-5 --- internal/routewatch/peeringhandler.go | 6 ++-- .../peeringhandler_internal_test.go | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/internal/routewatch/peeringhandler.go b/internal/routewatch/peeringhandler.go index b1cddc4..8e5f801 100644 --- a/internal/routewatch/peeringhandler.go +++ b/internal/routewatch/peeringhandler.go @@ -46,7 +46,7 @@ type PeeringHandler struct { // and the latest time an update carried it. mu sync.Mutex asPaths map[string]database.SeenTimes - droppedPaths int // paths dropped because the map was full + droppedPaths int // paths dropped since the last run because the map was full // writing is how many paths processPeerings took from asPaths and has // not finished writing the peerings of. writing int @@ -190,12 +190,14 @@ func (h *PeeringHandler) processLoop() { func (h *PeeringHandler) processPeerings() { // Take the accumulated paths and replace the map with a fresh empty one // under the lock. Each path is processed exactly once and the memory is - // released, so the map never grows past a single interval's traffic. + // released, so the map never grows past a single interval's traffic. The + // count of dropped paths starts again with the new map. h.mu.Lock() paths := h.asPaths h.asPaths = make(map[string]database.SeenTimes) h.writing = len(paths) dropped := h.droppedPaths + h.droppedPaths = 0 h.mu.Unlock() if len(paths) == 0 { diff --git a/internal/routewatch/peeringhandler_internal_test.go b/internal/routewatch/peeringhandler_internal_test.go index 75bc05a..6cbac03 100644 --- a/internal/routewatch/peeringhandler_internal_test.go +++ b/internal/routewatch/peeringhandler_internal_test.go @@ -264,6 +264,36 @@ func TestPeeringHandlerCapDropsAndCounts(t *testing.T) { } } +// TestPeeringHandlerRunResetsTheDroppedCount fills the map, has the handler +// drop one path, and runs the processing. The count of dropped paths is the +// run's own, so it is 0 again after the run. +func TestPeeringHandlerRunResetsTheDroppedCount(t *testing.T) { + t.Parallel() + + h := newTestHandler(newMockStore()) + + now := time.Now().UTC() + + // Paths of one AS carry no peering, so the run records nothing. + h.mu.Lock() + for asn := 1; len(h.asPaths) < maxTrackedPaths; asn++ { + h.asPaths[pathKey(t, asn)] = database.SeenTimes{First: now, Last: now} + } + h.mu.Unlock() + + handle(h, now, testASNA, testASNB) + + if _, dropped := snapshot(h); dropped != 1 { + t.Fatalf("want dropped count 1 with the map full, got %d", dropped) + } + + h.ProcessPeeringsNow() + + if _, dropped := snapshot(h); dropped != 0 { + t.Errorf("want dropped count 0 after a run, got %d", dropped) + } +} + // TestPeeringHandlerStoresTheEarliestAndLatestTimesOfANewPeering handles two // updates whose path carries a peering the database does not hold yet, the // second 5 seconds after the first, in one run into a real database. The -- 2.54.0