From e566e575ed5d670d892c5628f5ba128b282a022d Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Sun, 6 Sep 2026 15:59:08 +0100 Subject: [PATCH] test(metrics): isolate large summary seed persistence across reopen Release qualification crashed in historical baseline SQLite ingestion during the workloads-summary seed. Preserve a metrics-only diagnostic for the same 84,000-row batch shape, with row-count and integrity checks before and after reopen, without the HTTP or reflection fixtures. This does not reproduce or fix the unexplained crash. Validation: ten focused runs on Go 1.26.7 and one on Go 1.26.8 passed; a one-repeat race run passed. The three-repeat race run timed out at 180 seconds during its final integrity check and remains retained evidence. Omitting the final seed batch makes the row-count assertion fail. Change-source: pulse-maintainer --- pkg/metrics/store_large_seed_test.go | 94 ++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 pkg/metrics/store_large_seed_test.go diff --git a/pkg/metrics/store_large_seed_test.go b/pkg/metrics/store_large_seed_test.go new file mode 100644 index 000000000..e3e585436 --- /dev/null +++ b/pkg/metrics/store_large_seed_test.go @@ -0,0 +1,94 @@ +package metrics + +import ( + "fmt" + "path/filepath" + "testing" + "time" +) + +// Exercise the ingestion shape from the workloads-summary benchmark without +// its HTTP, reflection or monitor fixtures. A historical benchmark crashed in +// SQLite during the second synchronous seed; this is a diagnostic invariant, +// not a reproducer or a claim that the unexplained crash has been repaired. +func TestStoreLargeSummarySeedSurvivesReopen(t *testing.T) { + cfg := DefaultConfig(t.TempDir()) + cfg.DBPath = filepath.Join(filepath.Dir(cfg.DBPath), "summary-seed.db") + cfg.FlushInterval = time.Hour + cfg.WriteBufferSize = 10_000 + store, err := NewStore(cfg) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if store != nil { + if err := store.Close(); err != nil { + t.Error(err) + } + } + }) + base := time.Now().Add(-4 * time.Hour).UTC().Truncate(time.Second) + for _, group := range []struct { + kind string + count int + }{{"vm", 30}, {"container", 20}, {"dockerContainer", 20}} { + batch := make([]WriteMetric, 0, group.count*5*240) + for r := 0; r < group.count; r++ { + for _, metric := range []string{"cpu", "memory", "disk", "netin", "netout"} { + for p := 0; p < 240; p++ { + batch = append(batch, WriteMetric{ + ResourceType: group.kind, ResourceID: fmt.Sprintf("%s-%d", group.kind, r), + MetricType: metric, Value: float64((r + p) % 100), + Timestamp: base.Add(time.Duration(p) * time.Minute), Tier: TierMinute, + }) + } + } + } + store.WriteBatchSync(batch) + } + check := func() { + t.Helper() + var count int + if err := store.db.QueryRow("SELECT COUNT(*) FROM metrics WHERE tier = ?", string(TierMinute)).Scan(&count); err != nil { + t.Fatal(err) + } + if count != 84_000 { + t.Fatalf("persisted minute rows = %d, want 84000", count) + } + rows, err := store.db.Query("PRAGMA integrity_check") + if err != nil { + t.Fatal(err) + } + defer rows.Close() + var results int + for rows.Next() { + var result string + if err := rows.Scan(&result); err != nil { + t.Fatal(err) + } + if result != "ok" { + t.Errorf("integrity_check: %s", result) + } + results++ + } + if err := rows.Err(); err != nil { + t.Fatal(err) + } + if results != 1 { + t.Fatalf("integrity_check returned %d rows, want one ok", results) + } + } + check() + if err := store.Close(); err != nil { + t.Fatal(err) + } + store = nil + store, err = NewStore(cfg) + if err != nil { + t.Fatal(err) + } + if err := store.WaitForMaintenance(10 * time.Second); err != nil { + t.Fatal(err) + } + check() +}