mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-15 22:39:56 +00:00
release: v2.0.4 (#217)
* perf(routes): batch cross-IATA and hash route search (#216) One search fired a /routes/cross request per IATA pair (138k on 2026-10-08). Route search now takes an optional iatas list and runs a fixed number of index-backed queries. /info advertises built-in minimum client versions. * chore: bump version to 2.0.4
This commit is contained in:
+89
-29
@@ -432,21 +432,6 @@ func (mr *MockQuerierMockRecorder) GetChannelByID(ctx, id any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChannelByID", reflect.TypeOf((*MockQuerier)(nil).GetChannelByID), ctx, id)
|
||||
}
|
||||
|
||||
// GetCrossIATANeighbors mocks base method.
|
||||
func (m *MockQuerier) GetCrossIATANeighbors(ctx context.Context, arg db.GetCrossIATANeighborsParams) ([]db.GetCrossIATANeighborsRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetCrossIATANeighbors", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.GetCrossIATANeighborsRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetCrossIATANeighbors indicates an expected call of GetCrossIATANeighbors.
|
||||
func (mr *MockQuerierMockRecorder) GetCrossIATANeighbors(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
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()
|
||||
@@ -507,19 +492,19 @@ func (mr *MockQuerierMockRecorder) GetIATABorder(ctx, iata any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIATABorder", reflect.TypeOf((*MockQuerier)(nil).GetIATABorder), ctx, iata)
|
||||
}
|
||||
|
||||
// GetKnownRoutesByNode mocks base method.
|
||||
func (m *MockQuerier) GetKnownRoutesByNode(ctx context.Context, arg db.GetKnownRoutesByNodeParams) ([]db.GetKnownRoutesByNodeRow, error) {
|
||||
// GetKnownRoutesByNodes mocks base method.
|
||||
func (m *MockQuerier) GetKnownRoutesByNodes(ctx context.Context, arg db.GetKnownRoutesByNodesParams) ([]db.GetKnownRoutesByNodesRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetKnownRoutesByNode", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.GetKnownRoutesByNodeRow)
|
||||
ret := m.ctrl.Call(m, "GetKnownRoutesByNodes", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.GetKnownRoutesByNodesRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetKnownRoutesByNode indicates an expected call of GetKnownRoutesByNode.
|
||||
func (mr *MockQuerierMockRecorder) GetKnownRoutesByNode(ctx, arg any) *gomock.Call {
|
||||
// GetKnownRoutesByNodes indicates an expected call of GetKnownRoutesByNodes.
|
||||
func (mr *MockQuerierMockRecorder) GetKnownRoutesByNodes(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKnownRoutesByNode", reflect.TypeOf((*MockQuerier)(nil).GetKnownRoutesByNode), ctx, arg)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKnownRoutesByNodes", reflect.TypeOf((*MockQuerier)(nil).GetKnownRoutesByNodes), ctx, arg)
|
||||
}
|
||||
|
||||
// GetLatestCompleteRollupHour mocks base method.
|
||||
@@ -537,6 +522,21 @@ func (mr *MockQuerierMockRecorder) GetLatestCompleteRollupHour(ctx any) *gomock.
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestCompleteRollupHour", reflect.TypeOf((*MockQuerier)(nil).GetLatestCompleteRollupHour), ctx)
|
||||
}
|
||||
|
||||
// GetNeighborLinks mocks base method.
|
||||
func (m *MockQuerier) GetNeighborLinks(ctx context.Context, arg db.GetNeighborLinksParams) ([]db.GetNeighborLinksRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetNeighborLinks", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.GetNeighborLinksRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetNeighborLinks indicates an expected call of GetNeighborLinks.
|
||||
func (mr *MockQuerierMockRecorder) GetNeighborLinks(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNeighborLinks", reflect.TypeOf((*MockQuerier)(nil).GetNeighborLinks), ctx, arg)
|
||||
}
|
||||
|
||||
// GetNodeByID mocks base method.
|
||||
func (m *MockQuerier) GetNodeByID(ctx context.Context, id uuid.UUID) (db.GetNodeByIDRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1911,6 +1911,66 @@ func (mr *MockQuerierMockRecorder) ResolvePathHashesP4(ctx, arg any) *gomock.Cal
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolvePathHashesP4", reflect.TypeOf((*MockQuerier)(nil).ResolvePathHashesP4), ctx, arg)
|
||||
}
|
||||
|
||||
// ResolveRelayHashPairsP1 mocks base method.
|
||||
func (m *MockQuerier) ResolveRelayHashPairsP1(ctx context.Context, arg db.ResolveRelayHashPairsP1Params) ([]db.ResolveRelayHashPairsP1Row, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ResolveRelayHashPairsP1", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.ResolveRelayHashPairsP1Row)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ResolveRelayHashPairsP1 indicates an expected call of ResolveRelayHashPairsP1.
|
||||
func (mr *MockQuerierMockRecorder) ResolveRelayHashPairsP1(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveRelayHashPairsP1", reflect.TypeOf((*MockQuerier)(nil).ResolveRelayHashPairsP1), ctx, arg)
|
||||
}
|
||||
|
||||
// ResolveRelayHashPairsP2 mocks base method.
|
||||
func (m *MockQuerier) ResolveRelayHashPairsP2(ctx context.Context, arg db.ResolveRelayHashPairsP2Params) ([]db.ResolveRelayHashPairsP2Row, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ResolveRelayHashPairsP2", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.ResolveRelayHashPairsP2Row)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ResolveRelayHashPairsP2 indicates an expected call of ResolveRelayHashPairsP2.
|
||||
func (mr *MockQuerierMockRecorder) ResolveRelayHashPairsP2(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveRelayHashPairsP2", reflect.TypeOf((*MockQuerier)(nil).ResolveRelayHashPairsP2), ctx, arg)
|
||||
}
|
||||
|
||||
// ResolveRelayHashPairsP3 mocks base method.
|
||||
func (m *MockQuerier) ResolveRelayHashPairsP3(ctx context.Context, arg db.ResolveRelayHashPairsP3Params) ([]db.ResolveRelayHashPairsP3Row, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ResolveRelayHashPairsP3", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.ResolveRelayHashPairsP3Row)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ResolveRelayHashPairsP3 indicates an expected call of ResolveRelayHashPairsP3.
|
||||
func (mr *MockQuerierMockRecorder) ResolveRelayHashPairsP3(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveRelayHashPairsP3", reflect.TypeOf((*MockQuerier)(nil).ResolveRelayHashPairsP3), ctx, arg)
|
||||
}
|
||||
|
||||
// ResolveRelayHashPairsP4 mocks base method.
|
||||
func (m *MockQuerier) ResolveRelayHashPairsP4(ctx context.Context, arg db.ResolveRelayHashPairsP4Params) ([]db.ResolveRelayHashPairsP4Row, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ResolveRelayHashPairsP4", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.ResolveRelayHashPairsP4Row)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ResolveRelayHashPairsP4 indicates an expected call of ResolveRelayHashPairsP4.
|
||||
func (mr *MockQuerierMockRecorder) ResolveRelayHashPairsP4(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveRelayHashPairsP4", reflect.TypeOf((*MockQuerier)(nil).ResolveRelayHashPairsP4), ctx, arg)
|
||||
}
|
||||
|
||||
// RollAdvertHearings mocks base method.
|
||||
func (m *MockQuerier) RollAdvertHearings(ctx context.Context, hour pgtype.Timestamptz) error {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -2178,19 +2238,19 @@ func (mr *MockQuerierMockRecorder) SaveZoneList(ctx, arg any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveZoneList", reflect.TypeOf((*MockQuerier)(nil).SaveZoneList), ctx, arg)
|
||||
}
|
||||
|
||||
// SearchKnownRoutes mocks base method.
|
||||
func (m *MockQuerier) SearchKnownRoutes(ctx context.Context, arg db.SearchKnownRoutesParams) ([]db.SearchKnownRoutesRow, error) {
|
||||
// SearchKnownRoutesByNodes mocks base method.
|
||||
func (m *MockQuerier) SearchKnownRoutesByNodes(ctx context.Context, arg db.SearchKnownRoutesByNodesParams) ([]db.SearchKnownRoutesByNodesRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SearchKnownRoutes", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.SearchKnownRoutesRow)
|
||||
ret := m.ctrl.Call(m, "SearchKnownRoutesByNodes", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.SearchKnownRoutesByNodesRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SearchKnownRoutes indicates an expected call of SearchKnownRoutes.
|
||||
func (mr *MockQuerierMockRecorder) SearchKnownRoutes(ctx, arg any) *gomock.Call {
|
||||
// SearchKnownRoutesByNodes indicates an expected call of SearchKnownRoutesByNodes.
|
||||
func (mr *MockQuerierMockRecorder) SearchKnownRoutesByNodes(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchKnownRoutes", reflect.TypeOf((*MockQuerier)(nil).SearchKnownRoutes), ctx, arg)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchKnownRoutesByNodes", reflect.TypeOf((*MockQuerier)(nil).SearchKnownRoutesByNodes), ctx, arg)
|
||||
}
|
||||
|
||||
// SetNodeDefaultScope mocks base method.
|
||||
|
||||
+18
-6
@@ -64,8 +64,6 @@ type Querier interface {
|
||||
// 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)
|
||||
GetEarliestCompleteRollupHour(ctx context.Context) (pgtype.Timestamptz, error)
|
||||
// ============================================================
|
||||
// STATS
|
||||
@@ -76,8 +74,13 @@ type Querier interface {
|
||||
// when neither exists; a missing row (unknown IATA) is sql.ErrNoRows, same
|
||||
// not-found distinction GetIATA already makes.
|
||||
GetIATABorder(ctx context.Context, iata string) (json.RawMessage, error)
|
||||
GetKnownRoutesByNode(ctx context.Context, arg GetKnownRoutesByNodeParams) ([]GetKnownRoutesByNodeRow, error)
|
||||
// Routes containing @nodes[i] in IATA @iatas[i] (aligned, one node per IATA). The &&
|
||||
// drives the node_ids GIN index; the indexed check keeps LIMIT counting real matches.
|
||||
GetKnownRoutesByNodes(ctx context.Context, arg GetKnownRoutesByNodesParams) ([]GetKnownRoutesByNodesRow, error)
|
||||
GetLatestCompleteRollupHour(ctx context.Context) (pgtype.Timestamptz, error)
|
||||
// Neighbor rows between @a_nodes and @b_nodes in either orientation: ingest writes
|
||||
// adverts as (sender, forwarder) but traces as (receiver, sender). Callers normalise.
|
||||
GetNeighborLinks(ctx context.Context, arg GetNeighborLinksParams) ([]GetNeighborLinksRow, error)
|
||||
GetNodeByID(ctx context.Context, id uuid.UUID) (GetNodeByIDRow, error)
|
||||
GetNodeByPubkey(ctx context.Context, publicKey []byte) (uuid.UUID, error)
|
||||
// Returns the neighbors of a node with details, ordered by most recently seen.
|
||||
@@ -317,6 +320,14 @@ 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)
|
||||
// Batch form of ResolvePathHashesP1 across IATAs; callers check uniqueness per IATA.
|
||||
ResolveRelayHashPairsP1(ctx context.Context, arg ResolveRelayHashPairsP1Params) ([]ResolveRelayHashPairsP1Row, error)
|
||||
// Batch form of ResolvePathHashesP2 across IATAs; callers check uniqueness per IATA.
|
||||
ResolveRelayHashPairsP2(ctx context.Context, arg ResolveRelayHashPairsP2Params) ([]ResolveRelayHashPairsP2Row, error)
|
||||
// Batch form of ResolvePathHashesP3 across IATAs; callers check uniqueness per IATA.
|
||||
ResolveRelayHashPairsP3(ctx context.Context, arg ResolveRelayHashPairsP3Params) ([]ResolveRelayHashPairsP3Row, error)
|
||||
// Batch form of ResolvePathHashesP4 across IATAs; callers check uniqueness per IATA.
|
||||
ResolveRelayHashPairsP4(ctx context.Context, arg ResolveRelayHashPairsP4Params) ([]ResolveRelayHashPairsP4Row, 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
|
||||
@@ -347,9 +358,10 @@ type Querier interface {
|
||||
SaveZoneBoundary(ctx context.Context, arg SaveZoneBoundaryParams) error
|
||||
// NULL payload/etag/fetched_at retain the last good list after an error or 304.
|
||||
SaveZoneList(ctx context.Context, arg SaveZoneListParams) error
|
||||
// Returns known routes containing a subsequence from source to destination hash prefix.
|
||||
// Verifies source appears before destination in the route.
|
||||
SearchKnownRoutes(ctx context.Context, arg SearchKnownRoutesParams) ([]SearchKnownRoutesRow, error)
|
||||
// Routes holding a source and a destination candidate; callers check order and IATA.
|
||||
// A route's hops resolve in its own IATA, so candidates from other IATAs can't match.
|
||||
// Both && predicates use the node_ids GIN index.
|
||||
SearchKnownRoutesByNodes(ctx context.Context, arg SearchKnownRoutesByNodesParams) ([]SearchKnownRoutesByNodesRow, error)
|
||||
SetNodeDefaultScope(ctx context.Context, arg SetNodeDefaultScopeParams) error
|
||||
SetNodeMultibytePaths(ctx context.Context, id uuid.UUID) error
|
||||
SetNodeMultibyteTraces(ctx context.Context, id uuid.UUID) error
|
||||
|
||||
+245
-88
@@ -403,65 +403,6 @@ func (q *Queries) GetChannelByID(ctx context.Context, id int32) (Channel, error)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCrossIATANeighbors = `-- name: GetCrossIATANeighbors :many
|
||||
SELECT
|
||||
n.id, n.name, n.node_type, n.latitude, n.longitude,
|
||||
nn.iata AS neighbor_iata, nn.observation_count, nn.last_seen, nn.snr
|
||||
FROM node_neighbors nn
|
||||
JOIN nodes n ON n.id = nn.neighbor_id
|
||||
WHERE nn.node_id = $1
|
||||
AND nn.iata != $2
|
||||
ORDER BY nn.last_seen DESC
|
||||
`
|
||||
|
||||
type GetCrossIATANeighborsParams struct {
|
||||
NodeID uuid.UUID `json:"node_id"`
|
||||
Iata string `json:"iata"`
|
||||
}
|
||||
|
||||
type GetCrossIATANeighborsRow struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name *string `json:"name"`
|
||||
NodeType int16 `json:"node_type"`
|
||||
Latitude *float64 `json:"latitude"`
|
||||
Longitude *float64 `json:"longitude"`
|
||||
NeighborIata string `json:"neighbor_iata"`
|
||||
ObservationCount int64 `json:"observation_count"`
|
||||
LastSeen pgtype.Timestamptz `json:"last_seen"`
|
||||
Snr *float32 `json:"snr"`
|
||||
}
|
||||
|
||||
// Returns neighbors of a node that are in a different IATA.
|
||||
func (q *Queries) GetCrossIATANeighbors(ctx context.Context, arg GetCrossIATANeighborsParams) ([]GetCrossIATANeighborsRow, error) {
|
||||
rows, err := q.db.Query(ctx, getCrossIATANeighbors, arg.NodeID, arg.Iata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []GetCrossIATANeighborsRow{}
|
||||
for rows.Next() {
|
||||
var i GetCrossIATANeighborsRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.NodeType,
|
||||
&i.Latitude,
|
||||
&i.Longitude,
|
||||
&i.NeighborIata,
|
||||
&i.ObservationCount,
|
||||
&i.LastSeen,
|
||||
&i.Snr,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getHourlyStats = `-- name: GetHourlyStats :many
|
||||
|
||||
SELECT iata, hour, observation_count
|
||||
@@ -540,20 +481,23 @@ func (q *Queries) GetIATABorder(ctx context.Context, iata string) (json.RawMessa
|
||||
return border, err
|
||||
}
|
||||
|
||||
const getKnownRoutesByNode = `-- name: GetKnownRoutesByNode :many
|
||||
const getKnownRoutesByNodes = `-- name: GetKnownRoutesByNodes :many
|
||||
SELECT id, node_ids, hash_prefix, iata, hop_count, first_seen, last_seen, observation_count
|
||||
FROM known_routes
|
||||
WHERE iata = $1
|
||||
AND $2::uuid = ANY(node_ids)
|
||||
FROM known_routes kr
|
||||
WHERE kr.node_ids && $1::uuid[]
|
||||
AND kr.iata = ANY($2::bpchar[])
|
||||
AND ($1::uuid[])[array_position($2::bpchar[], kr.iata)] = ANY(kr.node_ids)
|
||||
ORDER BY hop_count ASC, last_seen DESC
|
||||
LIMIT $3
|
||||
`
|
||||
|
||||
type GetKnownRoutesByNodeParams struct {
|
||||
Iata string `json:"iata"`
|
||||
Column2 uuid.UUID `json:"column_2"`
|
||||
type GetKnownRoutesByNodesParams struct {
|
||||
Nodes []uuid.UUID `json:"nodes"`
|
||||
Iatas []string `json:"iatas"`
|
||||
RowLimit int32 `json:"row_limit"`
|
||||
}
|
||||
|
||||
type GetKnownRoutesByNodeRow struct {
|
||||
type GetKnownRoutesByNodesRow struct {
|
||||
ID int64 `json:"id"`
|
||||
NodeIds []uuid.UUID `json:"node_ids"`
|
||||
HashPrefix [][]byte `json:"hash_prefix"`
|
||||
@@ -564,15 +508,17 @@ type GetKnownRoutesByNodeRow struct {
|
||||
ObservationCount int64 `json:"observation_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetKnownRoutesByNode(ctx context.Context, arg GetKnownRoutesByNodeParams) ([]GetKnownRoutesByNodeRow, error) {
|
||||
rows, err := q.db.Query(ctx, getKnownRoutesByNode, arg.Iata, arg.Column2)
|
||||
// Routes containing @nodes[i] in IATA @iatas[i] (aligned, one node per IATA). The &&
|
||||
// drives the node_ids GIN index; the indexed check keeps LIMIT counting real matches.
|
||||
func (q *Queries) GetKnownRoutesByNodes(ctx context.Context, arg GetKnownRoutesByNodesParams) ([]GetKnownRoutesByNodesRow, error) {
|
||||
rows, err := q.db.Query(ctx, getKnownRoutesByNodes, arg.Nodes, arg.Iatas, arg.RowLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []GetKnownRoutesByNodeRow{}
|
||||
items := []GetKnownRoutesByNodesRow{}
|
||||
for rows.Next() {
|
||||
var i GetKnownRoutesByNodeRow
|
||||
var i GetKnownRoutesByNodesRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.NodeIds,
|
||||
@@ -593,6 +539,46 @@ func (q *Queries) GetKnownRoutesByNode(ctx context.Context, arg GetKnownRoutesBy
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getNeighborLinks = `-- name: GetNeighborLinks :many
|
||||
SELECT node_id, neighbor_id, last_seen
|
||||
FROM node_neighbors
|
||||
WHERE (node_id = ANY($1::uuid[]) AND neighbor_id = ANY($2::uuid[]))
|
||||
OR (node_id = ANY($2::uuid[]) AND neighbor_id = ANY($1::uuid[]))
|
||||
`
|
||||
|
||||
type GetNeighborLinksParams struct {
|
||||
ANodes []uuid.UUID `json:"a_nodes"`
|
||||
BNodes []uuid.UUID `json:"b_nodes"`
|
||||
}
|
||||
|
||||
type GetNeighborLinksRow struct {
|
||||
NodeID uuid.UUID `json:"node_id"`
|
||||
NeighborID uuid.UUID `json:"neighbor_id"`
|
||||
LastSeen pgtype.Timestamptz `json:"last_seen"`
|
||||
}
|
||||
|
||||
// Neighbor rows between @a_nodes and @b_nodes in either orientation: ingest writes
|
||||
// adverts as (sender, forwarder) but traces as (receiver, sender). Callers normalise.
|
||||
func (q *Queries) GetNeighborLinks(ctx context.Context, arg GetNeighborLinksParams) ([]GetNeighborLinksRow, error) {
|
||||
rows, err := q.db.Query(ctx, getNeighborLinks, arg.ANodes, arg.BNodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []GetNeighborLinksRow{}
|
||||
for rows.Next() {
|
||||
var i GetNeighborLinksRow
|
||||
if err := rows.Scan(&i.NodeID, &i.NeighborID, &i.LastSeen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getNodeByID = `-- name: GetNodeByID :one
|
||||
SELECT n.id, n.public_key, n.node_type, n.name, n.latitude, n.longitude, n.location_source, n.last_advert_at, n.supports_multibyte_paths, n.supports_multibyte_traces, n.default_scope_id, n.min_firmware_version, n.first_seen, n.last_seen, n.radio_freq_mhz, n.radio_sf, n.radio_bw_khz, n.metadata, n.device_clock_drift_seconds, ts.name AS default_scope_name,
|
||||
EXISTS (SELECT 1 FROM observers o WHERE o.public_key = n.public_key) AS is_observer,
|
||||
@@ -4856,6 +4842,170 @@ func (q *Queries) ResolvePathHashesP4(ctx context.Context, arg ResolvePathHashes
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const resolveRelayHashPairsP1 = `-- name: ResolveRelayHashPairsP1 :many
|
||||
SELECT ns.iata, ns.prefix_4 AS hash, ns.node_id
|
||||
FROM node_short_ids ns
|
||||
JOIN nodes n ON n.id = ns.node_id
|
||||
WHERE ns.iata = ANY($1::bpchar[])
|
||||
AND n.node_type IN (2, 3)
|
||||
AND ns.prefix_1 = ANY($2::bytea[])
|
||||
`
|
||||
|
||||
type ResolveRelayHashPairsP1Params struct {
|
||||
Iatas []string `json:"iatas"`
|
||||
Hashes [][]byte `json:"hashes"`
|
||||
}
|
||||
|
||||
type ResolveRelayHashPairsP1Row struct {
|
||||
Iata string `json:"iata"`
|
||||
Hash []byte `json:"hash"`
|
||||
NodeID uuid.UUID `json:"node_id"`
|
||||
}
|
||||
|
||||
// Batch form of ResolvePathHashesP1 across IATAs; callers check uniqueness per IATA.
|
||||
func (q *Queries) ResolveRelayHashPairsP1(ctx context.Context, arg ResolveRelayHashPairsP1Params) ([]ResolveRelayHashPairsP1Row, error) {
|
||||
rows, err := q.db.Query(ctx, resolveRelayHashPairsP1, arg.Iatas, arg.Hashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ResolveRelayHashPairsP1Row{}
|
||||
for rows.Next() {
|
||||
var i ResolveRelayHashPairsP1Row
|
||||
if err := rows.Scan(&i.Iata, &i.Hash, &i.NodeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const resolveRelayHashPairsP2 = `-- name: ResolveRelayHashPairsP2 :many
|
||||
SELECT ns.iata, ns.prefix_4 AS hash, ns.node_id
|
||||
FROM node_short_ids ns
|
||||
JOIN nodes n ON n.id = ns.node_id
|
||||
WHERE ns.iata = ANY($1::bpchar[])
|
||||
AND n.node_type IN (2, 3)
|
||||
AND ns.prefix_2 = ANY($2::bytea[])
|
||||
`
|
||||
|
||||
type ResolveRelayHashPairsP2Params struct {
|
||||
Iatas []string `json:"iatas"`
|
||||
Hashes [][]byte `json:"hashes"`
|
||||
}
|
||||
|
||||
type ResolveRelayHashPairsP2Row struct {
|
||||
Iata string `json:"iata"`
|
||||
Hash []byte `json:"hash"`
|
||||
NodeID uuid.UUID `json:"node_id"`
|
||||
}
|
||||
|
||||
// Batch form of ResolvePathHashesP2 across IATAs; callers check uniqueness per IATA.
|
||||
func (q *Queries) ResolveRelayHashPairsP2(ctx context.Context, arg ResolveRelayHashPairsP2Params) ([]ResolveRelayHashPairsP2Row, error) {
|
||||
rows, err := q.db.Query(ctx, resolveRelayHashPairsP2, arg.Iatas, arg.Hashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ResolveRelayHashPairsP2Row{}
|
||||
for rows.Next() {
|
||||
var i ResolveRelayHashPairsP2Row
|
||||
if err := rows.Scan(&i.Iata, &i.Hash, &i.NodeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const resolveRelayHashPairsP3 = `-- name: ResolveRelayHashPairsP3 :many
|
||||
SELECT ns.iata, ns.prefix_4 AS hash, ns.node_id
|
||||
FROM node_short_ids ns
|
||||
JOIN nodes n ON n.id = ns.node_id
|
||||
WHERE ns.iata = ANY($1::bpchar[])
|
||||
AND n.node_type IN (2, 3)
|
||||
AND ns.prefix_3 = ANY($2::bytea[])
|
||||
`
|
||||
|
||||
type ResolveRelayHashPairsP3Params struct {
|
||||
Iatas []string `json:"iatas"`
|
||||
Hashes [][]byte `json:"hashes"`
|
||||
}
|
||||
|
||||
type ResolveRelayHashPairsP3Row struct {
|
||||
Iata string `json:"iata"`
|
||||
Hash []byte `json:"hash"`
|
||||
NodeID uuid.UUID `json:"node_id"`
|
||||
}
|
||||
|
||||
// Batch form of ResolvePathHashesP3 across IATAs; callers check uniqueness per IATA.
|
||||
func (q *Queries) ResolveRelayHashPairsP3(ctx context.Context, arg ResolveRelayHashPairsP3Params) ([]ResolveRelayHashPairsP3Row, error) {
|
||||
rows, err := q.db.Query(ctx, resolveRelayHashPairsP3, arg.Iatas, arg.Hashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ResolveRelayHashPairsP3Row{}
|
||||
for rows.Next() {
|
||||
var i ResolveRelayHashPairsP3Row
|
||||
if err := rows.Scan(&i.Iata, &i.Hash, &i.NodeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const resolveRelayHashPairsP4 = `-- name: ResolveRelayHashPairsP4 :many
|
||||
SELECT ns.iata, ns.prefix_4 AS hash, ns.node_id
|
||||
FROM node_short_ids ns
|
||||
JOIN nodes n ON n.id = ns.node_id
|
||||
WHERE ns.iata = ANY($1::bpchar[])
|
||||
AND n.node_type IN (2, 3)
|
||||
AND ns.prefix_4 = ANY($2::bytea[])
|
||||
`
|
||||
|
||||
type ResolveRelayHashPairsP4Params struct {
|
||||
Iatas []string `json:"iatas"`
|
||||
Hashes [][]byte `json:"hashes"`
|
||||
}
|
||||
|
||||
type ResolveRelayHashPairsP4Row struct {
|
||||
Iata string `json:"iata"`
|
||||
Hash []byte `json:"hash"`
|
||||
NodeID uuid.UUID `json:"node_id"`
|
||||
}
|
||||
|
||||
// Batch form of ResolvePathHashesP4 across IATAs; callers check uniqueness per IATA.
|
||||
func (q *Queries) ResolveRelayHashPairsP4(ctx context.Context, arg ResolveRelayHashPairsP4Params) ([]ResolveRelayHashPairsP4Row, error) {
|
||||
rows, err := q.db.Query(ctx, resolveRelayHashPairsP4, arg.Iatas, arg.Hashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ResolveRelayHashPairsP4Row{}
|
||||
for rows.Next() {
|
||||
var i ResolveRelayHashPairsP4Row
|
||||
if err := rows.Scan(&i.Iata, &i.Hash, &i.NodeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const saveChannelCatalogue = `-- name: SaveChannelCatalogue :exec
|
||||
INSERT INTO meshmapper_channel_catalogues (iata, url, payload, etag, checked_at, attempted_at, next_attempt, last_error)
|
||||
VALUES ($1, $2, $3::jsonb, $4::text,
|
||||
@@ -5026,23 +5176,24 @@ func (q *Queries) SaveZoneList(ctx context.Context, arg SaveZoneListParams) erro
|
||||
return err
|
||||
}
|
||||
|
||||
const searchKnownRoutes = `-- name: SearchKnownRoutes :many
|
||||
const searchKnownRoutesByNodes = `-- name: SearchKnownRoutesByNodes :many
|
||||
SELECT id, node_ids, hash_prefix, iata, hop_count, first_seen, last_seen, observation_count
|
||||
FROM known_routes
|
||||
WHERE iata = $1
|
||||
AND array_position(hash_prefix, $2::bytea) IS NOT NULL
|
||||
AND array_position(hash_prefix, $3::bytea) IS NOT NULL
|
||||
AND array_position(hash_prefix, $2::bytea) < array_position(hash_prefix, $3::bytea)
|
||||
FROM known_routes kr
|
||||
WHERE kr.node_ids && $1::uuid[]
|
||||
AND kr.node_ids && $2::uuid[]
|
||||
AND kr.iata = ANY($3::bpchar[])
|
||||
ORDER BY hop_count ASC, last_seen DESC
|
||||
LIMIT $4
|
||||
`
|
||||
|
||||
type SearchKnownRoutesParams struct {
|
||||
Iata string `json:"iata"`
|
||||
Column2 []byte `json:"column_2"`
|
||||
Column3 []byte `json:"column_3"`
|
||||
type SearchKnownRoutesByNodesParams struct {
|
||||
FromNodes []uuid.UUID `json:"from_nodes"`
|
||||
ToNodes []uuid.UUID `json:"to_nodes"`
|
||||
Iatas []string `json:"iatas"`
|
||||
RowLimit int32 `json:"row_limit"`
|
||||
}
|
||||
|
||||
type SearchKnownRoutesRow struct {
|
||||
type SearchKnownRoutesByNodesRow struct {
|
||||
ID int64 `json:"id"`
|
||||
NodeIds []uuid.UUID `json:"node_ids"`
|
||||
HashPrefix [][]byte `json:"hash_prefix"`
|
||||
@@ -5053,17 +5204,23 @@ type SearchKnownRoutesRow struct {
|
||||
ObservationCount int64 `json:"observation_count"`
|
||||
}
|
||||
|
||||
// Returns known routes containing a subsequence from source to destination hash prefix.
|
||||
// Verifies source appears before destination in the route.
|
||||
func (q *Queries) SearchKnownRoutes(ctx context.Context, arg SearchKnownRoutesParams) ([]SearchKnownRoutesRow, error) {
|
||||
rows, err := q.db.Query(ctx, searchKnownRoutes, arg.Iata, arg.Column2, arg.Column3)
|
||||
// Routes holding a source and a destination candidate; callers check order and IATA.
|
||||
// A route's hops resolve in its own IATA, so candidates from other IATAs can't match.
|
||||
// Both && predicates use the node_ids GIN index.
|
||||
func (q *Queries) SearchKnownRoutesByNodes(ctx context.Context, arg SearchKnownRoutesByNodesParams) ([]SearchKnownRoutesByNodesRow, error) {
|
||||
rows, err := q.db.Query(ctx, searchKnownRoutesByNodes,
|
||||
arg.FromNodes,
|
||||
arg.ToNodes,
|
||||
arg.Iatas,
|
||||
arg.RowLimit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []SearchKnownRoutesRow{}
|
||||
items := []SearchKnownRoutesByNodesRow{}
|
||||
for rows.Next() {
|
||||
var i SearchKnownRoutesRow
|
||||
var i SearchKnownRoutesByNodesRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.NodeIds,
|
||||
|
||||
Reference in New Issue
Block a user