From 68c254ce04ede474277edae7388b99ea4e580df1 Mon Sep 17 00:00:00 2001 From: "Enot (ded) Skelly" Date: Mon, 8 Jun 2026 10:59:46 -0700 Subject: [PATCH] feat: add observation count to routes --- db/migrations/001_schema.sql | 1 + db/queries/queries.sql | 9 +++++---- db/routes.go | 26 ++++++++++++++------------ db/sqlc/models.go | 15 ++++++++------- db/sqlc/queries.sql.go | 12 ++++++++---- docs/docs.go | 3 +++ docs/swagger.json | 3 +++ docs/swagger.yaml | 2 ++ internal/api/routes.go | 13 +++++++------ 9 files changed, 51 insertions(+), 33 deletions(-) diff --git a/db/migrations/001_schema.sql b/db/migrations/001_schema.sql index 154f80e..a0fab31 100644 --- a/db/migrations/001_schema.sql +++ b/db/migrations/001_schema.sql @@ -347,6 +347,7 @@ CREATE TABLE known_routes ( hop_count INT NOT NULL, first_seen TIMESTAMPTZ NOT NULL DEFAULT NOW(), last_seen TIMESTAMPTZ NOT NULL DEFAULT NOW(), + observation_count BIGINT NOt NULL DEFAULT 1, UNIQUE (node_ids, iata) ); diff --git a/db/queries/queries.sql b/db/queries/queries.sql index cac659c..292de5e 100644 --- a/db/queries/queries.sql +++ b/db/queries/queries.sql @@ -832,10 +832,11 @@ LIMIT $6; INSERT INTO known_routes (node_ids, hash_prefix, iata, hop_count) VALUES ($1, $2, $3, $4) ON CONFLICT (node_ids, iata) DO UPDATE SET - last_seen = NOW(); + last_seen = NOW(), + observation_count = known_routes.observation_count + 1; -- name: ListKnownRoutes :many -SELECT id, node_ids, hash_prefix, iata, hop_count, first_seen, last_seen +SELECT id, node_ids, hash_prefix, iata, hop_count, first_seen, last_seen, observation_count FROM known_routes WHERE ($1 = '' OR iata = $1) AND ($2 = 0 OR hop_count = $2) @@ -846,7 +847,7 @@ LIMIT $4; -- name: SearchKnownRoutes :many -- Returns known routes containing a subsequence from source to destination hash prefix. -- Verifies source appears before destination in the route. -SELECT id, node_ids, hash_prefix, iata, hop_count, first_seen, last_seen +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 @@ -855,7 +856,7 @@ WHERE iata = $1 ORDER BY hop_count ASC, last_seen DESC; -- name: GetKnownRoutesByNode :many -SELECT id, node_ids, hash_prefix, iata, hop_count, first_seen, last_seen +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) diff --git a/db/routes.go b/db/routes.go index e1a04fc..14e7f62 100644 --- a/db/routes.go +++ b/db/routes.go @@ -81,12 +81,13 @@ func (s *Store) SearchKnownRoutes(ctx context.Context, iata, fromHash, toHash st hops = append(hops, hop) } items = append(items, api.KnownRoute{ - ID: r.ID, - IATA: r.Iata, - HopCount: int32(len(hops)), - Hops: hops, - FirstSeen: r.FirstSeen.Time.UnixMilli(), - LastSeen: r.LastSeen.Time.UnixMilli(), + ID: r.ID, + IATA: r.Iata, + HopCount: int32(len(hops)), + Hops: hops, + FirstSeen: r.FirstSeen.Time.UnixMilli(), + LastSeen: r.LastSeen.Time.UnixMilli(), + ObservationCount: r.ObservationCount, }) } return items, nil @@ -262,12 +263,13 @@ func toKnownRoutes(rows []sqlc.KnownRoute) []api.KnownRoute { hops = append(hops, hop) } items = append(items, api.KnownRoute{ - ID: r.ID, - IATA: r.Iata, - HopCount: r.HopCount, - Hops: hops, - FirstSeen: r.FirstSeen.Time.UnixMilli(), - LastSeen: r.LastSeen.Time.UnixMilli(), + ID: r.ID, + IATA: r.Iata, + HopCount: r.HopCount, + Hops: hops, + FirstSeen: r.FirstSeen.Time.UnixMilli(), + LastSeen: r.LastSeen.Time.UnixMilli(), + ObservationCount: r.ObservationCount, }) } return items diff --git a/db/sqlc/models.go b/db/sqlc/models.go index c454801..9e5e3ad 100644 --- a/db/sqlc/models.go +++ b/db/sqlc/models.go @@ -50,13 +50,14 @@ type IataCode struct { } type KnownRoute struct { - ID int64 `json:"id"` - NodeIds []uuid.UUID `json:"node_ids"` - HashPrefix [][]byte `json:"hash_prefix"` - Iata string `json:"iata"` - HopCount int32 `json:"hop_count"` - FirstSeen pgtype.Timestamptz `json:"first_seen"` - LastSeen pgtype.Timestamptz `json:"last_seen"` + ID int64 `json:"id"` + NodeIds []uuid.UUID `json:"node_ids"` + HashPrefix [][]byte `json:"hash_prefix"` + Iata string `json:"iata"` + HopCount int32 `json:"hop_count"` + FirstSeen pgtype.Timestamptz `json:"first_seen"` + LastSeen pgtype.Timestamptz `json:"last_seen"` + ObservationCount int64 `json:"observation_count"` } type MvHourlyIataStat struct { diff --git a/db/sqlc/queries.sql.go b/db/sqlc/queries.sql.go index 5e1eeff..2185dc5 100644 --- a/db/sqlc/queries.sql.go +++ b/db/sqlc/queries.sql.go @@ -263,7 +263,7 @@ func (q *Queries) GetIATA(ctx context.Context, iata string) (IataCode, error) { } const getKnownRoutesByNode = `-- name: GetKnownRoutesByNode :many -SELECT id, node_ids, hash_prefix, iata, hop_count, first_seen, last_seen +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) @@ -292,6 +292,7 @@ func (q *Queries) GetKnownRoutesByNode(ctx context.Context, arg GetKnownRoutesBy &i.HopCount, &i.FirstSeen, &i.LastSeen, + &i.ObservationCount, ); err != nil { return nil, err } @@ -1865,7 +1866,7 @@ func (q *Queries) ListIATAs(ctx context.Context) ([]IataCode, error) { } const listKnownRoutes = `-- name: ListKnownRoutes :many -SELECT id, node_ids, hash_prefix, iata, hop_count, first_seen, last_seen +SELECT id, node_ids, hash_prefix, iata, hop_count, first_seen, last_seen, observation_count FROM known_routes WHERE ($1 = '' OR iata = $1) AND ($2 = 0 OR hop_count = $2) @@ -1903,6 +1904,7 @@ func (q *Queries) ListKnownRoutes(ctx context.Context, arg ListKnownRoutesParams &i.HopCount, &i.FirstSeen, &i.LastSeen, + &i.ObservationCount, ); err != nil { return nil, err } @@ -2852,7 +2854,7 @@ func (q *Queries) ResolvePathHashes(ctx context.Context, arg ResolvePathHashesPa } const searchKnownRoutes = `-- name: SearchKnownRoutes :many -SELECT id, node_ids, hash_prefix, iata, hop_count, first_seen, last_seen +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 @@ -2886,6 +2888,7 @@ func (q *Queries) SearchKnownRoutes(ctx context.Context, arg SearchKnownRoutesPa &i.HopCount, &i.FirstSeen, &i.LastSeen, + &i.ObservationCount, ); err != nil { return nil, err } @@ -3126,7 +3129,8 @@ const upsertKnownRoute = `-- name: UpsertKnownRoute :exec INSERT INTO known_routes (node_ids, hash_prefix, iata, hop_count) VALUES ($1, $2, $3, $4) ON CONFLICT (node_ids, iata) DO UPDATE SET - last_seen = NOW() + last_seen = NOW(), + observation_count = known_routes.observation_count + 1 ` type UpsertKnownRouteParams struct { diff --git a/docs/docs.go b/docs/docs.go index 3f586d2..827aed8 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2151,6 +2151,9 @@ const docTemplate = `{ "lastSeen": { "description": "epoch ms", "type": "integer" + }, + "observationCount": { + "type": "integer" } } }, diff --git a/docs/swagger.json b/docs/swagger.json index 3f15727..24d2311 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2149,6 +2149,9 @@ "lastSeen": { "description": "epoch ms", "type": "integer" + }, + "observationCount": { + "type": "integer" } } }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 4474078..0106fe8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -167,6 +167,8 @@ definitions: lastSeen: description: epoch ms type: integer + observationCount: + type: integer type: object github_com_MeshCore-Beacon_beacon-server_internal_api.Node: properties: diff --git a/internal/api/routes.go b/internal/api/routes.go index 22665fd..622fbd8 100644 --- a/internal/api/routes.go +++ b/internal/api/routes.go @@ -12,12 +12,13 @@ type RouteHop struct { // KnownRoute is a fully resolved path through the mesh where all hops // have been confirmed as high confidence. type KnownRoute struct { - ID int64 `json:"id"` - IATA string `json:"iata"` - HopCount int32 `json:"hopCount"` - Hops []RouteHop `json:"hops"` - FirstSeen int64 `json:"firstSeen"` // epoch ms - LastSeen int64 `json:"lastSeen"` // epoch ms + ID int64 `json:"id"` + IATA string `json:"iata"` + HopCount int32 `json:"hopCount"` + Hops []RouteHop `json:"hops"` + FirstSeen int64 `json:"firstSeen"` // epoch ms + LastSeen int64 `json:"lastSeen"` // epoch ms + ObservationCount int64 `json:"observationCount"` } // CrossIATAHop represents the boundary hop between two IATAs in a cross-IATA route.