mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-16 16:00:03 +00:00
Stats endpoints read hourly rollup tables filled once per closed hour by one advisory-locked task, instead of materialized views that re-scanned every observation on each refresh. IATA-set tables give exact distinct counts for any IATA set or region. Cleanup holds raw rows for unrolled hours (up to 24h). Adds /stats/series; ingest keeps packet observation counts, observer last IATA, channel message counts and trace tag summaries. 001_baseline.sql is rewritten, so pre-release 2.0 databases must be recreated. See docs/historical-stats.md.
65 lines
1.9 KiB
Go
65 lines
1.9 KiB
Go
// Copyright 2026 Beacon Contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package cache
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"log"
|
|
"log/slog"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCacheDiagnostics(t *testing.T) {
|
|
previous, writer, flags := slog.Default(), log.Writer(), log.Flags()
|
|
t.Cleanup(func() { slog.SetDefault(previous); log.SetOutput(writer); log.SetFlags(flags) })
|
|
var out bytes.Buffer
|
|
slog.SetDefault(slog.New(slog.NewJSONHandler(&out, &slog.HandlerOptions{Level: slog.LevelDebug})))
|
|
c, mr := newTestClient(t)
|
|
key, value := "private-cache-key", "private-cache-value"
|
|
calls := 0
|
|
fetch := func(context.Context) (string, error) { calls++; return value, nil }
|
|
for i := 0; i < 2; i++ {
|
|
got, err := getOrSet(context.Background(), c, key, time.Minute, fetch)
|
|
if err != nil || got != value {
|
|
t.Fatalf("cache result changed: %v", err)
|
|
}
|
|
}
|
|
if calls != 1 {
|
|
t.Fatalf("fetches=%d, want 1", calls)
|
|
}
|
|
mr.Set(key, "invalid JSON")
|
|
if _, err := getOrSet(context.Background(), c, key, time.Minute, fetch); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
lines := bytes.Split(bytes.TrimSpace(out.Bytes()), []byte("\n"))
|
|
want := []string{"cache miss", "cache hit", "cache invalid entry"}
|
|
if len(lines) != len(want) {
|
|
t.Fatalf("got %d records, want %d", len(lines), len(want))
|
|
}
|
|
for i, line := range lines {
|
|
var record map[string]any
|
|
if err := json.Unmarshal(line, &record); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if record["component"] != "cache" || record["level"] != "DEBUG" || record["msg"] != want[i] {
|
|
t.Fatalf("unexpected record: %v", record)
|
|
}
|
|
}
|
|
if strings.Contains(out.String(), key) || strings.Contains(out.String(), value) {
|
|
t.Fatal("cache key or payload leaked")
|
|
}
|
|
out.Reset()
|
|
slog.SetDefault(slog.New(slog.NewJSONHandler(&out, nil)))
|
|
if _, err := getOrSet(context.Background(), c, key, time.Minute, fetch); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Len() != 0 {
|
|
t.Fatal("debug diagnostics appeared at info level")
|
|
}
|
|
}
|