187 lines
4.7 KiB
Go
187 lines
4.7 KiB
Go
package ingot
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
"git.dvdt.dev/david/ingot/labels"
|
|
)
|
|
|
|
func TestCollectMetrics(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setup func(t *testing.T, db *DB)
|
|
collectCalls int
|
|
wantHeadSeries float64 // expected value after last collectMetrics call
|
|
wantBlocksTotal float64
|
|
wantCompactions float64
|
|
wantMetricCount int // total ingot_* series
|
|
}{
|
|
{
|
|
name: "empty_db",
|
|
setup: func(t *testing.T, db *DB) {},
|
|
// First call snapshots 0 series, writes 5 metric series.
|
|
// Second call snapshots 5 series (the metrics themselves).
|
|
collectCalls: 2,
|
|
wantHeadSeries: 5,
|
|
wantBlocksTotal: 0,
|
|
wantCompactions: 0,
|
|
wantMetricCount: 5,
|
|
},
|
|
{
|
|
name: "with_user_series",
|
|
setup: func(t *testing.T, db *DB) {
|
|
app := db.Appender()
|
|
_, err := app.Append(0, labels.FromStrings("__name__", "temp", "room", "a"), 1000, 1.0)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
_, err = app.Append(0, labels.FromStrings("__name__", "temp", "room", "b"), 1000, 2.0)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if err := app.Commit(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
},
|
|
// 2 user series + 5 metric series = 7
|
|
collectCalls: 2,
|
|
wantHeadSeries: 7,
|
|
wantBlocksTotal: 0,
|
|
wantCompactions: 0,
|
|
wantMetricCount: 5,
|
|
},
|
|
{
|
|
name: "with_block",
|
|
setup: func(t *testing.T, db *DB) {
|
|
app := db.Appender()
|
|
ref, err := app.Append(0, labels.FromStrings("__name__", "temp"), 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
for i := 1; i < 250; i++ {
|
|
_, err = app.Append(ref, nil, int64(i*15000), float64(i))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
if err := app.Commit(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
_, err = db.FlushOlderThan(math.MaxInt64)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
},
|
|
collectCalls: 2,
|
|
wantHeadSeries: 6, // 1 user + 5 metric series
|
|
wantBlocksTotal: 1,
|
|
wantCompactions: 0,
|
|
wantMetricCount: 5,
|
|
},
|
|
{
|
|
name: "idempotent_five_calls",
|
|
setup: func(t *testing.T, db *DB) {},
|
|
collectCalls: 5,
|
|
wantHeadSeries: 5,
|
|
wantBlocksTotal: 0,
|
|
wantCompactions: 0,
|
|
wantMetricCount: 5,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
clock := &testClock{now: 100_000}
|
|
db, err := Open(t.TempDir(), Options{Clock: clock.fn()})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
t.Cleanup(func() { db.Close() })
|
|
|
|
tc.setup(t, db)
|
|
|
|
for i := 0; i < tc.collectCalls; i++ {
|
|
clock.now = int64(200_000 + i*1000)
|
|
db.collectMetrics()
|
|
}
|
|
|
|
// Query each expected metric.
|
|
lastValue := func(name string) (float64, bool) {
|
|
q, err := db.Querier(math.MinInt64, math.MaxInt64)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
defer q.Close()
|
|
ss := q.Select(labels.MustNewMatcher(labels.MatchEqual, "__name__", name))
|
|
found := false
|
|
var last float64
|
|
for ss.Next() {
|
|
it := ss.At().Iterator()
|
|
for it.Next() {
|
|
_, last = it.At()
|
|
}
|
|
if err := it.Err(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
found = true
|
|
}
|
|
if err := ss.Err(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
return last, found
|
|
}
|
|
|
|
v, found := lastValue(MetricHeadSeries)
|
|
if !found {
|
|
t.Errorf("ingot_head_series not found")
|
|
}
|
|
if v != tc.wantHeadSeries {
|
|
t.Errorf("ingot_head_series: got %v, want %v", v, tc.wantHeadSeries)
|
|
}
|
|
|
|
v, found = lastValue(MetricBlocksTotal)
|
|
if !found {
|
|
t.Errorf("ingot_blocks_total not found")
|
|
}
|
|
if v != tc.wantBlocksTotal {
|
|
t.Errorf("ingot_blocks_total: got %v, want %v", v, tc.wantBlocksTotal)
|
|
}
|
|
|
|
v, found = lastValue(MetricCompactionsTotal)
|
|
if !found {
|
|
t.Errorf("ingot_compactions_total not found")
|
|
}
|
|
if v != tc.wantCompactions {
|
|
t.Errorf("ingot_compactions_total: got %v, want %v", v, tc.wantCompactions)
|
|
}
|
|
|
|
// Count total ingot_* series.
|
|
q, err := db.Querier(math.MinInt64, math.MaxInt64)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
defer q.Close()
|
|
ss := q.Select(labels.MustNewMatcher(labels.MatchRegexp, "__name__", "ingot_.*"))
|
|
count := 0
|
|
for ss.Next() {
|
|
count++
|
|
}
|
|
if err := ss.Err(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if count != tc.wantMetricCount {
|
|
t.Errorf("metric series count: got %v, want %v", count, tc.wantMetricCount)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type testClock struct {
|
|
now int64
|
|
}
|
|
|
|
func (c *testClock) fn() func() int64 {
|
|
return func() int64 { return c.now }
|
|
}
|