mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-11-04 21:27:21 +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
151 lines
5.3 KiB
Go
151 lines
5.3 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: paths.sql
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const getPathStats = `-- name: GetPathStats :many
|
|
WITH readings AS (
|
|
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 analytics_hourly_paths
|
|
WHERE hour >= $1::timestamptz AND hour < $2::timestamptz
|
|
AND cardinality($3::bpchar[]) > 0 AND iata = ANY($3::bpchar[])
|
|
)
|
|
SELECT (grouping(hour) = 0)::boolean AS is_hourly,
|
|
COALESCE(hour, 'epoch'::timestamptz)::timestamptz AS hour,
|
|
category, hash_bytes, COALESCE(entries, 0)::integer AS entries,
|
|
sum(receptions)::bigint AS receptions,
|
|
-- Non-routed categories store entries = 0, so an hour of only empty paths gives 0.
|
|
COALESCE(max(entries) FILTER (WHERE receptions > 0), 0)::integer AS max_entries
|
|
FROM readings
|
|
GROUP BY GROUPING SETS ((category, hash_bytes, entries), (hour, category, hash_bytes))
|
|
ORDER BY is_hourly, hour, category, hash_bytes, entries
|
|
`
|
|
|
|
type GetPathStatsParams struct {
|
|
Since pgtype.Timestamptz `json:"since"`
|
|
Until pgtype.Timestamptz `json:"until"`
|
|
Iatas []string `json:"iatas"`
|
|
}
|
|
|
|
type GetPathStatsRow struct {
|
|
IsHourly bool `json:"is_hourly"`
|
|
Hour pgtype.Timestamptz `json:"hour"`
|
|
Category int32 `json:"category"`
|
|
HashBytes int32 `json:"hash_bytes"`
|
|
Entries int32 `json:"entries"`
|
|
Receptions int64 `json:"receptions"`
|
|
MaxEntries int32 `json:"max_entries"`
|
|
}
|
|
|
|
// 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 {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []GetPathStatsRow{}
|
|
for rows.Next() {
|
|
var i GetPathStatsRow
|
|
if err := rows.Scan(
|
|
&i.IsHourly,
|
|
&i.Hour,
|
|
&i.Category,
|
|
&i.HashBytes,
|
|
&i.Entries,
|
|
&i.Receptions,
|
|
&i.MaxEntries,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getPathStatsProbe = `-- name: GetPathStatsProbe :many
|
|
WITH readings AS (
|
|
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 p.iata, p.hour, p.category, p.hash_bytes, p.entries, p.receptions
|
|
FROM generate_series((SELECT CASE WHEN min(hour) IS NOT NULL THEN GREATEST($1::timestamptz, min(hour)) END FROM analytics_hourly_paths),
|
|
(SELECT LEAST($2::timestamptz, max(hour)) FROM analytics_hourly_paths), interval '1 hour') g(h)
|
|
CROSS JOIN (SELECT DISTINCT unnest($3::bpchar[]) AS iata) i
|
|
CROSS JOIN LATERAL (SELECT x.iata, x.hour, x.category, x.hash_bytes, x.entries, x.receptions
|
|
FROM analytics_hourly_paths x WHERE x.hour = g.h AND x.iata = i.iata OFFSET 0) p
|
|
WHERE g.h < $2::timestamptz AND cardinality($3::bpchar[]) > 0
|
|
)
|
|
SELECT (grouping(hour) = 0)::boolean AS is_hourly,
|
|
COALESCE(hour, 'epoch'::timestamptz)::timestamptz AS hour,
|
|
category, hash_bytes, COALESCE(entries, 0)::integer AS entries,
|
|
sum(receptions)::bigint AS receptions,
|
|
-- Non-routed categories store entries = 0, so an hour of only empty paths gives 0.
|
|
COALESCE(max(entries) FILTER (WHERE receptions > 0), 0)::integer AS max_entries
|
|
FROM readings
|
|
GROUP BY GROUPING SETS ((category, hash_bytes, entries), (hour, category, hash_bytes))
|
|
ORDER BY is_hourly, hour, category, hash_bytes, entries
|
|
`
|
|
|
|
type GetPathStatsProbeParams struct {
|
|
Since pgtype.Timestamptz `json:"since"`
|
|
Until pgtype.Timestamptz `json:"until"`
|
|
Iatas []string `json:"iatas"`
|
|
}
|
|
|
|
type GetPathStatsProbeRow struct {
|
|
IsHourly bool `json:"is_hourly"`
|
|
Hour pgtype.Timestamptz `json:"hour"`
|
|
Category int32 `json:"category"`
|
|
HashBytes int32 `json:"hash_bytes"`
|
|
Entries int32 `json:"entries"`
|
|
Receptions int64 `json:"receptions"`
|
|
MaxEntries int32 `json:"max_entries"`
|
|
}
|
|
|
|
// GetPathStats for at most 32 distinct IATAs: one rollup probe per (hour, IATA). @since and @until are UTC hours.
|
|
// The hours walked stay inside the table's own [min(hour), max(hour)]; an empty table walks none
|
|
// (a NULL start makes generate_series return no rows).
|
|
func (q *Queries) GetPathStatsProbe(ctx context.Context, arg GetPathStatsProbeParams) ([]GetPathStatsProbeRow, error) {
|
|
rows, err := q.db.Query(ctx, getPathStatsProbe, arg.Since, arg.Until, arg.Iatas)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []GetPathStatsProbeRow{}
|
|
for rows.Next() {
|
|
var i GetPathStatsProbeRow
|
|
if err := rows.Scan(
|
|
&i.IsHourly,
|
|
&i.Hour,
|
|
&i.Category,
|
|
&i.HashBytes,
|
|
&i.Entries,
|
|
&i.Receptions,
|
|
&i.MaxEntries,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|