mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-11-11 18:57:33 +00:00
* feat(db): observer adverts on a partial index once observations carry their payload type * fix(db): keep today's adverts statement unguarded and rate-limit the gate's read warning * feat(background): batched, resumable payload-type backfill for untyped observations * fix(background): cap each payload-type backfill statement at 50,000 rows * fix(db): reject a non-positive backfill row cap * perf(db): probe rollups per hour and IATA for region-sized stats lists * fix(db): keep the stats probes inside each rollup table's hour range * test(db): push the far-future until to year 9999 so a missing path-stats end bound times out * perf(db): row-comparison trace cursor and a since-only telemetry statement * test(db): pin the empty trace cursor tag and run the trace pagination tests in CI * test(planguard): stats, adverts, traces and telemetry statements leave pending; diff cases * test(planguard): window-bounded advert roles, short quiet page, per-observer telemetry cursors
89 lines
2.8 KiB
Go
89 lines
2.8 KiB
Go
// Copyright 2026 Beacon Contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
sqlc "github.com/MeshCore-Beacon/beacon-server/db/sqlc"
|
|
"github.com/MeshCore-Beacon/beacon-server/internal/api"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
func (s *Store) GetPathStats(ctx context.Context, since, until time.Time, iatas []string) (*api.PathStats, error) {
|
|
from, to := pgtype.Timestamptz{Time: since, Valid: true}, pgtype.Timestamptz{Time: until, Valid: true}
|
|
var rows []sqlc.GetPathStatsRow
|
|
if useStatsProbe(iatas, since, until) {
|
|
probeRows, err := s.q.GetPathStatsProbe(ctx, sqlc.GetPathStatsProbeParams{Since: from, Until: to, Iatas: iatas})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rows = make([]sqlc.GetPathStatsRow, len(probeRows))
|
|
for i, r := range probeRows {
|
|
rows[i] = sqlc.GetPathStatsRow(r)
|
|
}
|
|
} else {
|
|
var err error
|
|
if rows, err = s.q.GetPathStats(ctx, sqlc.GetPathStatsParams{Since: from, Until: to, Iatas: iatas}); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
stats := &api.PathStats{Since: since.UnixMilli(), Until: until.UnixMilli(), HashWidths: []api.PathHashWidth{{Bytes: 1}, {Bytes: 2}, {Bytes: 3}}, PathLengths: []api.PathLengthBin{}, Hourly: []api.PathHour{}}
|
|
var lengths [64]int64
|
|
for _, row := range rows {
|
|
if row.Category == 0 && (row.HashBytes < 1 || int(row.HashBytes) > len(stats.HashWidths) || row.Entries < 0 || int(row.Entries) >= len(lengths)) {
|
|
return nil, fmt.Errorf("invalid path statistics bucket")
|
|
}
|
|
if row.IsHourly {
|
|
hour := row.Hour.Time.UnixMilli()
|
|
if len(stats.Hourly) == 0 || stats.Hourly[len(stats.Hourly)-1].Hour != hour {
|
|
stats.Hourly = append(stats.Hourly, api.PathHour{Hour: hour})
|
|
}
|
|
point := &stats.Hourly[len(stats.Hourly)-1]
|
|
point.Receptions += row.Receptions
|
|
point.MaxEntries = max(point.MaxEntries, row.MaxEntries)
|
|
switch row.Category {
|
|
case 0:
|
|
switch row.HashBytes {
|
|
case 1:
|
|
point.OneByte += row.Receptions
|
|
case 2:
|
|
point.TwoByte += row.Receptions
|
|
case 3:
|
|
point.ThreeByte += row.Receptions
|
|
}
|
|
case 1:
|
|
point.Empty += row.Receptions
|
|
case 2:
|
|
point.Trace += row.Receptions
|
|
case 3:
|
|
point.Unclassified += row.Receptions
|
|
}
|
|
continue
|
|
}
|
|
stats.Receptions += row.Receptions
|
|
switch row.Category {
|
|
case 0:
|
|
stats.Hashed += row.Receptions
|
|
stats.HashWidths[row.HashBytes-1].Receptions += row.Receptions
|
|
lengths[row.Entries] += row.Receptions
|
|
case 1:
|
|
stats.Empty += row.Receptions
|
|
lengths[0] += row.Receptions
|
|
case 2:
|
|
stats.Trace += row.Receptions
|
|
case 3:
|
|
stats.Unclassified += row.Receptions
|
|
}
|
|
}
|
|
for entries, count := range lengths {
|
|
if count > 0 {
|
|
stats.PathLengths = append(stats.PathLengths, api.PathLengthBin{Entries: int32(entries), Receptions: count})
|
|
}
|
|
}
|
|
return stats, nil
|
|
}
|