mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-23 01:51:27 +00:00
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:
1 parent
6cd03e73da
commit
98006a9364
112 files changed
+6766
-2828
No files matched your search
+446
-172
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user