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 authored and MrAlders0n committed 2026-10-02 07:43:56 -04:00
1 parent 6cd03e73da
commit 98006a9364
112 files changed
+6766 -2828

No files matched your search

+446 -172
View File
@@ -245,6 +245,20 @@ func (mr *MockQuerierMockRecorder) DeleteOldPackets(ctx, arg any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldPackets", reflect.TypeOf((*MockQuerier)(nil).DeleteOldPackets), ctx, arg)
}
// DeleteOldRollups mocks base method.
func (m *MockQuerier) DeleteOldRollups(ctx context.Context, cutoff pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteOldRollups", ctx, cutoff)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteOldRollups indicates an expected call of DeleteOldRollups.
func (mr *MockQuerierMockRecorder) DeleteOldRollups(ctx, cutoff any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldRollups", reflect.TypeOf((*MockQuerier)(nil).DeleteOldRollups), ctx, cutoff)
}
// DeleteOldRoutes mocks base method.
func (m *MockQuerier) DeleteOldRoutes(ctx context.Context, arg db.DeleteOldRoutesParams) (int64, error) {
m.ctrl.T.Helper()
@@ -288,6 +302,20 @@ func (mr *MockQuerierMockRecorder) DeleteOldTraceIATAs(ctx, lastHeard any) *gomo
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldTraceIATAs", reflect.TypeOf((*MockQuerier)(nil).DeleteOldTraceIATAs), ctx, lastHeard)
}
// DeleteOldTraceTags mocks base method.
func (m *MockQuerier) DeleteOldTraceTags(ctx context.Context, lastHeardAt pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteOldTraceTags", ctx, lastHeardAt)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteOldTraceTags indicates an expected call of DeleteOldTraceTags.
func (mr *MockQuerierMockRecorder) DeleteOldTraceTags(ctx, lastHeardAt any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldTraceTags", reflect.TypeOf((*MockQuerier)(nil).DeleteOldTraceTags), ctx, lastHeardAt)
}
// DeleteRegionIATAsNotIn mocks base method.
func (m *MockQuerier) DeleteRegionIATAsNotIn(ctx context.Context, arg db.DeleteRegionIATAsNotInParams) error {
m.ctrl.T.Helper()
@@ -302,6 +330,63 @@ func (mr *MockQuerierMockRecorder) DeleteRegionIATAsNotIn(ctx, arg any) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRegionIATAsNotIn", reflect.TypeOf((*MockQuerier)(nil).DeleteRegionIATAsNotIn), ctx, arg)
}
// DeleteRollupHour mocks base method.
func (m *MockQuerier) DeleteRollupHour(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteRollupHour", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteRollupHour indicates an expected call of DeleteRollupHour.
func (mr *MockQuerierMockRecorder) DeleteRollupHour(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRollupHour", reflect.TypeOf((*MockQuerier)(nil).DeleteRollupHour), ctx, hour)
}
// DeleteStaleDirtyHours mocks base method.
func (m *MockQuerier) DeleteStaleDirtyHours(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteStaleDirtyHours", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteStaleDirtyHours indicates an expected call of DeleteStaleDirtyHours.
func (mr *MockQuerierMockRecorder) DeleteStaleDirtyHours(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStaleDirtyHours", reflect.TypeOf((*MockQuerier)(nil).DeleteStaleDirtyHours), ctx)
}
// FillRollupObs mocks base method.
func (m *MockQuerier) FillRollupObs(ctx context.Context, dollar_1 pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FillRollupObs", ctx, dollar_1)
ret0, _ := ret[0].(error)
return ret0
}
// FillRollupObs indicates an expected call of FillRollupObs.
func (mr *MockQuerierMockRecorder) FillRollupObs(ctx, dollar_1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FillRollupObs", reflect.TypeOf((*MockQuerier)(nil).FillRollupObs), ctx, dollar_1)
}
// FinishRollupHour mocks base method.
func (m *MockQuerier) FinishRollupHour(ctx context.Context, arg db.FinishRollupHourParams) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FinishRollupHour", ctx, arg)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FinishRollupHour indicates an expected call of FinishRollupHour.
func (mr *MockQuerierMockRecorder) FinishRollupHour(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinishRollupHour", reflect.TypeOf((*MockQuerier)(nil).FinishRollupHour), ctx, arg)
}
// GetAccount mocks base method.
func (m *MockQuerier) GetAccount(ctx context.Context, id uuid.UUID) (db.Account, error) {
m.ctrl.T.Helper()
@@ -317,6 +402,21 @@ func (mr *MockQuerierMockRecorder) GetAccount(ctx, id any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockQuerier)(nil).GetAccount), ctx, id)
}
// GetAnalyticsRevision mocks base method.
func (m *MockQuerier) GetAnalyticsRevision(ctx context.Context) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAnalyticsRevision", ctx)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetAnalyticsRevision indicates an expected call of GetAnalyticsRevision.
func (mr *MockQuerierMockRecorder) GetAnalyticsRevision(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAnalyticsRevision", reflect.TypeOf((*MockQuerier)(nil).GetAnalyticsRevision), ctx)
}
// GetChannelByID mocks base method.
func (m *MockQuerier) GetChannelByID(ctx context.Context, id int32) (db.Channel, error) {
m.ctrl.T.Helper()
@@ -347,11 +447,26 @@ func (mr *MockQuerierMockRecorder) GetCrossIATANeighbors(ctx, arg any) *gomock.C
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCrossIATANeighbors", reflect.TypeOf((*MockQuerier)(nil).GetCrossIATANeighbors), ctx, arg)
}
// GetEarliestCompleteRollupHour mocks base method.
func (m *MockQuerier) GetEarliestCompleteRollupHour(ctx context.Context) (pgtype.Timestamptz, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetEarliestCompleteRollupHour", ctx)
ret0, _ := ret[0].(pgtype.Timestamptz)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetEarliestCompleteRollupHour indicates an expected call of GetEarliestCompleteRollupHour.
func (mr *MockQuerierMockRecorder) GetEarliestCompleteRollupHour(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEarliestCompleteRollupHour", reflect.TypeOf((*MockQuerier)(nil).GetEarliestCompleteRollupHour), ctx)
}
// GetHourlyStats mocks base method.
func (m *MockQuerier) GetHourlyStats(ctx context.Context, arg db.GetHourlyStatsParams) ([]db.MvHourlyIataStat, error) {
func (m *MockQuerier) GetHourlyStats(ctx context.Context, arg db.GetHourlyStatsParams) ([]db.GetHourlyStatsRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetHourlyStats", ctx, arg)
ret0, _ := ret[0].([]db.MvHourlyIataStat)
ret0, _ := ret[0].([]db.GetHourlyStatsRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@@ -618,18 +733,18 @@ func (mr *MockQuerierMockRecorder) GetObserverComparison(ctx, arg any) *gomock.C
}
// GetObserverLastIATA mocks base method.
func (m *MockQuerier) GetObserverLastIATA(ctx context.Context, observerID uuid.UUID) (string, error) {
func (m *MockQuerier) GetObserverLastIATA(ctx context.Context, id uuid.UUID) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetObserverLastIATA", ctx, observerID)
ret := m.ctrl.Call(m, "GetObserverLastIATA", ctx, id)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetObserverLastIATA indicates an expected call of GetObserverLastIATA.
func (mr *MockQuerierMockRecorder) GetObserverLastIATA(ctx, observerID any) *gomock.Call {
func (mr *MockQuerierMockRecorder) GetObserverLastIATA(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObserverLastIATA", reflect.TypeOf((*MockQuerier)(nil).GetObserverLastIATA), ctx, observerID)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObserverLastIATA", reflect.TypeOf((*MockQuerier)(nil).GetObserverLastIATA), ctx, id)
}
// GetObserverRadio mocks base method.
@@ -692,21 +807,6 @@ func (mr *MockQuerierMockRecorder) GetPacketByHash(ctx, packetHash any) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPacketByHash", reflect.TypeOf((*MockQuerier)(nil).GetPacketByHash), ctx, packetHash)
}
// GetPacketObservationCount mocks base method.
func (m *MockQuerier) GetPacketObservationCount(ctx context.Context, packetHash []byte) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPacketObservationCount", ctx, packetHash)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetPacketObservationCount indicates an expected call of GetPacketObservationCount.
func (mr *MockQuerierMockRecorder) GetPacketObservationCount(ctx, packetHash any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPacketObservationCount", reflect.TypeOf((*MockQuerier)(nil).GetPacketObservationCount), ctx, packetHash)
}
// GetPacketsByTraceTag mocks base method.
func (m *MockQuerier) GetPacketsByTraceTag(ctx context.Context, decode string) ([]db.GetPacketsByTraceTagRow, error) {
m.ctrl.T.Helper()
@@ -858,18 +958,18 @@ func (mr *MockQuerierMockRecorder) GetScopeNames(ctx any) *gomock.Call {
}
// GetScopeStats mocks base method.
func (m *MockQuerier) GetScopeStats(ctx context.Context, iatas []string) ([]db.GetScopeStatsRow, error) {
func (m *MockQuerier) GetScopeStats(ctx context.Context, arg db.GetScopeStatsParams) ([]db.GetScopeStatsRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetScopeStats", ctx, iatas)
ret := m.ctrl.Call(m, "GetScopeStats", ctx, arg)
ret0, _ := ret[0].([]db.GetScopeStatsRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetScopeStats indicates an expected call of GetScopeStats.
func (mr *MockQuerierMockRecorder) GetScopeStats(ctx, iatas any) *gomock.Call {
func (mr *MockQuerierMockRecorder) GetScopeStats(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetScopeStats", reflect.TypeOf((*MockQuerier)(nil).GetScopeStats), ctx, iatas)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetScopeStats", reflect.TypeOf((*MockQuerier)(nil).GetScopeStats), ctx, arg)
}
// GetSignalStats mocks base method.
@@ -917,21 +1017,6 @@ func (mr *MockQuerierMockRecorder) GetStatsNodeTypes(ctx, dollar_1 any) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatsNodeTypes", reflect.TypeOf((*MockQuerier)(nil).GetStatsNodeTypes), ctx, dollar_1)
}
// GetStatsOverview mocks base method.
func (m *MockQuerier) GetStatsOverview(ctx context.Context, dollar_1 []string) (db.GetStatsOverviewRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetStatsOverview", ctx, dollar_1)
ret0, _ := ret[0].(db.GetStatsOverviewRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetStatsOverview indicates an expected call of GetStatsOverview.
func (mr *MockQuerierMockRecorder) GetStatsOverview(ctx, dollar_1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatsOverview", reflect.TypeOf((*MockQuerier)(nil).GetStatsOverview), ctx, dollar_1)
}
// GetStatsPayloadBreakdown mocks base method.
func (m *MockQuerier) GetStatsPayloadBreakdown(ctx context.Context, arg db.GetStatsPayloadBreakdownParams) ([]db.GetStatsPayloadBreakdownRow, error) {
m.ctrl.T.Helper()
@@ -947,6 +1032,21 @@ func (mr *MockQuerierMockRecorder) GetStatsPayloadBreakdown(ctx, arg any) *gomoc
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatsPayloadBreakdown", reflect.TypeOf((*MockQuerier)(nil).GetStatsPayloadBreakdown), ctx, arg)
}
// GetStatsSeries mocks base method.
func (m *MockQuerier) GetStatsSeries(ctx context.Context, arg db.GetStatsSeriesParams) ([]db.GetStatsSeriesRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetStatsSeries", ctx, arg)
ret0, _ := ret[0].([]db.GetStatsSeriesRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetStatsSeries indicates an expected call of GetStatsSeries.
func (mr *MockQuerierMockRecorder) GetStatsSeries(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatsSeries", reflect.TypeOf((*MockQuerier)(nil).GetStatsSeries), ctx, arg)
}
// GetStatsTopAdvertisers mocks base method.
func (m *MockQuerier) GetStatsTopAdvertisers(ctx context.Context, arg db.GetStatsTopAdvertisersParams) ([]db.GetStatsTopAdvertisersRow, error) {
m.ctrl.T.Helper()
@@ -993,10 +1093,10 @@ func (mr *MockQuerierMockRecorder) GetStatsTopTalkers(ctx, arg any) *gomock.Call
}
// GetTopNodes mocks base method.
func (m *MockQuerier) GetTopNodes(ctx context.Context, arg db.GetTopNodesParams) ([]db.MvTopNodesByIatum, error) {
func (m *MockQuerier) GetTopNodes(ctx context.Context, arg db.GetTopNodesParams) ([]db.GetTopNodesRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetTopNodes", ctx, arg)
ret0, _ := ret[0].([]db.MvTopNodesByIatum)
ret0, _ := ret[0].([]db.GetTopNodesRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@@ -1053,10 +1153,10 @@ func (mr *MockQuerierMockRecorder) InsertChannelMessage(ctx, arg any) *gomock.Ca
}
// InsertObservation mocks base method.
func (m *MockQuerier) InsertObservation(ctx context.Context, arg db.InsertObservationParams) (db.PacketObservation, error) {
func (m *MockQuerier) InsertObservation(ctx context.Context, arg db.InsertObservationParams) (db.InsertObservationRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InsertObservation", ctx, arg)
ret0, _ := ret[0].(db.PacketObservation)
ret0, _ := ret[0].(db.InsertObservationRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@@ -1186,6 +1286,21 @@ func (mr *MockQuerierMockRecorder) ListChannelsAfter(ctx, arg any) *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListChannelsAfter", reflect.TypeOf((*MockQuerier)(nil).ListChannelsAfter), ctx, arg)
}
// ListDirtyRollupHours mocks base method.
func (m *MockQuerier) ListDirtyRollupHours(ctx context.Context, limit int32) ([]pgtype.Timestamptz, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListDirtyRollupHours", ctx, limit)
ret0, _ := ret[0].([]pgtype.Timestamptz)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListDirtyRollupHours indicates an expected call of ListDirtyRollupHours.
func (mr *MockQuerierMockRecorder) ListDirtyRollupHours(ctx, limit any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDirtyRollupHours", reflect.TypeOf((*MockQuerier)(nil).ListDirtyRollupHours), ctx, limit)
}
// ListIATAs mocks base method.
func (m *MockQuerier) ListIATAs(ctx context.Context) ([]db.IataCode, error) {
m.ctrl.T.Helper()
@@ -1231,6 +1346,21 @@ func (mr *MockQuerierMockRecorder) ListMessagesAfterID(ctx, arg any) *gomock.Cal
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListMessagesAfterID", reflect.TypeOf((*MockQuerier)(nil).ListMessagesAfterID), ctx, arg)
}
// ListMissingRollupHours mocks base method.
func (m *MockQuerier) ListMissingRollupHours(ctx context.Context, limit int32) ([]pgtype.Timestamptz, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListMissingRollupHours", ctx, limit)
ret0, _ := ret[0].([]pgtype.Timestamptz)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListMissingRollupHours indicates an expected call of ListMissingRollupHours.
func (mr *MockQuerierMockRecorder) ListMissingRollupHours(ctx, limit any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListMissingRollupHours", reflect.TypeOf((*MockQuerier)(nil).ListMissingRollupHours), ctx, limit)
}
// ListNodeObservations mocks base method.
func (m *MockQuerier) ListNodeObservations(ctx context.Context, arg db.ListNodeObservationsParams) ([]db.ListNodeObservationsRow, error) {
m.ctrl.T.Helper()
@@ -1486,6 +1616,50 @@ func (mr *MockQuerierMockRecorder) ListZoneLists(ctx any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListZoneLists", reflect.TypeOf((*MockQuerier)(nil).ListZoneLists), ctx)
}
// LockRollupHour mocks base method.
func (m *MockQuerier) LockRollupHour(ctx context.Context, hour pgtype.Timestamptz) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LockRollupHour", ctx, hour)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// LockRollupHour indicates an expected call of LockRollupHour.
func (mr *MockQuerierMockRecorder) LockRollupHour(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockRollupHour", reflect.TypeOf((*MockQuerier)(nil).LockRollupHour), ctx, hour)
}
// MarkPartialRollupHours mocks base method.
func (m *MockQuerier) MarkPartialRollupHours(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "MarkPartialRollupHours", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// MarkPartialRollupHours indicates an expected call of MarkPartialRollupHours.
func (mr *MockQuerierMockRecorder) MarkPartialRollupHours(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkPartialRollupHours", reflect.TypeOf((*MockQuerier)(nil).MarkPartialRollupHours), ctx)
}
// OldestMissingRollupHour mocks base method.
func (m *MockQuerier) OldestMissingRollupHour(ctx context.Context) (pgtype.Timestamptz, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "OldestMissingRollupHour", ctx)
ret0, _ := ret[0].(pgtype.Timestamptz)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// OldestMissingRollupHour indicates an expected call of OldestMissingRollupHour.
func (mr *MockQuerierMockRecorder) OldestMissingRollupHour(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OldestMissingRollupHour", reflect.TypeOf((*MockQuerier)(nil).OldestMissingRollupHour), ctx)
}
// PruneImportedRegions mocks base method.
func (m *MockQuerier) PruneImportedRegions(ctx context.Context, keep []string) ([]string, error) {
m.ctrl.T.Helper()
@@ -1545,60 +1719,18 @@ func (mr *MockQuerierMockRecorder) ReconfirmRoutes(ctx, arg any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReconfirmRoutes", reflect.TypeOf((*MockQuerier)(nil).ReconfirmRoutes), ctx, arg)
}
// RefreshHourlyStats mocks base method.
func (m *MockQuerier) RefreshHourlyStats(ctx context.Context) error {
// RecordTrace mocks base method.
func (m *MockQuerier) RecordTrace(ctx context.Context, arg db.RecordTraceParams) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshHourlyStats", ctx)
ret := m.ctrl.Call(m, "RecordTrace", ctx, arg)
ret0, _ := ret[0].(error)
return ret0
}
// RefreshHourlyStats indicates an expected call of RefreshHourlyStats.
func (mr *MockQuerierMockRecorder) RefreshHourlyStats(ctx any) *gomock.Call {
// RecordTrace indicates an expected call of RecordTrace.
func (mr *MockQuerierMockRecorder) RecordTrace(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshHourlyStats", reflect.TypeOf((*MockQuerier)(nil).RefreshHourlyStats), ctx)
}
// RefreshObserverActivity mocks base method.
func (m *MockQuerier) RefreshObserverActivity(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshObserverActivity", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// RefreshObserverActivity indicates an expected call of RefreshObserverActivity.
func (mr *MockQuerierMockRecorder) RefreshObserverActivity(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshObserverActivity", reflect.TypeOf((*MockQuerier)(nil).RefreshObserverActivity), ctx)
}
// RefreshPathStats mocks base method.
func (m *MockQuerier) RefreshPathStats(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshPathStats", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// RefreshPathStats indicates an expected call of RefreshPathStats.
func (mr *MockQuerierMockRecorder) RefreshPathStats(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshPathStats", reflect.TypeOf((*MockQuerier)(nil).RefreshPathStats), ctx)
}
// RefreshPayloadBreakdown mocks base method.
func (m *MockQuerier) RefreshPayloadBreakdown(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshPayloadBreakdown", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// RefreshPayloadBreakdown indicates an expected call of RefreshPayloadBreakdown.
func (mr *MockQuerierMockRecorder) RefreshPayloadBreakdown(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshPayloadBreakdown", reflect.TypeOf((*MockQuerier)(nil).RefreshPayloadBreakdown), ctx)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordTrace", reflect.TypeOf((*MockQuerier)(nil).RecordTrace), ctx, arg)
}
// RefreshRadioPresets mocks base method.
@@ -1615,74 +1747,18 @@ func (mr *MockQuerierMockRecorder) RefreshRadioPresets(ctx any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshRadioPresets", reflect.TypeOf((*MockQuerier)(nil).RefreshRadioPresets), ctx)
}
// RefreshSignalStats mocks base method.
func (m *MockQuerier) RefreshSignalStats(ctx context.Context) error {
// RegisterRollupHours mocks base method.
func (m *MockQuerier) RegisterRollupHours(ctx context.Context, since pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshSignalStats", ctx)
ret := m.ctrl.Call(m, "RegisterRollupHours", ctx, since)
ret0, _ := ret[0].(error)
return ret0
}
// RefreshSignalStats indicates an expected call of RefreshSignalStats.
func (mr *MockQuerierMockRecorder) RefreshSignalStats(ctx any) *gomock.Call {
// RegisterRollupHours indicates an expected call of RegisterRollupHours.
func (mr *MockQuerierMockRecorder) RegisterRollupHours(ctx, since any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshSignalStats", reflect.TypeOf((*MockQuerier)(nil).RefreshSignalStats), ctx)
}
// RefreshTopAdvertisers mocks base method.
func (m *MockQuerier) RefreshTopAdvertisers(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshTopAdvertisers", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// RefreshTopAdvertisers indicates an expected call of RefreshTopAdvertisers.
func (mr *MockQuerierMockRecorder) RefreshTopAdvertisers(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTopAdvertisers", reflect.TypeOf((*MockQuerier)(nil).RefreshTopAdvertisers), ctx)
}
// RefreshTopNodes mocks base method.
func (m *MockQuerier) RefreshTopNodes(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshTopNodes", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// RefreshTopNodes indicates an expected call of RefreshTopNodes.
func (mr *MockQuerierMockRecorder) RefreshTopNodes(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTopNodes", reflect.TypeOf((*MockQuerier)(nil).RefreshTopNodes), ctx)
}
// RefreshTopObservers mocks base method.
func (m *MockQuerier) RefreshTopObservers(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshTopObservers", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// RefreshTopObservers indicates an expected call of RefreshTopObservers.
func (mr *MockQuerierMockRecorder) RefreshTopObservers(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTopObservers", reflect.TypeOf((*MockQuerier)(nil).RefreshTopObservers), ctx)
}
// RefreshTopTalkers mocks base method.
func (m *MockQuerier) RefreshTopTalkers(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshTopTalkers", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// RefreshTopTalkers indicates an expected call of RefreshTopTalkers.
func (mr *MockQuerierMockRecorder) RefreshTopTalkers(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshTopTalkers", reflect.TypeOf((*MockQuerier)(nil).RefreshTopTalkers), ctx)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterRollupHours", reflect.TypeOf((*MockQuerier)(nil).RegisterRollupHours), ctx, since)
}
// ResolveEndpointHashPairs mocks base method.
@@ -1775,6 +1851,189 @@ func (mr *MockQuerierMockRecorder) ResolvePathHashesP4(ctx, arg any) *gomock.Cal
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolvePathHashesP4", reflect.TypeOf((*MockQuerier)(nil).ResolvePathHashesP4), ctx, arg)
}
// RollAdvertHearings mocks base method.
func (m *MockQuerier) RollAdvertHearings(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollAdvertHearings", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollAdvertHearings indicates an expected call of RollAdvertHearings.
func (mr *MockQuerierMockRecorder) RollAdvertHearings(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollAdvertHearings", reflect.TypeOf((*MockQuerier)(nil).RollAdvertHearings), ctx, hour)
}
// RollAdvertSets mocks base method.
func (m *MockQuerier) RollAdvertSets(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollAdvertSets", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollAdvertSets indicates an expected call of RollAdvertSets.
func (mr *MockQuerierMockRecorder) RollAdvertSets(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollAdvertSets", reflect.TypeOf((*MockQuerier)(nil).RollAdvertSets), ctx, hour)
}
// RollIATAObservations mocks base method.
func (m *MockQuerier) RollIATAObservations(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollIATAObservations", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollIATAObservations indicates an expected call of RollIATAObservations.
func (mr *MockQuerierMockRecorder) RollIATAObservations(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollIATAObservations", reflect.TypeOf((*MockQuerier)(nil).RollIATAObservations), ctx, hour)
}
// RollObserverActivity mocks base method.
func (m *MockQuerier) RollObserverActivity(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollObserverActivity", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollObserverActivity indicates an expected call of RollObserverActivity.
func (mr *MockQuerierMockRecorder) RollObserverActivity(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollObserverActivity", reflect.TypeOf((*MockQuerier)(nil).RollObserverActivity), ctx, hour)
}
// RollObserverIdentity mocks base method.
func (m *MockQuerier) RollObserverIdentity(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollObserverIdentity", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollObserverIdentity indicates an expected call of RollObserverIdentity.
func (mr *MockQuerierMockRecorder) RollObserverIdentity(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollObserverIdentity", reflect.TypeOf((*MockQuerier)(nil).RollObserverIdentity), ctx, hour)
}
// RollPacketSets mocks base method.
func (m *MockQuerier) RollPacketSets(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollPacketSets", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollPacketSets indicates an expected call of RollPacketSets.
func (mr *MockQuerierMockRecorder) RollPacketSets(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollPacketSets", reflect.TypeOf((*MockQuerier)(nil).RollPacketSets), ctx, hour)
}
// RollPaths mocks base method.
func (m *MockQuerier) RollPaths(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollPaths", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollPaths indicates an expected call of RollPaths.
func (mr *MockQuerierMockRecorder) RollPaths(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollPaths", reflect.TypeOf((*MockQuerier)(nil).RollPaths), ctx, hour)
}
// RollPayloadBreakdown mocks base method.
func (m *MockQuerier) RollPayloadBreakdown(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollPayloadBreakdown", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollPayloadBreakdown indicates an expected call of RollPayloadBreakdown.
func (mr *MockQuerierMockRecorder) RollPayloadBreakdown(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollPayloadBreakdown", reflect.TypeOf((*MockQuerier)(nil).RollPayloadBreakdown), ctx, hour)
}
// RollScopeSets mocks base method.
func (m *MockQuerier) RollScopeSets(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollScopeSets", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollScopeSets indicates an expected call of RollScopeSets.
func (mr *MockQuerierMockRecorder) RollScopeSets(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollScopeSets", reflect.TypeOf((*MockQuerier)(nil).RollScopeSets), ctx, hour)
}
// RollSignal mocks base method.
func (m *MockQuerier) RollSignal(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollSignal", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollSignal indicates an expected call of RollSignal.
func (mr *MockQuerierMockRecorder) RollSignal(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollSignal", reflect.TypeOf((*MockQuerier)(nil).RollSignal), ctx, hour)
}
// RollTalkerSets mocks base method.
func (m *MockQuerier) RollTalkerSets(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollTalkerSets", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// RollTalkerSets indicates an expected call of RollTalkerSets.
func (mr *MockQuerierMockRecorder) RollTalkerSets(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollTalkerSets", reflect.TypeOf((*MockQuerier)(nil).RollTalkerSets), ctx, hour)
}
// RollupContentHash mocks base method.
func (m *MockQuerier) RollupContentHash(ctx context.Context, hour pgtype.Timestamptz) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollupContentHash", ctx, hour)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// RollupContentHash indicates an expected call of RollupContentHash.
func (mr *MockQuerierMockRecorder) RollupContentHash(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollupContentHash", reflect.TypeOf((*MockQuerier)(nil).RollupContentHash), ctx, hour)
}
// RollupUnlock mocks base method.
func (m *MockQuerier) RollupUnlock(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RollupUnlock", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// RollupUnlock indicates an expected call of RollupUnlock.
func (mr *MockQuerierMockRecorder) RollupUnlock(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollupUnlock", reflect.TypeOf((*MockQuerier)(nil).RollupUnlock), ctx)
}
// SaveChannelCatalogue mocks base method.
func (m *MockQuerier) SaveChannelCatalogue(ctx context.Context, arg db.SaveChannelCatalogueParams) error {
m.ctrl.T.Helper()
@@ -1902,6 +2161,20 @@ func (mr *MockQuerierMockRecorder) SetPacketDecrypted(ctx, packetHash any) *gomo
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPacketDecrypted", reflect.TypeOf((*MockQuerier)(nil).SetPacketDecrypted), ctx, packetHash)
}
// SetRollupHourPartial mocks base method.
func (m *MockQuerier) SetRollupHourPartial(ctx context.Context, hour pgtype.Timestamptz) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetRollupHourPartial", ctx, hour)
ret0, _ := ret[0].(error)
return ret0
}
// SetRollupHourPartial indicates an expected call of SetRollupHourPartial.
func (mr *MockQuerierMockRecorder) SetRollupHourPartial(ctx, hour any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetRollupHourPartial", reflect.TypeOf((*MockQuerier)(nil).SetRollupHourPartial), ctx, hour)
}
// TouchObserverBrokers mocks base method.
func (m *MockQuerier) TouchObserverBrokers(ctx context.Context, arg db.TouchObserverBrokersParams) error {
m.ctrl.T.Helper()
@@ -1944,6 +2217,21 @@ func (mr *MockQuerierMockRecorder) TouchPackets(ctx, arg any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TouchPackets", reflect.TypeOf((*MockQuerier)(nil).TouchPackets), ctx, arg)
}
// TryRollupLock mocks base method.
func (m *MockQuerier) TryRollupLock(ctx context.Context) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "TryRollupLock", ctx)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// TryRollupLock indicates an expected call of TryRollupLock.
func (mr *MockQuerierMockRecorder) TryRollupLock(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryRollupLock", reflect.TypeOf((*MockQuerier)(nil).TryRollupLock), ctx)
}
// UpdateObserverRegionScope mocks base method.
func (m *MockQuerier) UpdateObserverRegionScope(ctx context.Context, arg db.UpdateObserverRegionScopeParams) error {
m.ctrl.T.Helper()
@@ -2146,18 +2434,18 @@ func (mr *MockQuerierMockRecorder) UpsertNodeShortID(ctx, arg any) *gomock.Call
}
// UpsertObserver mocks base method.
func (m *MockQuerier) UpsertObserver(ctx context.Context, publicKey []byte) (db.Observer, error) {
func (m *MockQuerier) UpsertObserver(ctx context.Context, arg db.UpsertObserverParams) (db.Observer, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpsertObserver", ctx, publicKey)
ret := m.ctrl.Call(m, "UpsertObserver", ctx, arg)
ret0, _ := ret[0].(db.Observer)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpsertObserver indicates an expected call of UpsertObserver.
func (mr *MockQuerierMockRecorder) UpsertObserver(ctx, publicKey any) *gomock.Call {
func (mr *MockQuerierMockRecorder) UpsertObserver(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertObserver", reflect.TypeOf((*MockQuerier)(nil).UpsertObserver), ctx, publicKey)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertObserver", reflect.TypeOf((*MockQuerier)(nil).UpsertObserver), ctx, arg)
}
// UpsertObserverBroker mocks base method.
@@ -2218,20 +2506,6 @@ func (mr *MockQuerierMockRecorder) UpsertRegion(ctx, arg any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertRegion", reflect.TypeOf((*MockQuerier)(nil).UpsertRegion), ctx, arg)
}
// UpsertTraceIATA mocks base method.
func (m *MockQuerier) UpsertTraceIATA(ctx context.Context, arg db.UpsertTraceIATAParams) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpsertTraceIATA", ctx, arg)
ret0, _ := ret[0].(error)
return ret0
}
// UpsertTraceIATA indicates an expected call of UpsertTraceIATA.
func (mr *MockQuerierMockRecorder) UpsertTraceIATA(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertTraceIATA", reflect.TypeOf((*MockQuerier)(nil).UpsertTraceIATA), ctx, arg)
}
// UpsertTransportScope mocks base method.
func (m *MockQuerier) UpsertTransportScope(ctx context.Context, arg db.UpsertTransportScopeParams) error {
m.ctrl.T.Helper()
+115 -208
View File
@@ -16,96 +16,40 @@ type Account struct {
DeactivatedAt pgtype.Timestamptz `json:"deactivated_at"`
}
type AnalyticsHourlyIataStat struct {
Iata string `json:"iata"`
Hour pgtype.Timestamptz `json:"hour"`
ObservationCount int64 `json:"observation_count"`
UniquePackets int64 `json:"unique_packets"`
type AnalyticsDirtyHour struct {
Hour pgtype.Timestamptz `json:"hour"`
EnqueuedAt pgtype.Timestamptz `json:"enqueued_at"`
}
type AnalyticsLiveHourlyIataStat struct {
Iata string `json:"iata"`
Hour pgtype.Timestamptz `json:"hour"`
ObservationCount int64 `json:"observation_count"`
UniquePackets int64 `json:"unique_packets"`
}
type AnalyticsLiveObserverActivityHourly struct {
ObserverID uuid.UUID `json:"observer_id"`
PayloadType int16 `json:"payload_type"`
Bucket pgtype.Timestamptz `json:"bucket"`
Observations int64 `json:"observations"`
AirtimeMs float32 `json:"airtime_ms"`
AirtimeN int64 `json:"airtime_n"`
SnrSum float32 `json:"snr_sum"`
SnrN int64 `json:"snr_n"`
SnrMin float32 `json:"snr_min"`
RssiSum int64 `json:"rssi_sum"`
RssiN int64 `json:"rssi_n"`
}
type AnalyticsLivePathStatsHourly struct {
Iata string `json:"iata"`
Hour interface{} `json:"hour"`
Category int32 `json:"category"`
HashBytes int32 `json:"hash_bytes"`
Entries int32 `json:"entries"`
Receptions int64 `json:"receptions"`
}
type AnalyticsLivePayloadBreakdownByIatum struct {
Iata string `json:"iata"`
PayloadType *int16 `json:"payload_type"`
Bucket pgtype.Timestamptz `json:"bucket"`
Count int64 `json:"count"`
}
type AnalyticsLiveSignalStatsHourly struct {
Iata string `json:"iata"`
Hour interface{} `json:"hour"`
Kind int32 `json:"kind"`
SnrBin int32 `json:"snr_bin"`
RssiBin int32 `json:"rssi_bin"`
Receptions int64 `json:"receptions"`
SnrSamples int64 `json:"snr_samples"`
SnrSum float64 `json:"snr_sum"`
RssiSamples int64 `json:"rssi_samples"`
RssiSum float64 `json:"rssi_sum"`
}
type AnalyticsLiveTopAdvertisersByIatum struct {
Iata string `json:"iata"`
NodeID uuid.UUID `json:"node_id"`
Name *string `json:"name"`
NodeType int16 `json:"node_type"`
Bucket pgtype.Timestamptz `json:"bucket"`
AdvertCount int64 `json:"advert_count"`
FloodAdvertCount int64 `json:"flood_advert_count"`
DirectAdvertCount int64 `json:"direct_advert_count"`
LastHeard interface{} `json:"last_heard"`
}
type AnalyticsLiveTopObserversByIatum struct {
Iata string `json:"iata"`
ObserverID uuid.UUID `json:"observer_id"`
DisplayName *string `json:"display_name"`
ObserverType *string `json:"observer_type"`
Bucket pgtype.Timestamptz `json:"bucket"`
ObservationCount int64 `json:"observation_count"`
}
type AnalyticsLiveTopTalkersByIatum struct {
type AnalyticsHourlyAdvertHearing struct {
Hour pgtype.Timestamptz `json:"hour"`
Iata string `json:"iata"`
SenderName *string `json:"sender_name"`
Bucket pgtype.Timestamptz `json:"bucket"`
MessageCount int64 `json:"message_count"`
LastSent interface{} `json:"last_sent"`
OriginPubkey []byte `json:"origin_pubkey"`
Observations int64 `json:"observations"`
LastHeard pgtype.Timestamptz `json:"last_heard"`
Name *string `json:"name"`
NodeType *int16 `json:"node_type"`
}
type AnalyticsObserverActivityHourly struct {
type AnalyticsHourlyAdvertSet struct {
Hour pgtype.Timestamptz `json:"hour"`
OriginPubkey []byte `json:"origin_pubkey"`
Iatas []string `json:"iatas"`
AdvertPackets int64 `json:"advert_packets"`
FloodPackets int64 `json:"flood_packets"`
DirectPackets int64 `json:"direct_packets"`
}
type AnalyticsHourlyIataObservation struct {
Hour pgtype.Timestamptz `json:"hour"`
Iata string `json:"iata"`
ObservationCount int64 `json:"observation_count"`
}
type AnalyticsHourlyObserverActivity struct {
Hour pgtype.Timestamptz `json:"hour"`
ObserverID uuid.UUID `json:"observer_id"`
PayloadType int16 `json:"payload_type"`
Bucket pgtype.Timestamptz `json:"bucket"`
Observations int64 `json:"observations"`
AirtimeMs *float32 `json:"airtime_ms"`
AirtimeN int64 `json:"airtime_n"`
@@ -116,62 +60,81 @@ type AnalyticsObserverActivityHourly struct {
RssiN int64 `json:"rssi_n"`
}
type AnalyticsPathStatsHourly struct {
Iata string `json:"iata"`
type AnalyticsHourlyObserverIdentity struct {
Hour pgtype.Timestamptz `json:"hour"`
Iata string `json:"iata"`
ObserverID uuid.UUID `json:"observer_id"`
ObservationCount int64 `json:"observation_count"`
DisplayName *string `json:"display_name"`
ObserverType *string `json:"observer_type"`
}
type AnalyticsHourlyPacketSet struct {
Hour pgtype.Timestamptz `json:"hour"`
Iatas []string `json:"iatas"`
Packets int64 `json:"packets"`
}
type AnalyticsHourlyPath struct {
Hour pgtype.Timestamptz `json:"hour"`
Iata string `json:"iata"`
Category int32 `json:"category"`
HashBytes int32 `json:"hash_bytes"`
Entries int32 `json:"entries"`
Receptions int64 `json:"receptions"`
}
type AnalyticsPayloadBreakdownByIatum struct {
type AnalyticsHourlyPayloadBreakdown struct {
Hour pgtype.Timestamptz `json:"hour"`
Iata string `json:"iata"`
PayloadType int16 `json:"payload_type"`
Bucket pgtype.Timestamptz `json:"bucket"`
Count int64 `json:"count"`
}
type AnalyticsSignalStatsHourly struct {
Iata string `json:"iata"`
type AnalyticsHourlyScopeSet struct {
Hour pgtype.Timestamptz `json:"hour"`
Iatas []string `json:"iatas"`
ScopeID int32 `json:"scope_id"`
Packets int64 `json:"packets"`
}
type AnalyticsHourlySignal struct {
Hour pgtype.Timestamptz `json:"hour"`
Iata string `json:"iata"`
Kind int32 `json:"kind"`
SnrBin int32 `json:"snr_bin"`
RssiBin int32 `json:"rssi_bin"`
Receptions int64 `json:"receptions"`
SnrSamples int64 `json:"snr_samples"`
SnrSum *float64 `json:"snr_sum"`
SnrSum float64 `json:"snr_sum"`
RssiSamples int64 `json:"rssi_samples"`
RssiSum *float64 `json:"rssi_sum"`
RssiSum float64 `json:"rssi_sum"`
}
type AnalyticsTopAdvertisersByIatum struct {
Iata string `json:"iata"`
NodeID uuid.UUID `json:"node_id"`
Bucket pgtype.Timestamptz `json:"bucket"`
AdvertCount int64 `json:"advert_count"`
FloodAdvertCount int64 `json:"flood_advert_count"`
DirectAdvertCount int64 `json:"direct_advert_count"`
LastHeard pgtype.Timestamptz `json:"last_heard"`
Name *string `json:"name"`
NodeType *int16 `json:"node_type"`
type AnalyticsHourlyTalkerSet struct {
Hour pgtype.Timestamptz `json:"hour"`
SenderName string `json:"sender_name"`
Iatas []string `json:"iatas"`
Messages int64 `json:"messages"`
LastSent pgtype.Timestamptz `json:"last_sent"`
}
type AnalyticsTopObserversByIatum struct {
Iata string `json:"iata"`
ObserverID uuid.UUID `json:"observer_id"`
Bucket pgtype.Timestamptz `json:"bucket"`
ObservationCount int64 `json:"observation_count"`
DisplayName *string `json:"display_name"`
ObserverType *string `json:"observer_type"`
type AnalyticsRawState struct {
Singleton bool `json:"singleton"`
RawDeletedBefore pgtype.Timestamptz `json:"raw_deleted_before"`
Coverage int64 `json:"coverage"`
}
type AnalyticsTopTalkersByIatum struct {
Iata string `json:"iata"`
SenderName string `json:"sender_name"`
Bucket pgtype.Timestamptz `json:"bucket"`
MessageCount int64 `json:"message_count"`
LastSent pgtype.Timestamptz `json:"last_sent"`
type AnalyticsRollupHour struct {
Hour pgtype.Timestamptz `json:"hour"`
Status string `json:"status"`
RolledAt pgtype.Timestamptz `json:"rolled_at"`
ContentHash *string `json:"content_hash"`
}
type AnalyticsState struct {
Singleton bool `json:"singleton"`
Revision int64 `json:"revision"`
}
type Channel struct {
@@ -185,7 +148,7 @@ type Channel struct {
KeyKnown *bool `json:"key_known"`
FirstSeen pgtype.Timestamptz `json:"first_seen"`
LastSeen pgtype.Timestamptz `json:"last_seen"`
MessageCount *int64 `json:"message_count"`
MessageCount int64 `json:"message_count"`
}
type ChannelConfigScope struct {
@@ -287,100 +250,11 @@ type MeshmapperZoneList struct {
LastError string `json:"last_error"`
}
type MvHourlyIataStat struct {
Iata string `json:"iata"`
Hour pgtype.Timestamptz `json:"hour"`
ObservationCount int64 `json:"observation_count"`
UniquePackets int64 `json:"unique_packets"`
ActiveObservers int64 `json:"active_observers"`
}
type MvObserverActivityHourly struct {
ObserverID uuid.UUID `json:"observer_id"`
PayloadType int16 `json:"payload_type"`
Bucket pgtype.Timestamptz `json:"bucket"`
Observations int64 `json:"observations"`
AirtimeMs float32 `json:"airtime_ms"`
AirtimeN int64 `json:"airtime_n"`
SnrSum float32 `json:"snr_sum"`
SnrN int64 `json:"snr_n"`
SnrMin float32 `json:"snr_min"`
RssiSum int64 `json:"rssi_sum"`
RssiN int64 `json:"rssi_n"`
}
type MvPathStatsHourly struct {
Iata string `json:"iata"`
Hour interface{} `json:"hour"`
Category int32 `json:"category"`
HashBytes int32 `json:"hash_bytes"`
Entries int32 `json:"entries"`
Receptions int64 `json:"receptions"`
}
type MvPayloadBreakdownByIatum struct {
Iata string `json:"iata"`
PayloadType *int16 `json:"payload_type"`
Bucket pgtype.Timestamptz `json:"bucket"`
Count int64 `json:"count"`
}
type MvRadioPreset struct {
Preset string `json:"preset"`
Iata string `json:"iata"`
SourceType string `json:"source_type"`
Count int64 `json:"count"`
}
type MvSignalStatsHourly struct {
Iata string `json:"iata"`
Hour interface{} `json:"hour"`
Kind int32 `json:"kind"`
SnrBin int32 `json:"snr_bin"`
RssiBin int32 `json:"rssi_bin"`
Receptions int64 `json:"receptions"`
SnrSamples int64 `json:"snr_samples"`
SnrSum float64 `json:"snr_sum"`
RssiSamples int64 `json:"rssi_samples"`
RssiSum float64 `json:"rssi_sum"`
}
type MvTopAdvertisersByIatum struct {
Iata string `json:"iata"`
NodeID uuid.UUID `json:"node_id"`
Name *string `json:"name"`
NodeType int16 `json:"node_type"`
Bucket pgtype.Timestamptz `json:"bucket"`
AdvertCount int64 `json:"advert_count"`
FloodAdvertCount int64 `json:"flood_advert_count"`
DirectAdvertCount int64 `json:"direct_advert_count"`
LastHeard interface{} `json:"last_heard"`
}
type MvTopNodesByIatum struct {
Iata string `json:"iata"`
NodeID uuid.UUID `json:"node_id"`
Name *string `json:"name"`
NodeType int16 `json:"node_type"`
ObservationCount *int64 `json:"observation_count"`
LastHeard pgtype.Timestamptz `json:"last_heard"`
}
type MvTopObserversByIatum struct {
Iata string `json:"iata"`
ObserverID uuid.UUID `json:"observer_id"`
DisplayName *string `json:"display_name"`
ObserverType *string `json:"observer_type"`
Bucket pgtype.Timestamptz `json:"bucket"`
ObservationCount int64 `json:"observation_count"`
}
type MvTopTalkersByIatum struct {
Iata string `json:"iata"`
SenderName *string `json:"sender_name"`
Bucket pgtype.Timestamptz `json:"bucket"`
MessageCount int64 `json:"message_count"`
LastSent interface{} `json:"last_sent"`
Preset string `json:"preset"`
Iata *string `json:"iata"`
SourceType string `json:"source_type"`
Count int64 `json:"count"`
}
type Node struct {
@@ -455,6 +329,8 @@ type Observer struct {
ObservationCount *int64 `json:"observation_count"`
Metadata []byte `json:"metadata"`
RegionScope *string `json:"region_scope"`
LastIata *string `json:"last_iata"`
LastIataAt pgtype.Timestamptz `json:"last_iata_at"`
}
type ObserverBroker struct {
@@ -525,6 +401,7 @@ type Packet struct {
TraceTag []byte `json:"trace_tag"`
FirstHeardAt pgtype.Timestamptz `json:"first_heard_at"`
LastHeardAt pgtype.Timestamptz `json:"last_heard_at"`
ObservationCount int64 `json:"observation_count"`
}
type PacketObservation struct {
@@ -569,12 +446,42 @@ type RegionIata struct {
AddedAt pgtype.Timestamptz `json:"added_at"`
}
type RollupOb struct {
PacketHash []byte `json:"packet_hash"`
ObserverID uuid.UUID `json:"observer_id"`
Iata string `json:"iata"`
HeardAt pgtype.Timestamptz `json:"heard_at"`
PathLengthByte int16 `json:"path_length_byte"`
HashSize int16 `json:"hash_size"`
HopCount int16 `json:"hop_count"`
PathLen int32 `json:"path_len"`
Rssi *int16 `json:"rssi"`
Snr *float32 `json:"snr"`
AirtimeMs *float32 `json:"airtime_ms"`
PayloadType *int16 `json:"payload_type"`
RouteType int16 `json:"route_type"`
OriginPubkey []byte `json:"origin_pubkey"`
ScopeID *int32 `json:"scope_id"`
}
type TraceIata struct {
TraceTag []byte `json:"trace_tag"`
Iata string `json:"iata"`
LastHeard pgtype.Timestamptz `json:"last_heard"`
}
type TraceTag struct {
TraceTag []byte `json:"trace_tag"`
FirstHeardAt pgtype.Timestamptz `json:"first_heard_at"`
LastHeardAt pgtype.Timestamptz `json:"last_heard_at"`
PacketCount int64 `json:"packet_count"`
TraceType *string `json:"trace_type"`
ScopeID *int32 `json:"scope_id"`
BestPayload []byte `json:"best_payload"`
BestPathLen int32 `json:"best_path_len"`
Heard bool `json:"heard"`
}
type TransportScope struct {
ID int32 `json:"id"`
Name string `json:"name"`
+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
}
+91 -37
View File
@@ -40,8 +40,11 @@ type Querier interface {
// Opt-in age-out: preserve retained history and manually recorded ownership.
// Bound deletions per cleanup tick and skip observers being updated by ingest.
DeleteOldObservers(ctx context.Context, lastSeen pgtype.Timestamptz) ([]uuid.UUID, error)
// Locks, archives and cascades one bounded packet cohort in a single transaction.
// Deletes one bounded cohort (observations cascade). SKIP LOCKED leaves packets an in-flight
// observation insert holds. raw_deleted_before tells the rollup which hours lost raw rows.
DeleteOldPackets(ctx context.Context, arg DeleteOldPacketsParams) (int64, error)
// Bumps coverage when hours go, so cached responses that included them are not reused.
DeleteOldRollups(ctx context.Context, cutoff pgtype.Timestamptz) error
// One batch of routes past retention, or past grace with too few observations.
// GREATEST keeps the scan on idx_known_routes_last_seen.
DeleteOldRoutes(ctx context.Context, arg DeleteOldRoutesParams) (int64, error)
@@ -49,12 +52,25 @@ type Querier interface {
DeleteOldTelemetry(ctx context.Context, reportedAt pgtype.Timestamptz) error
// Keeps the trace IATA filter in step with packet retention.
DeleteOldTraceIATAs(ctx context.Context, lastHeard pgtype.Timestamptz) error
DeleteOldTraceTags(ctx context.Context, lastHeardAt pgtype.Timestamptz) error
DeleteRegionIATAsNotIn(ctx context.Context, arg DeleteRegionIATAsNotInParams) error
DeleteRollupHour(ctx context.Context, hour pgtype.Timestamptz) error
// Dirty hours that can no longer be re-rolled.
DeleteStaleDirtyHours(ctx context.Context) error
FillRollupObs(ctx context.Context, dollar_1 pgtype.Timestamptz) error
// Marks the hour complete and bumps the revision only when its content changed.
FinishRollupHour(ctx context.Context, arg FinishRollupHourParams) (bool, error)
GetAccount(ctx context.Context, id uuid.UUID) (Account, error)
// Both counters only grow, so their sum changes whenever rolled content or coverage does.
GetAnalyticsRevision(ctx context.Context) (int64, error)
GetChannelByID(ctx context.Context, id int32) (Channel, error)
// Returns neighbors of a node that are in a different IATA.
GetCrossIATANeighbors(ctx context.Context, arg GetCrossIATANeighborsParams) ([]GetCrossIATANeighborsRow, error)
GetHourlyStats(ctx context.Context, arg GetHourlyStatsParams) ([]MvHourlyIataStat, error)
GetEarliestCompleteRollupHour(ctx context.Context) (pgtype.Timestamptz, error)
// ============================================================
// STATS
// ============================================================
GetHourlyStats(ctx context.Context, arg GetHourlyStatsParams) ([]GetHourlyStatsRow, error)
GetIATA(ctx context.Context, iata string) (IataCode, error)
// An imported MeshMapper boundary overrides the configured one. border is NULL
// when neither exists; a missing row (unknown IATA) is sql.ErrNoRows, same
@@ -83,30 +99,30 @@ type Querier interface {
// Count packet identities, not reception rows: a broker repeat or another
// reception by the same observer must not inflate the comparison.
GetObserverComparison(ctx context.Context, arg GetObserverComparisonParams) (GetObserverComparisonRow, error)
GetObserverLastIATA(ctx context.Context, observerID uuid.UUID) (string, error)
GetObserverLastIATA(ctx context.Context, id uuid.UUID) (string, error)
GetObserverRadio(ctx context.Context, id uuid.UUID) (GetObserverRadioRow, error)
GetObserverScopes(ctx context.Context, observerID uuid.UUID) ([]string, error)
GetObserverTelemetry(ctx context.Context, arg GetObserverTelemetryParams) ([]GetObserverTelemetryRow, error)
GetPacketByHash(ctx context.Context, packetHash []byte) (GetPacketByHashRow, error)
GetPacketObservationCount(ctx context.Context, packetHash []byte) (int64, error)
// Return distinct observation IATAs in first-heard order for path resolution,
// without fetching full observations separately for every trace packet.
GetPacketsByTraceTag(ctx context.Context, decode string) ([]GetPacketsByTraceTagRow, error)
// All HTTP aggregates read the compact classification snapshot.
// All HTTP aggregates read the hourly rollup, never observations.
GetPathStats(ctx context.Context, arg GetPathStatsParams) ([]GetPathStatsRow, error)
GetRadioPresets(ctx context.Context, arg GetRadioPresetsParams) ([]MvRadioPreset, error)
GetRegion(ctx context.Context, id int32) (GetRegionRow, error)
GetRegionBySlug(ctx context.Context, slug string) (GetRegionBySlugRow, error)
GetRegionIATAs(ctx context.Context, regionID int32) ([]string, error)
GetRouteEvidenceRoute(ctx context.Context, arg GetRouteEvidenceRouteParams) (KnownRoute, error)
// Packets and IATAs cover every retained rollup hour; IATAs are those that heard the
// scope's own packets. Observer and node counts are current memberships.
GetScopeByName(ctx context.Context, name string) (GetScopeByNameRow, error)
GetScopeCatalogue(ctx context.Context, arg GetScopeCatalogueParams) (MeshmapperScopeCatalogue, error)
GetScopeNames(ctx context.Context) ([]string, error)
// Aggregate matching observations once, separately from node memberships to avoid
// a cross-join. Empty IATAs keep the original global counts, including associations
// whose observations have expired; the filtered aggregates are empty in that case.
GetScopeStats(ctx context.Context, iatas []string) ([]GetScopeStatsRow, error)
// Read compact hourly snapshots, never observations on an HTTP request.
// Packets since the given hour come from the IATA-set rollup (counted once per hour heard).
// Observer and node counts are current memberships; observers filter by their latest IATA.
GetScopeStats(ctx context.Context, arg GetScopeStatsParams) ([]GetScopeStatsRow, error)
// Read the hourly rollup, never observations on an HTTP request.
// Weight averages by sample counts instead of averaging regional/hourly means.
GetSignalStats(ctx context.Context, arg GetSignalStatsParams) ([]GetSignalStatsRow, error)
// Repeaters/room servers (node_type 2/3) whose current advert-derived clock drift exceeds
@@ -116,21 +132,24 @@ type Querier interface {
GetStatsClockDrift(ctx context.Context, arg GetStatsClockDriftParams) ([]GetStatsClockDriftRow, error)
// Returns node counts grouped by type, optionally filtered by IATA.
GetStatsNodeTypes(ctx context.Context, dollar_1 []string) ([]GetStatsNodeTypesRow, error)
// ============================================================
// STATS
// ============================================================
GetStatsOverview(ctx context.Context, dollar_1 []string) (GetStatsOverviewRow, error)
// Payload-type counts for the IATA within the window, summed from the
// precomputed hourly buckets.
// Payload-type observation counts since the given hour, from the hourly rollup.
GetStatsPayloadBreakdown(ctx context.Context, arg GetStatsPayloadBreakdownParams) ([]GetStatsPayloadBreakdownRow, error)
// Top N advertisers in the window, summed from the hourly buckets.
// One row per hour in [since, until) plus a summary row (hour NULL) over its complete hours.
// Counts sum; observers, IATAs and scopes are distinct across the window; packets count
// once per hour heard. Each branch pair keeps "all IATAs" and "these IATAs" plans separate.
GetStatsSeries(ctx context.Context, arg GetStatsSeriesParams) ([]GetStatsSeriesRow, error)
// Top N advertisers since the given hour. Each ADVERT packet counts once per hour heard,
// however many of the requested IATAs heard it (IATA-set rollup). Live names win.
GetStatsTopAdvertisers(ctx context.Context, arg GetStatsTopAdvertisersParams) ([]GetStatsTopAdvertisersRow, error)
// Top N observers for the IATA within the window, summed from the precomputed
// hourly buckets. Counts sum across matched IATAs; iata is a representative one.
// Top N observers since the given hour. Counts sum across matched IATAs; iata is a
// representative one. Live names win over the rolled snapshot.
GetStatsTopObservers(ctx context.Context, arg GetStatsTopObserversParams) ([]GetStatsTopObserversRow, error)
// Top N talkers (by decrypted sender_name) in the window, summed from the hourly buckets.
// Top N senders since the given hour. Each message counts once per hour heard, however many
// of the requested IATAs heard it (IATA-set rollup).
GetStatsTopTalkers(ctx context.Context, arg GetStatsTopTalkersParams) ([]GetStatsTopTalkersRow, error)
GetTopNodes(ctx context.Context, arg GetTopNodesParams) ([]MvTopNodesByIatum, error)
// Nodes by ADVERT hearings since the given hour. Live names win over the rolled snapshot;
// node_id is NULL once the node row is gone. iata is a representative one.
GetTopNodes(ctx context.Context, arg GetTopNodesParams) ([]GetTopNodesRow, error)
GetTransportScopeByName(ctx context.Context, name string) (int32, error)
GetTransportScopes(ctx context.Context) ([]GetTransportScopesRow, error)
// ============================================================
@@ -138,11 +157,14 @@ type Querier interface {
// ============================================================
// Read the immutable first-packet scope in the same statement as insertion.
// A later reception's transport code must not give live and historical messages different tags.
// message_count is a lifetime count, bumped only for a new message. A historical (backfilled)
// message queues its observation hours for a re-roll in the same statement, so the two can't diverge.
InsertChannelMessage(ctx context.Context, arg InsertChannelMessageParams) (InsertChannelMessageRow, error)
// ============================================================
// PACKET OBSERVATIONS
// ============================================================
InsertObservation(ctx context.Context, arg InsertObservationParams) (PacketObservation, error)
// Bumps packets.observation_count only for a new row; a duplicate returns the current count.
InsertObservation(ctx context.Context, arg InsertObservationParams) (InsertObservationRow, error)
// Inserts a telemetry snapshot for an observer. The reported_at timestamp should
// be truncated to the configured resolution before calling to ensure deduplication.
InsertObserverTelemetry(ctx context.Context, arg InsertObserverTelemetryParams) error
@@ -170,6 +192,9 @@ type Querier interface {
// Keep the non-null tuple boundary separate from the legacy optional cursor so
// generic prepared plans can seek directly into the composite ordered index.
ListChannelsAfter(ctx context.Context, arg ListChannelsAfterParams) ([]Channel, error)
// Only complete hours whose raw rows are still intact can be re-rolled. Queued missing hours
// wait: their first roll consumes the entry only if the entry predates its snapshot.
ListDirtyRollupHours(ctx context.Context, limit int32) ([]pgtype.Timestamptz, error)
ListIATAs(ctx context.Context) ([]IataCode, error)
// Only one branch runs. Keep the IATA range ordered by the composite index:
// generic plans can otherwise prefer scanning the global timestamp index.
@@ -178,6 +203,7 @@ type Querier interface {
// Returns messages after the given message ID, ordered oldest first.
// Used for WS reconnect backfill.
ListMessagesAfterID(ctx context.Context, arg ListMessagesAfterIDParams) ([]ListMessagesAfterIDRow, error)
ListMissingRollupHours(ctx context.Context, limit int32) ([]pgtype.Timestamptz, error)
ListNodeObservations(ctx context.Context, arg ListNodeObservationsParams) ([]ListNodeObservationsRow, error)
// Limit the filtered node page before enriching IATA membership and neighbours.
ListNodes(ctx context.Context, arg ListNodesParams) ([]ListNodesRow, error)
@@ -224,9 +250,7 @@ type Querier interface {
// ============================================================
// TRACES
// ============================================================
// Returns distinct trace tags with summary info, ordered by most recent first.
// IATA membership comes from trace_iatas (joining observations here spilled the
// hash join). Per-tag details filled in only for the returned page.
// Tags, most recent first. Filters match the tag summary (see RecordTrace).
ListTraceTags(ctx context.Context, arg ListTraceTagsParams) ([]ListTraceTagsRow, error)
// Returns GRP_TXT packets (payload_type=5) never successfully decrypted. Used at boot to
// retry decryption against the current keystore for packets whose channel key was only added
@@ -237,6 +261,12 @@ type Querier interface {
ListUndecryptedGroupTextPacketsByHash(ctx context.Context, hashes [][]byte) ([]ListUndecryptedGroupTextPacketsByHashRow, error)
ListZoneBoundaries(ctx context.Context) ([]MeshmapperZoneBoundary, error)
ListZoneLists(ctx context.Context) ([]MeshmapperZoneList, error)
// Returns the hour's status, or 'partial' if raw deletion has reached it since registration.
LockRollupHour(ctx context.Context, hour pgtype.Timestamptz) (string, error)
// Hours whose raw rows cleanup already started deleting can never be rolled completely.
// Readers report the new status, so the revision moves too.
MarkPartialRollupHours(ctx context.Context) error
OldestMissingRollupHour(ctx context.Context) (pgtype.Timestamptz, error)
PruneImportedRegions(ctx context.Context, keep []string) ([]string, error)
// Drops imports for IATAs no longer configured, so their manual border returns.
PruneZoneBoundaries(ctx context.Context, keep []string) ([]string, error)
@@ -248,16 +278,15 @@ type Querier interface {
// 1/2/3/4-byte hop prefixes check prefix_1/2/3/4; ambiguity set supplied by AmbiguousPrefixes),
// and stamps the survivors.
ReconfirmRoutes(ctx context.Context, arg ReconfirmRoutesParams) (int64, error)
RefreshHourlyStats(ctx context.Context) error
RefreshObserverActivity(ctx context.Context) error
RefreshPathStats(ctx context.Context) error
RefreshPayloadBreakdown(ctx context.Context) error
// One hearing of a TRACE packet: trace_iatas refreshes at most hourly and the tag's heard
// window widens (the first hearing replaces the packet's provisional times). Packet counts
// and payloads come from UpsertPacket.
RecordTrace(ctx context.Context, arg RecordTraceParams) error
RefreshRadioPresets(ctx context.Context) error
RefreshSignalStats(ctx context.Context) error
RefreshTopAdvertisers(ctx context.Context) error
RefreshTopNodes(ctx context.Context) error
RefreshTopObservers(ctx context.Context) error
RefreshTopTalkers(ctx context.Context) error
// Adds a 'missing' row for every eligible hour since the earliest retained observation
// (and not before @since), zero-observation hours included.
// No observations yet means nothing to register (GREATEST would ignore the NULL MIN).
RegisterRollupHours(ctx context.Context, since pgtype.Timestamptz) error
// Batch form of ResolveEndpointHashes for a page of packets. Matches the cross
// product of IATAs and hashes; callers pick out the pairs they asked for.
ResolveEndpointHashPairs(ctx context.Context, arg ResolveEndpointHashPairsParams) ([]ResolveEndpointHashPairsRow, error)
@@ -276,6 +305,24 @@ type Querier interface {
ResolvePathHashesP2(ctx context.Context, arg ResolvePathHashesP2Params) ([]ResolvePathHashesP2Row, error)
ResolvePathHashesP3(ctx context.Context, arg ResolvePathHashesP3Params) ([]ResolvePathHashesP3Row, error)
ResolvePathHashesP4(ctx context.Context, arg ResolvePathHashesP4Params) ([]ResolvePathHashesP4Row, error)
RollAdvertHearings(ctx context.Context, hour pgtype.Timestamptz) error
// Each distinct ADVERT packet counts once, under the exact set of IATAs that heard it this hour.
RollAdvertSets(ctx context.Context, hour pgtype.Timestamptz) error
RollIATAObservations(ctx context.Context, hour pgtype.Timestamptz) error
RollObserverActivity(ctx context.Context, hour pgtype.Timestamptz) error
RollObserverIdentity(ctx context.Context, hour pgtype.Timestamptz) error
RollPacketSets(ctx context.Context, hour pgtype.Timestamptz) error
// category: 0 = routed, 1 = zero-hop, 2 = TRACE, 3 = invalid (meshcore-go IsValidPathLen).
RollPaths(ctx context.Context, hour pgtype.Timestamptz) error
RollPayloadBreakdown(ctx context.Context, hour pgtype.Timestamptz) error
RollScopeSets(ctx context.Context, hour pgtype.Timestamptz) error
// kind = grouping(snr_bin, rssi_bin): 3 = per IATA totals, 1 = per SNR bin, 2 = per RSSI bin.
// rssi=0 AND snr=0 means "not reported".
RollSignal(ctx context.Context, hour pgtype.Timestamptz) error
RollTalkerSets(ctx context.Context, hour pgtype.Timestamptz) error
// Order-independent fingerprint of every family row for the hour.
RollupContentHash(ctx context.Context, hour pgtype.Timestamptz) (string, error)
RollupUnlock(ctx context.Context) error
// NULL payload/etag/checked_at retain the last good list after an error or 304.
SaveChannelCatalogue(ctx context.Context, arg SaveChannelCatalogueParams) error
// One statement commits the validated snapshot and its lookup identities together.
@@ -293,11 +340,17 @@ type Querier interface {
SetNodeMultibytePaths(ctx context.Context, id uuid.UUID) error
SetNodeMultibyteTraces(ctx context.Context, id uuid.UUID) error
SetPacketDecrypted(ctx context.Context, packetHash []byte) error
SetRollupHourPartial(ctx context.Context, hour pgtype.Timestamptz) error
TouchObserverBrokers(ctx context.Context, arg TouchObserverBrokersParams) error
// Batched flush of coalesced presence bumps. GREATEST keeps a late flush
// from regressing a newer write-through (e.g. a status update).
TouchObservers(ctx context.Context, arg TouchObserversParams) error
TouchPackets(ctx context.Context, arg TouchPacketsParams) error
// ============================================================
// HOURLY ANALYTICS ROLLUPS
// An hour [H, H+1h) is eligible once now() >= H + 95 min (ingest clamps heard_at to ±30 min).
// ============================================================
TryRollupLock(ctx context.Context) (bool, error)
// Records the observer's own OTA-reported region scope, from the "self"
// field of a /neighbors report. Always known (not queried OTA), so this
// unconditionally overwrites, unlike the neighbor-side region_scope.
@@ -356,7 +409,8 @@ type Querier interface {
// ============================================================
// OBSERVERS
// ============================================================
UpsertObserver(ctx context.Context, publicKey []byte) (Observer, error)
// Empty iata (status/neighbors) leaves last_iata alone; otherwise the newest iata_at wins.
UpsertObserver(ctx context.Context, arg UpsertObserverParams) (Observer, error)
// ============================================================
// OBSERVER BROKERS
// ============================================================
@@ -365,10 +419,10 @@ type Querier interface {
// ============================================================
// PACKETS
// ============================================================
// A new TRACE packet adds itself to its tag summary in the same statement, so the summary
// can't miss a stored packet. 'TRACE' > 'PING'; the longest path keeps the best payload.
UpsertPacket(ctx context.Context, arg UpsertPacketParams) (UpsertPacketRow, error)
UpsertRegion(ctx context.Context, arg UpsertRegionParams) (int32, error)
// Refreshes at most hourly so repeat hears don't churn the row.
UpsertTraceIATA(ctx context.Context, arg UpsertTraceIATAParams) error
// ============================================================
// TRANSPORT CODES
// ============================================================
+371 -365
View File
File diff suppressed because it is too large. Load diff
+501
View File
@@ -0,0 +1,501 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: rollup.sql
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const deleteOldRollups = `-- name: DeleteOldRollups :exec
WITH a AS (DELETE FROM analytics_hourly_iata_observations ta WHERE ta.hour < $1::timestamptz),
b AS (DELETE FROM analytics_hourly_payload_breakdown tb WHERE tb.hour < $1::timestamptz),
c AS (DELETE FROM analytics_hourly_signal tc WHERE tc.hour < $1::timestamptz),
d AS (DELETE FROM analytics_hourly_paths td WHERE td.hour < $1::timestamptz),
e AS (DELETE FROM analytics_hourly_observer_activity te WHERE te.hour < $1::timestamptz),
f AS (DELETE FROM analytics_hourly_observer_identity tf WHERE tf.hour < $1::timestamptz),
g AS (DELETE FROM analytics_hourly_advert_hearings tg WHERE tg.hour < $1::timestamptz),
h AS (DELETE FROM analytics_hourly_advert_sets th WHERE th.hour < $1::timestamptz),
i AS (DELETE FROM analytics_hourly_talker_sets ti WHERE ti.hour < $1::timestamptz),
j AS (DELETE FROM analytics_hourly_packet_sets tj WHERE tj.hour < $1::timestamptz),
k AS (DELETE FROM analytics_hourly_scope_sets tk WHERE tk.hour < $1::timestamptz),
l AS (DELETE FROM analytics_dirty_hours tl WHERE tl.hour < $1::timestamptz),
m AS (DELETE FROM analytics_rollup_hours tm WHERE tm.hour < $1::timestamptz RETURNING 1)
UPDATE analytics_raw_state SET coverage = coverage + 1 WHERE EXISTS (SELECT 1 FROM m)
`
// Bumps coverage when hours go, so cached responses that included them are not reused.
func (q *Queries) DeleteOldRollups(ctx context.Context, cutoff pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, deleteOldRollups, cutoff)
return err
}
const deleteRollupHour = `-- name: DeleteRollupHour :exec
WITH a AS (DELETE FROM analytics_hourly_iata_observations ta WHERE ta.hour = $1::timestamptz),
b AS (DELETE FROM analytics_hourly_payload_breakdown tb WHERE tb.hour = $1::timestamptz),
c AS (DELETE FROM analytics_hourly_signal tc WHERE tc.hour = $1::timestamptz),
d AS (DELETE FROM analytics_hourly_paths td WHERE td.hour = $1::timestamptz),
e AS (DELETE FROM analytics_hourly_observer_activity te WHERE te.hour = $1::timestamptz),
f AS (DELETE FROM analytics_hourly_observer_identity tf WHERE tf.hour = $1::timestamptz),
g AS (DELETE FROM analytics_hourly_advert_hearings tg WHERE tg.hour = $1::timestamptz),
h AS (DELETE FROM analytics_hourly_advert_sets th WHERE th.hour = $1::timestamptz),
i AS (DELETE FROM analytics_hourly_talker_sets ti WHERE ti.hour = $1::timestamptz),
j AS (DELETE FROM analytics_hourly_packet_sets tj WHERE tj.hour = $1::timestamptz)
DELETE FROM analytics_hourly_scope_sets tk WHERE tk.hour = $1::timestamptz
`
func (q *Queries) DeleteRollupHour(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, deleteRollupHour, hour)
return err
}
const deleteStaleDirtyHours = `-- name: DeleteStaleDirtyHours :exec
DELETE FROM analytics_dirty_hours d
WHERE EXISTS (SELECT 1 FROM analytics_rollup_hours h WHERE h.hour = d.hour AND h.status = 'partial')
OR d.hour <= (SELECT raw_deleted_before + INTERVAL '30 minutes' FROM analytics_raw_state)
`
// Dirty hours that can no longer be re-rolled.
func (q *Queries) DeleteStaleDirtyHours(ctx context.Context) error {
_, err := q.db.Exec(ctx, deleteStaleDirtyHours)
return err
}
const fillRollupObs = `-- name: FillRollupObs :exec
INSERT INTO rollup_obs (packet_hash, observer_id, iata, heard_at, path_length_byte, hash_size, hop_count,
path_len, rssi, snr, airtime_ms, payload_type, route_type, origin_pubkey, scope_id)
SELECT po.packet_hash, po.observer_id, po.iata, po.heard_at, po.path_length_byte, po.hash_size, po.hop_count,
COALESCE(octet_length(po.path_bytes), 0), po.rssi, po.snr, po.airtime_ms, po.payload_type,
p.route_type, p.origin_pubkey, p.scope_id
FROM packet_observations po
JOIN packets p ON p.packet_hash = po.packet_hash
WHERE po.heard_at >= $1::timestamptz AND po.heard_at < $1::timestamptz + INTERVAL '1 hour'
`
func (q *Queries) FillRollupObs(ctx context.Context, dollar_1 pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, fillRollupObs, dollar_1)
return err
}
const finishRollupHour = `-- name: FinishRollupHour :one
WITH prev AS (
SELECT ph.content_hash FROM analytics_rollup_hours ph WHERE ph.hour = $1::timestamptz
), upd AS (
UPDATE analytics_rollup_hours uh
SET status = 'complete', rolled_at = now(), content_hash = $2::text
WHERE uh.hour = $1::timestamptz
), undirty AS (
DELETE FROM analytics_dirty_hours dh WHERE dh.hour = $1::timestamptz
), rev AS (
UPDATE analytics_state SET revision = revision + 1
WHERE (SELECT content_hash FROM prev) IS DISTINCT FROM $2::text
RETURNING revision
)
SELECT EXISTS (SELECT 1 FROM rev)::boolean AS changed
`
type FinishRollupHourParams struct {
Hour pgtype.Timestamptz `json:"hour"`
ContentHash string `json:"content_hash"`
}
// Marks the hour complete and bumps the revision only when its content changed.
func (q *Queries) FinishRollupHour(ctx context.Context, arg FinishRollupHourParams) (bool, error) {
row := q.db.QueryRow(ctx, finishRollupHour, arg.Hour, arg.ContentHash)
var changed bool
err := row.Scan(&changed)
return changed, err
}
const getAnalyticsRevision = `-- name: GetAnalyticsRevision :one
SELECT (s.revision + r.coverage)::bigint FROM analytics_state s CROSS JOIN analytics_raw_state r
`
// Both counters only grow, so their sum changes whenever rolled content or coverage does.
func (q *Queries) GetAnalyticsRevision(ctx context.Context) (int64, error) {
row := q.db.QueryRow(ctx, getAnalyticsRevision)
var column_1 int64
err := row.Scan(&column_1)
return column_1, err
}
const listDirtyRollupHours = `-- name: ListDirtyRollupHours :many
SELECT d.hour FROM analytics_dirty_hours d
JOIN analytics_rollup_hours h ON h.hour = d.hour AND h.status = 'complete'
CROSS JOIN analytics_raw_state r
WHERE r.raw_deleted_before IS NULL OR d.hour > r.raw_deleted_before + INTERVAL '30 minutes'
ORDER BY d.hour
LIMIT $1
`
// Only complete hours whose raw rows are still intact can be re-rolled. Queued missing hours
// wait: their first roll consumes the entry only if the entry predates its snapshot.
func (q *Queries) ListDirtyRollupHours(ctx context.Context, limit int32) ([]pgtype.Timestamptz, error) {
rows, err := q.db.Query(ctx, listDirtyRollupHours, limit)
if err != nil {
return nil, err
}
defer rows.Close()
items := []pgtype.Timestamptz{}
for rows.Next() {
var hour pgtype.Timestamptz
if err := rows.Scan(&hour); err != nil {
return nil, err
}
items = append(items, hour)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listMissingRollupHours = `-- name: ListMissingRollupHours :many
SELECT hour FROM analytics_rollup_hours
WHERE status = 'missing'
ORDER BY hour
LIMIT $1
`
func (q *Queries) ListMissingRollupHours(ctx context.Context, limit int32) ([]pgtype.Timestamptz, error) {
rows, err := q.db.Query(ctx, listMissingRollupHours, limit)
if err != nil {
return nil, err
}
defer rows.Close()
items := []pgtype.Timestamptz{}
for rows.Next() {
var hour pgtype.Timestamptz
if err := rows.Scan(&hour); err != nil {
return nil, err
}
items = append(items, hour)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const lockRollupHour = `-- name: LockRollupHour :one
SELECT CASE
WHEN r.raw_deleted_before IS NOT NULL AND h.hour <= r.raw_deleted_before + INTERVAL '30 minutes'
THEN 'partial'
ELSE h.status
END::text AS status
FROM analytics_rollup_hours h CROSS JOIN analytics_raw_state r
WHERE h.hour = $1
FOR UPDATE OF h
`
// Returns the hour's status, or 'partial' if raw deletion has reached it since registration.
func (q *Queries) LockRollupHour(ctx context.Context, hour pgtype.Timestamptz) (string, error) {
row := q.db.QueryRow(ctx, lockRollupHour, hour)
var status string
err := row.Scan(&status)
return status, err
}
const markPartialRollupHours = `-- name: MarkPartialRollupHours :exec
WITH marked AS (
UPDATE analytics_rollup_hours h SET status = 'partial'
FROM analytics_raw_state r
WHERE h.status = 'missing'
AND r.raw_deleted_before IS NOT NULL
AND h.hour <= r.raw_deleted_before + INTERVAL '30 minutes'
RETURNING 1
)
UPDATE analytics_state SET revision = revision + 1 WHERE EXISTS (SELECT 1 FROM marked)
`
// Hours whose raw rows cleanup already started deleting can never be rolled completely.
// Readers report the new status, so the revision moves too.
func (q *Queries) MarkPartialRollupHours(ctx context.Context) error {
_, err := q.db.Exec(ctx, markPartialRollupHours)
return err
}
const oldestMissingRollupHour = `-- name: OldestMissingRollupHour :one
SELECT min(hour)::timestamptz FROM analytics_rollup_hours WHERE status = 'missing'
`
func (q *Queries) OldestMissingRollupHour(ctx context.Context) (pgtype.Timestamptz, error) {
row := q.db.QueryRow(ctx, oldestMissingRollupHour)
var column_1 pgtype.Timestamptz
err := row.Scan(&column_1)
return column_1, err
}
const registerRollupHours = `-- name: RegisterRollupHours :exec
INSERT INTO analytics_rollup_hours (hour, status)
SELECT h, 'missing'
FROM (SELECT CASE WHEN MIN(heard_at) IS NOT NULL
THEN date_trunc('hour', GREATEST(MIN(heard_at), $1::timestamptz), 'UTC') END AS first_hour
FROM packet_observations) f,
generate_series(f.first_hour, date_trunc('hour', now() - INTERVAL '95 minutes', 'UTC'), INTERVAL '1 hour') h
WHERE f.first_hour IS NOT NULL
ON CONFLICT (hour) DO NOTHING
`
// Adds a 'missing' row for every eligible hour since the earliest retained observation
// (and not before @since), zero-observation hours included.
// No observations yet means nothing to register (GREATEST would ignore the NULL MIN).
func (q *Queries) RegisterRollupHours(ctx context.Context, since pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, registerRollupHours, since)
return err
}
const rollAdvertHearings = `-- name: RollAdvertHearings :exec
INSERT INTO analytics_hourly_advert_hearings (hour, iata, origin_pubkey, observations, last_heard, name, node_type)
SELECT $1::timestamptz, r.iata, r.origin_pubkey, r.n, r.last_heard, n.name, n.node_type
FROM (SELECT iata, origin_pubkey, count(*) AS n, max(heard_at) AS last_heard
FROM rollup_obs
WHERE payload_type = 4 AND origin_pubkey IS NOT NULL
GROUP BY iata, origin_pubkey) r
LEFT JOIN nodes n ON n.public_key = r.origin_pubkey
`
func (q *Queries) RollAdvertHearings(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollAdvertHearings, hour)
return err
}
const rollAdvertSets = `-- name: RollAdvertSets :exec
INSERT INTO analytics_hourly_advert_sets (hour, origin_pubkey, iatas, advert_packets, flood_packets, direct_packets)
SELECT $1::timestamptz, origin_pubkey, iatas, count(*),
count(*) FILTER (WHERE route_type IN (0, 1)),
count(*) FILTER (WHERE route_type IN (2, 3))
FROM (SELECT origin_pubkey, route_type, array_agg(DISTINCT iata ORDER BY iata) AS iatas
FROM rollup_obs
WHERE payload_type = 4 AND origin_pubkey IS NOT NULL
GROUP BY packet_hash, origin_pubkey, route_type) p
GROUP BY origin_pubkey, iatas
`
// Each distinct ADVERT packet counts once, under the exact set of IATAs that heard it this hour.
func (q *Queries) RollAdvertSets(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollAdvertSets, hour)
return err
}
const rollIATAObservations = `-- name: RollIATAObservations :exec
INSERT INTO analytics_hourly_iata_observations (hour, iata, observation_count)
SELECT $1::timestamptz, iata, count(*) FROM rollup_obs GROUP BY iata
`
func (q *Queries) RollIATAObservations(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollIATAObservations, hour)
return err
}
const rollObserverActivity = `-- name: RollObserverActivity :exec
INSERT INTO analytics_hourly_observer_activity (hour, observer_id, payload_type, observations, airtime_ms, airtime_n,
snr_sum, snr_n, snr_min, rssi_sum, rssi_n)
SELECT $1::timestamptz, observer_id, COALESCE(payload_type, -1)::smallint,
count(*), sum(airtime_ms)::real, count(airtime_ms),
sum(snr) FILTER (WHERE NOT (COALESCE(rssi, 0) = 0 AND COALESCE(snr, 0) = 0))::real,
count(snr) FILTER (WHERE NOT (COALESCE(rssi, 0) = 0 AND COALESCE(snr, 0) = 0)),
min(snr) FILTER (WHERE NOT (COALESCE(rssi, 0) = 0 AND COALESCE(snr, 0) = 0))::real,
sum(rssi) FILTER (WHERE NOT (COALESCE(rssi, 0) = 0 AND COALESCE(snr, 0) = 0))::bigint,
count(rssi) FILTER (WHERE NOT (COALESCE(rssi, 0) = 0 AND COALESCE(snr, 0) = 0))
FROM rollup_obs
GROUP BY observer_id, COALESCE(payload_type, -1)
`
func (q *Queries) RollObserverActivity(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollObserverActivity, hour)
return err
}
const rollObserverIdentity = `-- name: RollObserverIdentity :exec
INSERT INTO analytics_hourly_observer_identity (hour, iata, observer_id, observation_count, display_name, observer_type)
SELECT $1::timestamptz, r.iata, r.observer_id, r.n, o.display_name, o.observer_type
FROM (SELECT iata, observer_id, count(*) AS n FROM rollup_obs GROUP BY iata, observer_id) r
LEFT JOIN observers o ON o.id = r.observer_id
`
func (q *Queries) RollObserverIdentity(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollObserverIdentity, hour)
return err
}
const rollPacketSets = `-- name: RollPacketSets :exec
INSERT INTO analytics_hourly_packet_sets (hour, iatas, packets)
SELECT $1::timestamptz, iatas, count(*)
FROM (SELECT array_agg(DISTINCT iata ORDER BY iata) AS iatas FROM rollup_obs GROUP BY packet_hash) p
GROUP BY iatas
`
func (q *Queries) RollPacketSets(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollPacketSets, hour)
return err
}
const rollPaths = `-- name: RollPaths :exec
INSERT INTO analytics_hourly_paths (hour, iata, category, hash_bytes, entries, receptions)
SELECT $1::timestamptz, c.iata, c.category, c.hash_bytes, c.entries, count(*)
FROM (
SELECT k.iata, k.category,
CASE WHEN k.category = 0 THEN k.hash_size ELSE 0 END::integer AS hash_bytes,
CASE WHEN k.category = 0 THEN k.hop_count ELSE 0 END::integer AS entries
FROM (
SELECT iata, hash_size, hop_count,
CASE WHEN payload_type = 9 THEN 2
WHEN payload_type IS NULL OR payload_type NOT BETWEEN 0 AND 15
OR NOT (path_length_byte BETWEEN 0 AND 191
AND hash_size BETWEEN 1 AND 3 AND hop_count BETWEEN 0 AND 63
AND hash_size = (path_length_byte >> 6) + 1
AND hop_count = (path_length_byte & 63)
AND hash_size::integer * hop_count::integer <= 64
AND path_len = hash_size::integer * hop_count::integer)
THEN 3
WHEN hop_count = 0 THEN 1
ELSE 0 END::integer AS category
FROM rollup_obs
) k
) c
GROUP BY c.iata, c.category, c.hash_bytes, c.entries
`
// category: 0 = routed, 1 = zero-hop, 2 = TRACE, 3 = invalid (meshcore-go IsValidPathLen).
func (q *Queries) RollPaths(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollPaths, hour)
return err
}
const rollPayloadBreakdown = `-- name: RollPayloadBreakdown :exec
INSERT INTO analytics_hourly_payload_breakdown (hour, iata, payload_type, count)
SELECT $1::timestamptz, iata, payload_type, count(*)
FROM rollup_obs WHERE payload_type IS NOT NULL
GROUP BY iata, payload_type
`
func (q *Queries) RollPayloadBreakdown(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollPayloadBreakdown, hour)
return err
}
const rollScopeSets = `-- name: RollScopeSets :exec
INSERT INTO analytics_hourly_scope_sets (hour, iatas, scope_id, packets)
SELECT $1::timestamptz, iatas, scope_id, count(*)
FROM (SELECT scope_id, array_agg(DISTINCT iata ORDER BY iata) AS iatas
FROM rollup_obs WHERE scope_id IS NOT NULL
GROUP BY packet_hash, scope_id) p
GROUP BY iatas, scope_id
`
func (q *Queries) RollScopeSets(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollScopeSets, hour)
return err
}
const rollSignal = `-- name: RollSignal :exec
INSERT INTO analytics_hourly_signal (hour, iata, kind, snr_bin, rssi_bin, receptions, snr_samples, snr_sum, rssi_samples, rssi_sum)
SELECT $1::timestamptz, b.iata, grouping(b.snr_bin, b.rssi_bin)::integer,
COALESCE(b.snr_bin, -1)::integer, COALESCE(b.rssi_bin, -1)::integer,
count(*), count(b.snr), COALESCE(sum(b.snr), 0)::double precision,
count(b.rssi), COALESCE(sum(b.rssi), 0)::double precision
FROM (
SELECT s.iata, s.snr, s.rssi,
width_bucket(s.snr, -30, 30, 12) AS snr_bin,
width_bucket(s.rssi, -140, 0, 14) AS rssi_bin
FROM (
SELECT ro.iata,
CASE WHEN NOT (COALESCE(ro.rssi, 0) = 0 AND COALESCE(ro.snr, 0) = 0)
AND ro.snr > '-Infinity'::real AND ro.snr < 'Infinity'::real
THEN ro.snr::double precision END AS snr,
CASE WHEN NOT (COALESCE(ro.rssi, 0) = 0 AND COALESCE(ro.snr, 0) = 0)
THEN ro.rssi::double precision END AS rssi
FROM rollup_obs ro
) s
) b
GROUP BY GROUPING SETS ((b.iata), (b.iata, b.snr_bin), (b.iata, b.rssi_bin))
`
// kind = grouping(snr_bin, rssi_bin): 3 = per IATA totals, 1 = per SNR bin, 2 = per RSSI bin.
// rssi=0 AND snr=0 means "not reported".
func (q *Queries) RollSignal(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollSignal, hour)
return err
}
const rollTalkerSets = `-- name: RollTalkerSets :exec
INSERT INTO analytics_hourly_talker_sets (hour, sender_name, iatas, messages, last_sent)
SELECT $1::timestamptz, sender_name, iatas, count(*), max(sent_at)
FROM (SELECT cm.sender_name, max(cm.sent_at) AS sent_at, array_agg(DISTINCT ro.iata ORDER BY ro.iata) AS iatas
FROM rollup_obs ro
JOIN channel_messages cm ON cm.packet_hash = ro.packet_hash
WHERE cm.sender_name IS NOT NULL
GROUP BY cm.id, cm.sender_name) m
GROUP BY sender_name, iatas
`
func (q *Queries) RollTalkerSets(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, rollTalkerSets, hour)
return err
}
const rollupContentHash = `-- name: RollupContentHash :one
SELECT md5(concat_ws('|',
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_iata_observations t WHERE t.hour = $1::timestamptz),
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_payload_breakdown t WHERE t.hour = $1::timestamptz),
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_signal t WHERE t.hour = $1::timestamptz),
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_paths t WHERE t.hour = $1::timestamptz),
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_observer_activity t WHERE t.hour = $1::timestamptz),
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_observer_identity t WHERE t.hour = $1::timestamptz),
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_advert_hearings t WHERE t.hour = $1::timestamptz),
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_advert_sets t WHERE t.hour = $1::timestamptz),
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_talker_sets t WHERE t.hour = $1::timestamptz),
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_packet_sets t WHERE t.hour = $1::timestamptz),
(SELECT string_agg(t::text, ',' ORDER BY t::text) FROM analytics_hourly_scope_sets t WHERE t.hour = $1::timestamptz)
))::text AS content_hash
`
// Order-independent fingerprint of every family row for the hour.
func (q *Queries) RollupContentHash(ctx context.Context, hour pgtype.Timestamptz) (string, error) {
row := q.db.QueryRow(ctx, rollupContentHash, hour)
var content_hash string
err := row.Scan(&content_hash)
return content_hash, err
}
const rollupUnlock = `-- name: RollupUnlock :exec
SELECT pg_advisory_unlock(25189, 1)
`
func (q *Queries) RollupUnlock(ctx context.Context) error {
_, err := q.db.Exec(ctx, rollupUnlock)
return err
}
const setRollupHourPartial = `-- name: SetRollupHourPartial :exec
WITH marked AS (
UPDATE analytics_rollup_hours SET status = 'partial' WHERE hour = $1 AND status = 'missing' RETURNING 1
)
UPDATE analytics_state SET revision = revision + 1 WHERE EXISTS (SELECT 1 FROM marked)
`
func (q *Queries) SetRollupHourPartial(ctx context.Context, hour pgtype.Timestamptz) error {
_, err := q.db.Exec(ctx, setRollupHourPartial, hour)
return err
}
const tryRollupLock = `-- name: TryRollupLock :one
SELECT pg_try_advisory_lock(25189, 1)
`
// ============================================================
// HOURLY ANALYTICS ROLLUPS
// An hour [H, H+1h) is eligible once now() >= H + 95 min (ingest clamps heard_at to ±30 min).
// ============================================================
func (q *Queries) TryRollupLock(ctx context.Context) (bool, error) {
row := q.db.QueryRow(ctx, tryRollupLock)
var pg_try_advisory_lock bool
err := row.Scan(&pg_try_advisory_lock)
return pg_try_advisory_lock, err
}
+169
View File
@@ -0,0 +1,169 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: series.sql
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const getEarliestCompleteRollupHour = `-- name: GetEarliestCompleteRollupHour :one
SELECT min(hour)::timestamptz FROM analytics_rollup_hours WHERE status = 'complete'
`
func (q *Queries) GetEarliestCompleteRollupHour(ctx context.Context) (pgtype.Timestamptz, error) {
row := q.db.QueryRow(ctx, getEarliestCompleteRollupHour)
var column_1 pgtype.Timestamptz
err := row.Scan(&column_1)
return column_1, err
}
const getStatsSeries = `-- name: GetStatsSeries :many
WITH hours AS (
SELECT g.h::timestamptz AS hour, COALESCE(r.status, 'missing')::text AS status
FROM generate_series($1::timestamptz, $2::timestamptz, INTERVAL '1 hour') g(h)
LEFT JOIN analytics_rollup_hours r ON r.hour = g.h
WHERE g.h < $2::timestamptz
), complete AS (
SELECT hour FROM hours WHERE status = 'complete'
), obs AS (
SELECT o.hour, o.iata, o.observation_count FROM analytics_hourly_iata_observations o JOIN complete c ON c.hour = o.hour
WHERE COALESCE(cardinality($3::bpchar[]), 0) = 0
UNION ALL
SELECT o.hour, o.iata, o.observation_count FROM analytics_hourly_iata_observations o JOIN complete c ON c.hour = o.hour
WHERE cardinality($3::bpchar[]) > 0 AND o.iata = ANY($3::bpchar[])
), obs_agg AS (
SELECT hour, sum(observation_count)::bigint AS observations, count(DISTINCT iata)::bigint AS active_iatas
FROM obs GROUP BY GROUPING SETS ((), (hour))
), pkt AS (
SELECT s.hour, s.packets FROM analytics_hourly_packet_sets s JOIN complete c ON c.hour = s.hour
WHERE COALESCE(cardinality($3::bpchar[]), 0) = 0
UNION ALL
SELECT s.hour, s.packets FROM analytics_hourly_packet_sets s JOIN complete c ON c.hour = s.hour
WHERE cardinality($3::bpchar[]) > 0 AND s.iatas && $3::bpchar[]
), pkt_agg AS (
SELECT hour, sum(packets)::bigint AS unique_packets FROM pkt GROUP BY GROUPING SETS ((), (hour))
), obsr AS (
SELECT i.hour, i.observer_id FROM analytics_hourly_observer_identity i JOIN complete c ON c.hour = i.hour
WHERE COALESCE(cardinality($3::bpchar[]), 0) = 0
UNION ALL
SELECT i.hour, i.observer_id FROM analytics_hourly_observer_identity i JOIN complete c ON c.hour = i.hour
WHERE cardinality($3::bpchar[]) > 0 AND i.iata = ANY($3::bpchar[])
), obsr_agg AS (
SELECT hour, count(DISTINCT observer_id)::bigint AS active_observers FROM obsr GROUP BY GROUPING SETS ((), (hour))
), scp AS (
SELECT s.hour, s.scope_id, s.packets FROM analytics_hourly_scope_sets s JOIN complete c ON c.hour = s.hour
WHERE COALESCE(cardinality($3::bpchar[]), 0) = 0
UNION ALL
SELECT s.hour, s.scope_id, s.packets FROM analytics_hourly_scope_sets s JOIN complete c ON c.hour = s.hour
WHERE cardinality($3::bpchar[]) > 0 AND s.iatas && $3::bpchar[]
), scp_agg AS (
SELECT hour, sum(packets)::bigint AS scoped_packets, count(DISTINCT scope_id)::bigint AS active_scopes
FROM scp GROUP BY GROUPING SETS ((), (hour))
), pth AS (
SELECT p.hour, p.entries FROM analytics_hourly_paths p JOIN complete c ON c.hour = p.hour
WHERE p.category = 0 AND COALESCE(cardinality($3::bpchar[]), 0) = 0
UNION ALL
SELECT p.hour, p.entries FROM analytics_hourly_paths p JOIN complete c ON c.hour = p.hour
WHERE p.category = 0 AND cardinality($3::bpchar[]) > 0 AND p.iata = ANY($3::bpchar[])
), pth_agg AS (
SELECT hour, max(entries)::integer AS max_path_entries FROM pth GROUP BY GROUPING SETS ((), (hour))
), sig AS (
SELECT s.hour, s.snr_sum, s.snr_samples, s.rssi_sum, s.rssi_samples FROM analytics_hourly_signal s JOIN complete c ON c.hour = s.hour
WHERE s.kind = 3 AND COALESCE(cardinality($3::bpchar[]), 0) = 0
UNION ALL
SELECT s.hour, s.snr_sum, s.snr_samples, s.rssi_sum, s.rssi_samples FROM analytics_hourly_signal s JOIN complete c ON c.hour = s.hour
WHERE s.kind = 3 AND cardinality($3::bpchar[]) > 0 AND s.iata = ANY($3::bpchar[])
), sig_agg AS (
SELECT hour, sum(snr_sum)::double precision AS snr_sum, sum(snr_samples)::bigint AS snr_samples,
sum(rssi_sum)::double precision AS rssi_sum, sum(rssi_samples)::bigint AS rssi_samples
FROM sig GROUP BY GROUPING SETS ((), (hour))
), keys AS (
SELECT hour, status FROM hours
UNION ALL
SELECT NULL::timestamptz, 'summary'::text
)
SELECT k.hour, k.status,
COALESCE(o.observations, 0)::bigint AS observations,
COALESCE(p.unique_packets, 0)::bigint AS unique_packets,
COALESCE(ob.active_observers, 0)::bigint AS active_observers,
COALESCE(o.active_iatas, 0)::bigint AS active_iatas,
COALESCE(s.scoped_packets, 0)::bigint AS scoped_packets,
COALESCE(s.active_scopes, 0)::bigint AS active_scopes,
COALESCE(pa.max_path_entries, 0)::integer AS max_path_entries,
COALESCE(sg.snr_sum, 0)::double precision AS snr_sum,
COALESCE(sg.snr_samples, 0)::bigint AS snr_samples,
COALESCE(sg.rssi_sum, 0)::double precision AS rssi_sum,
COALESCE(sg.rssi_samples, 0)::bigint AS rssi_samples
FROM keys k
LEFT JOIN obs_agg o ON o.hour IS NOT DISTINCT FROM k.hour
LEFT JOIN pkt_agg p ON p.hour IS NOT DISTINCT FROM k.hour
LEFT JOIN obsr_agg ob ON ob.hour IS NOT DISTINCT FROM k.hour
LEFT JOIN scp_agg s ON s.hour IS NOT DISTINCT FROM k.hour
LEFT JOIN pth_agg pa ON pa.hour IS NOT DISTINCT FROM k.hour
LEFT JOIN sig_agg sg ON sg.hour IS NOT DISTINCT FROM k.hour
ORDER BY k.hour NULLS LAST
`
type GetStatsSeriesParams struct {
Since pgtype.Timestamptz `json:"since"`
Until pgtype.Timestamptz `json:"until"`
Iatas []string `json:"iatas"`
}
type GetStatsSeriesRow struct {
Hour pgtype.Timestamptz `json:"hour"`
Status string `json:"status"`
Observations int64 `json:"observations"`
UniquePackets int64 `json:"unique_packets"`
ActiveObservers int64 `json:"active_observers"`
ActiveIatas int64 `json:"active_iatas"`
ScopedPackets int64 `json:"scoped_packets"`
ActiveScopes int64 `json:"active_scopes"`
MaxPathEntries int32 `json:"max_path_entries"`
SnrSum float64 `json:"snr_sum"`
SnrSamples int64 `json:"snr_samples"`
RssiSum float64 `json:"rssi_sum"`
RssiSamples int64 `json:"rssi_samples"`
}
// One row per hour in [since, until) plus a summary row (hour NULL) over its complete hours.
// Counts sum; observers, IATAs and scopes are distinct across the window; packets count
// once per hour heard. Each branch pair keeps "all IATAs" and "these IATAs" plans separate.
func (q *Queries) GetStatsSeries(ctx context.Context, arg GetStatsSeriesParams) ([]GetStatsSeriesRow, error) {
rows, err := q.db.Query(ctx, getStatsSeries, arg.Since, arg.Until, arg.Iatas)
if err != nil {
return nil, err
}
defer rows.Close()
items := []GetStatsSeriesRow{}
for rows.Next() {
var i GetStatsSeriesRow
if err := rows.Scan(
&i.Hour,
&i.Status,
&i.Observations,
&i.UniquePackets,
&i.ActiveObservers,
&i.ActiveIatas,
&i.ScopedPackets,
&i.ActiveScopes,
&i.MaxPathEntries,
&i.SnrSum,
&i.SnrSamples,
&i.RssiSum,
&i.RssiSamples,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
+3 -12
View File
@@ -13,11 +13,11 @@ import (
const getSignalStats = `-- name: GetSignalStats :many
WITH readings AS (
SELECT iata, hour, kind, snr_bin, rssi_bin, receptions, snr_samples, snr_sum, rssi_samples, rssi_sum FROM mv_signal_stats_hourly
SELECT iata, hour, kind, snr_bin, rssi_bin, receptions, snr_samples, snr_sum, rssi_samples, rssi_sum FROM analytics_hourly_signal
WHERE hour >= $1::timestamptz AND hour < $2::timestamptz
AND COALESCE(cardinality($3::bpchar[]), 0) = 0
UNION ALL
SELECT iata, hour, kind, snr_bin, rssi_bin, receptions, snr_samples, snr_sum, rssi_samples, rssi_sum FROM mv_signal_stats_hourly
SELECT iata, hour, kind, snr_bin, rssi_bin, receptions, snr_samples, snr_sum, rssi_samples, rssi_sum FROM analytics_hourly_signal
WHERE hour >= $1::timestamptz AND hour < $2::timestamptz
AND cardinality($3::bpchar[]) > 0 AND iata = ANY($3::bpchar[])
)
@@ -57,7 +57,7 @@ type GetSignalStatsRow struct {
RssiAverage float64 `json:"rssi_average"`
}
// Read compact hourly snapshots, never observations on an HTTP request.
// Read the hourly rollup, never observations on an HTTP request.
// Weight averages by sample counts instead of averaging regional/hourly means.
func (q *Queries) GetSignalStats(ctx context.Context, arg GetSignalStatsParams) ([]GetSignalStatsRow, error) {
rows, err := q.db.Query(ctx, getSignalStats, arg.Since, arg.Until, arg.Iatas)
@@ -88,12 +88,3 @@ func (q *Queries) GetSignalStats(ctx context.Context, arg GetSignalStatsParams)
}
return items, nil
}
const refreshSignalStats = `-- name: RefreshSignalStats :exec
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_signal_stats_hourly
`
func (q *Queries) RefreshSignalStats(ctx context.Context) error {
_, err := q.db.Exec(ctx, refreshSignalStats)
return err
}