mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-16 20:20:07 +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.
51 lines
1.5 KiB
Go
51 lines
1.5 KiB
Go
// Copyright 2026 Beacon Contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package cache
|
|
|
|
import (
|
|
"context"
|
|
"slices"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/MeshCore-Beacon/beacon-server/internal/api"
|
|
)
|
|
|
|
type pathStatsReader struct {
|
|
api.Reader
|
|
calls int64
|
|
}
|
|
|
|
func (r *pathStatsReader) AnalyticsRevision(context.Context) (int64, error) { return 0, nil }
|
|
|
|
func (r *pathStatsReader) GetPathStats(context.Context, time.Time, time.Time, []string) (*api.PathStats, error) {
|
|
r.calls++
|
|
return &api.PathStats{Receptions: r.calls}, nil
|
|
}
|
|
|
|
func TestPathCacheSeparatesWindowAndRegion(t *testing.T) {
|
|
c, mr := newTestClient(t)
|
|
inner := &pathStatsReader{}
|
|
r := NewCachedReader(inner, c, CacheTTLs{Stats: time.Minute})
|
|
for _, tc := range []struct {
|
|
iatas []string
|
|
since, until, want int64
|
|
}{
|
|
{nil, 0, 10, 1}, {[]string{}, 0, 10, 1}, {[]string{""}, 0, 10, 2}, {[]string{"YVR"}, 0, 10, 3},
|
|
{[]string{"YVR", "YYJ"}, 0, 10, 4}, {[]string{"YYJ", "YVR", "YYJ"}, 0, 10, 4},
|
|
{nil, 1, 10, 5}, {nil, 0, 11, 6}, {nil, 0, 10, 1},
|
|
} {
|
|
before := slices.Clone(tc.iatas)
|
|
got, err := r.GetPathStats(context.Background(), time.UnixMilli(tc.since), time.UnixMilli(tc.until), tc.iatas)
|
|
if err != nil || got.Receptions != tc.want || !slices.Equal(before, tc.iatas) {
|
|
t.Fatalf("%+v: %+v %v", tc, got, err)
|
|
}
|
|
}
|
|
mr.FastForward(time.Minute)
|
|
got, err := r.GetPathStats(context.Background(), time.UnixMilli(0), time.UnixMilli(10), nil)
|
|
if err != nil || got.Receptions != 7 {
|
|
t.Fatalf("expiry: %+v %v", got, err)
|
|
}
|
|
}
|