Files
adamandClaude Opus 5.5 33060f86ce Don't count a per-view counter going down as a restart of named
After a reconfig that changed named's views, the cache and resolver
counters (summed over views) went down, which was taken for a restart:
every counter then counted its total since named started for one minute.
Only a new boot time means a restart now; a counter that goes down
counts 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:51:24 +01:00

62 lines
2.5 KiB
Go

package stats
import "testing"
// A trimmed /json/v1/server answer from named 9.20.24.
const sampleJSON = `{"json-stats-version":"1.8","boot-time":"2026-10-07T19:46:51.322Z",
"opcodes":{"QUERY":10,"IQUERY":0,"NOTIFY":0},
"rcodes":{"NOERROR":7,"NXDOMAIN":2,"SERVFAIL":1,"RESERVED11":0,"17":0},
"qtypes":{"A":6,"AAAA":3,"MX":1},
"nsstats":{"Requestv4":10,"QryAuthAns":4,"QryNoauthAns":6,"QryRecursion":2,"QryUDP":9,"QryTCP":1},
"views":{"internal":{"resolver":{"stats":{"QryRTT100":3,"QryRTT500":1,"ValOk":2},"cachestats":{"QueryHits":4,"QueryMisses":2,"CacheHits":345}}},
"_bind":{"resolver":{"stats":{},"cachestats":{}}}}}`
func TestFlattenAndDelta(t *testing.T) {
s1, err := Flatten([]byte(sampleJSON))
if err != nil {
t.Fatal(err)
}
want := map[string]int64{"queries": 10, "rcode:NOERROR": 7, "rcode:NXDOMAIN": 2, "rcode:SERVFAIL": 1, "qtype:A": 6, "qtype:AAAA": 3, "qtype:MX": 1,
"answer:authoritative": 4, "answer:recursive": 6, "recursion": 2, "transport:udp": 9, "transport:tcp": 1,
"rtt:10-100ms": 3, "rtt:100-500ms": 1, "dnssec:valid": 2, "cache:hits": 4, "cache:misses": 2}
for k, v := range want {
if s1.Values[k] != v {
t.Errorf("%s = %d, want %d", k, s1.Values[k], v)
}
}
if len(s1.Values) != len(want) {
t.Errorf("unexpected keys: %v", s1.Values)
}
// Normal change.
s2 := &Snapshot{BootTime: s1.BootTime, Values: map[string]int64{}}
for k, v := range s1.Values {
s2.Values[k] = v
}
s2.Values["queries"] += 5
s2.Values["qtype:A"] += 5
d := Delta(s1, s2)
if d["queries"] != 5 || d["qtype:A"] != 5 || d["qtype:AAAA"] != 0 || len(d) != 2 {
t.Errorf("delta = %v", d)
}
// Quiet minute: queries is still recorded (as 0) so coverage is visible.
if d := Delta(s2, s2); len(d) != 1 || d["queries"] != 0 {
t.Errorf("quiet delta = %v", d)
}
// named restarted: counters start again from zero.
s3 := &Snapshot{BootTime: "2026-10-07T20:00:00.000Z", Values: map[string]int64{"queries": 3, "qtype:A": 3}}
if d := Delta(s2, s3); d["queries"] != 3 || d["qtype:A"] != 3 {
t.Errorf("restart delta = %v", d)
}
// A counter going down without a restart (views changed in a
// reconfig) counts 0, and doesn't make the others count from zero.
s4 := &Snapshot{BootTime: s2.BootTime, Values: map[string]int64{}}
for k, v := range s2.Values {
s4.Values[k] = v
}
s4.Values["queries"] += 4
s4.Values["cache:hits"] = 1 // was 4: a view's cache counters are gone
if d := Delta(s2, s4); d["queries"] != 4 || d["cache:hits"] != 0 || len(d) != 1 {
t.Errorf("delta with a counter going down = %v", d)
}
}