-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathseries_counters_seeded_test.go
More file actions
88 lines (81 loc) · 3.49 KB
/
Copy pathseries_counters_seeded_test.go
File metadata and controls
88 lines (81 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package main
import (
"os"
"path/filepath"
"regexp"
"runtime"
"sort"
"strings"
"testing"
)
// TestSeriesCountersAreSeededAndExported guards the two lists that have to move
// together whenever the chart-aggregate cache learns a new outcome, and which
// nothing else checks: seedKnownCounters in this file's package (main.go) and
// the named-family table in writeNamedStats (internal/web/web.go).
//
// The failure this exists to catch has already happened twice. A state is added
// to Store.Series, the store's own tests pass because they read the counter
// directly, and the two lists are left behind - so the new counter is absent
// from a scrape until its first increment (unseeded counters spring into
// existence at 1, which rate() and increase() cannot see across that boundary)
// and, if it is also missing from writeNamedStats, has no named family at all.
// The visible symptom is a scrape whose numbers do not reconcile: main.go's
// seeding comment states the relation the families are read against each other
// with, queries = outcomes - hits, and a missing member silently breaks it.
//
// Scanning the store's source rather than hardcoding the names is the point.
// A fixed list here would need the same manual update as the two lists it
// guards, and would therefore go stale in exactly the case the test exists for.
func TestSeriesCountersAreSeededAndExported(t *testing.T) {
_, thisFile, ok := callerFile()
if !ok {
t.Fatal("runtime.Caller failed; cannot locate the module root")
}
root := filepath.Dir(thisFile) // this file lives at the module root
read := func(rel string) string {
b, err := os.ReadFile(filepath.Join(root, rel))
if err != nil {
t.Fatalf("read %s: %v", rel, err)
}
return string(b)
}
// Every stats.Inc("series...") the store records. The store is the only
// producer: these counters describe its cache, and a counter incremented
// anywhere else would not be part of the queries = outcomes - hits relation.
storeSrc := read(filepath.Join("internal", "store", "store.go"))
recorded := regexp.MustCompile(`stats\.Inc\("(series\.[a-z.]+)"\)`).FindAllStringSubmatch(storeSrc, -1)
if len(recorded) == 0 {
t.Fatal("no stats.Inc(\"series.*\") calls found in internal/store/store.go: " +
"either the counters were removed, or the call shape changed and this guard now " +
"scans for something that no longer exists, which would let it pass vacuously")
}
seen := map[string]bool{}
var keys []string
for _, m := range recorded {
if !seen[m[1]] {
seen[m[1]] = true
keys = append(keys, m[1])
}
}
sort.Strings(keys)
mainSrc := read("main.go")
webSrc := read(filepath.Join("internal", "web", "web.go"))
for _, k := range keys {
quoted := `"` + k + `"`
if !strings.Contains(mainSrc, quoted) {
t.Errorf("internal/store records %s but main.go never names it: an unseeded counter is "+
"absent from /metrics until its first increment, so its family appears mid-series and "+
"rate() cannot see the step. Add it to seedKnownCounters.", k)
}
if !strings.Contains(webSrc, quoted) {
t.Errorf("internal/store records %s but internal/web/web.go never names it: with no entry "+
"in writeNamedStats it has no pingularity_series_* family, so a scrape carries a query "+
"count its outcome counters cannot account for. Add it to the named-family table.", k)
}
}
}
// callerFile isolates the runtime.Caller call so the test body reads plainly.
func callerFile() (uintptr, string, bool) {
pc, file, _, ok := runtime.Caller(1)
return pc, file, ok
}