Files
beacon-server/db/queries/paths.sql
T
MrAlders0n ded8708fec Stats, adverts, traces and telemetry: bounded statements, same results (#243)
* 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
2026-10-19 19:18:45 -04:00

48 lines
2.9 KiB
SQL

-- name: GetPathStats :many
-- All HTTP aggregates read the hourly rollup, never observations.
WITH readings AS (
SELECT iata, hour, category, hash_bytes, entries, receptions FROM analytics_hourly_paths
WHERE hour >= @since::timestamptz AND hour < @until::timestamptz
AND COALESCE(cardinality(@iatas::bpchar[]), 0) = 0
UNION ALL
SELECT iata, hour, category, hash_bytes, entries, receptions FROM analytics_hourly_paths
WHERE hour >= @since::timestamptz AND hour < @until::timestamptz
AND cardinality(@iatas::bpchar[]) > 0 AND iata = ANY(@iatas::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;
-- name: GetPathStatsProbe :many
-- 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).
WITH readings AS (
SELECT iata, hour, category, hash_bytes, entries, receptions FROM analytics_hourly_paths
WHERE hour >= @since::timestamptz AND hour < @until::timestamptz
AND COALESCE(cardinality(@iatas::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(@since::timestamptz, min(hour)) END FROM analytics_hourly_paths),
(SELECT LEAST(@until::timestamptz, max(hour)) FROM analytics_hourly_paths), interval '1 hour') g(h)
CROSS JOIN (SELECT DISTINCT unnest(@iatas::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 < @until::timestamptz AND cardinality(@iatas::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;