feat(stats): serve historical stats from hourly rollups

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.
This commit is contained in:
MrAlders0n
2026-10-02 07:43:56 -04:00
committed by MrAlders0n
parent 6cd03e73da
commit 98006a9364
112 changed files with 6766 additions and 2828 deletions
+3 -12
View File
@@ -13,11 +13,11 @@ import (
const getPathStats = `-- name: GetPathStats :many
WITH readings AS (
SELECT iata, hour, category, hash_bytes, entries, receptions FROM mv_path_stats_hourly
SELECT iata, hour, category, hash_bytes, entries, receptions FROM analytics_hourly_paths
WHERE hour >= $1::timestamptz AND hour < $2::timestamptz
AND COALESCE(cardinality($3::bpchar[]), 0) = 0
UNION ALL
SELECT iata, hour, category, hash_bytes, entries, receptions FROM mv_path_stats_hourly
SELECT iata, hour, category, hash_bytes, entries, receptions FROM analytics_hourly_paths
WHERE hour >= $1::timestamptz AND hour < $2::timestamptz
AND cardinality($3::bpchar[]) > 0 AND iata = ANY($3::bpchar[])
)
@@ -45,7 +45,7 @@ type GetPathStatsRow struct {
Receptions int64 `json:"receptions"`
}
// All HTTP aggregates read the compact classification snapshot.
// All HTTP aggregates read the hourly rollup, never observations.
func (q *Queries) GetPathStats(ctx context.Context, arg GetPathStatsParams) ([]GetPathStatsRow, error) {
rows, err := q.db.Query(ctx, getPathStats, arg.Since, arg.Until, arg.Iatas)
if err != nil {
@@ -72,12 +72,3 @@ func (q *Queries) GetPathStats(ctx context.Context, arg GetPathStatsParams) ([]G
}
return items, nil
}
const refreshPathStats = `-- name: RefreshPathStats :exec
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_path_stats_hourly
`
func (q *Queries) RefreshPathStats(ctx context.Context) error {
_, err := q.db.Exec(ctx, refreshPathStats)
return err
}