From f1b7ccc6b2199d5e1b714ed565910be341c35c21 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Thu, 26 Jun 2025 10:10:10 +0200 Subject: [PATCH] sqldb+graph/db: prune graph nodes in a single query Update the pruneGraphNodes routine to prune the graph nodes in a single query instead of two. --- graph/db/sql_store.go | 27 ++++-------- sqldb/sqlc/graph.sql.go | 85 +++++++++++++++++------------------- sqldb/sqlc/querier.go | 4 +- sqldb/sqlc/queries/graph.sql | 30 ++++++------- 4 files changed, 65 insertions(+), 81 deletions(-) diff --git a/graph/db/sql_store.go b/graph/db/sql_store.go index 10edcea23..74de9b977 100644 --- a/graph/db/sql_store.go +++ b/graph/db/sql_store.go @@ -64,7 +64,7 @@ type SQLQueries interface { ListNodesPaginated(ctx context.Context, arg sqlc.ListNodesPaginatedParams) ([]sqlc.Node, error) ListNodeIDsAndPubKeys(ctx context.Context, arg sqlc.ListNodeIDsAndPubKeysParams) ([]sqlc.ListNodeIDsAndPubKeysRow, error) IsPublicV1Node(ctx context.Context, pubKey []byte) (bool, error) - GetUnconnectedNodes(ctx context.Context) ([]sqlc.GetUnconnectedNodesRow, error) + DeleteUnconnectedNodes(ctx context.Context) ([][]byte, error) DeleteNodeByPubKey(ctx context.Context, arg sqlc.DeleteNodeByPubKeyParams) (sql.Result, error) DeleteNode(ctx context.Context, id int64) error @@ -2551,30 +2551,21 @@ func (s *SQLStore) PruneTip() (*chainhash.Hash, uint32, error) { func (s *SQLStore) pruneGraphNodes(ctx context.Context, db SQLQueries) ([]route.Vertex, error) { - // Fetch all un-connected nodes from the database. - // NOTE: this will not include any nodes that are listed in the - // source table. - nodes, err := db.GetUnconnectedNodes(ctx) + nodeKeys, err := db.DeleteUnconnectedNodes(ctx) if err != nil { - return nil, fmt.Errorf("unable to fetch unconnected nodes: %w", - err) + return nil, fmt.Errorf("unable to delete unconnected "+ + "nodes: %w", err) } - prunedNodes := make([]route.Vertex, 0, len(nodes)) - for _, node := range nodes { - // TODO(elle): update to use sqlc.slice() once that works. - if err = db.DeleteNode(ctx, node.ID); err != nil { - return nil, fmt.Errorf("unable to delete "+ - "node(id=%d): %w", node.ID, err) - } - - pubKey, err := route.NewVertexFromBytes(node.PubKey) + prunedNodes := make([]route.Vertex, len(nodeKeys)) + for i, nodeKey := range nodeKeys { + pub, err := route.NewVertexFromBytes(nodeKey) if err != nil { return nil, fmt.Errorf("unable to parse pubkey "+ - "for node(id=%d): %w", node.ID, err) + "from bytes: %w", err) } - prunedNodes = append(prunedNodes, pubKey) + prunedNodes[i] = pub } return prunedNodes, nil diff --git a/sqldb/sqlc/graph.sql.go b/sqldb/sqlc/graph.sql.go index 0387f612e..c914da391 100644 --- a/sqldb/sqlc/graph.sql.go +++ b/sqldb/sqlc/graph.sql.go @@ -244,6 +244,46 @@ func (q *Queries) DeletePruneLogEntriesInRange(ctx context.Context, arg DeletePr return err } +const deleteUnconnectedNodes = `-- name: DeleteUnconnectedNodes :many +DELETE FROM nodes +WHERE + -- Ignore any of our source nodes. + NOT EXISTS ( + SELECT 1 + FROM source_nodes sn + WHERE sn.node_id = nodes.id + ) + -- Select all nodes that do not have any channels. + AND NOT EXISTS ( + SELECT 1 + FROM channels c + WHERE c.node_id_1 = nodes.id OR c.node_id_2 = nodes.id +) RETURNING pub_key +` + +func (q *Queries) DeleteUnconnectedNodes(ctx context.Context) ([][]byte, error) { + rows, err := q.db.QueryContext(ctx, deleteUnconnectedNodes) + if err != nil { + return nil, err + } + defer rows.Close() + var items [][]byte + for rows.Next() { + var pub_key []byte + if err := rows.Scan(&pub_key); err != nil { + return nil, err + } + items = append(items, pub_key) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const deleteZombieChannel = `-- name: DeleteZombieChannel :execresult DELETE FROM zombie_channels WHERE scid = $1 @@ -1388,51 +1428,6 @@ func (q *Queries) GetSourceNodesByVersion(ctx context.Context, version int16) ([ return items, nil } -const getUnconnectedNodes = `-- name: GetUnconnectedNodes :many -SELECT n.id, n.pub_key -FROM nodes n -WHERE NOT EXISTS ( - SELECT 1 - FROM channels c - WHERE c.node_id_1 = n.id OR c.node_id_2 = n.id -) -AND NOT EXISTS ( - SELECT 1 - FROM source_nodes sn - WHERE sn.node_id = n.id -) -` - -type GetUnconnectedNodesRow struct { - ID int64 - PubKey []byte -} - -// Select all nodes that do not have any channels. -// Ignore any of our source nodes. -func (q *Queries) GetUnconnectedNodes(ctx context.Context) ([]GetUnconnectedNodesRow, error) { - rows, err := q.db.QueryContext(ctx, getUnconnectedNodes) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetUnconnectedNodesRow - for rows.Next() { - var i GetUnconnectedNodesRow - if err := rows.Scan(&i.ID, &i.PubKey); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const getV1DisabledSCIDs = `-- name: GetV1DisabledSCIDs :many SELECT c.scid FROM channels c diff --git a/sqldb/sqlc/querier.go b/sqldb/sqlc/querier.go index 28df66d90..b6f776a74 100644 --- a/sqldb/sqlc/querier.go +++ b/sqldb/sqlc/querier.go @@ -27,6 +27,7 @@ type Querier interface { DeleteNodeByPubKey(ctx context.Context, arg DeleteNodeByPubKeyParams) (sql.Result, error) DeleteNodeFeature(ctx context.Context, arg DeleteNodeFeatureParams) error DeletePruneLogEntriesInRange(ctx context.Context, arg DeletePruneLogEntriesInRangeParams) error + DeleteUnconnectedNodes(ctx context.Context) ([][]byte, error) DeleteZombieChannel(ctx context.Context, arg DeleteZombieChannelParams) (sql.Result, error) FetchAMPSubInvoiceHTLCs(ctx context.Context, arg FetchAMPSubInvoiceHTLCsParams) ([]FetchAMPSubInvoiceHTLCsRow, error) FetchAMPSubInvoices(ctx context.Context, arg FetchAMPSubInvoicesParams) ([]AmpSubInvoice, error) @@ -66,9 +67,6 @@ type Querier interface { GetPublicV1ChannelsBySCID(ctx context.Context, arg GetPublicV1ChannelsBySCIDParams) ([]Channel, error) GetSCIDByOutpoint(ctx context.Context, arg GetSCIDByOutpointParams) ([]byte, error) GetSourceNodesByVersion(ctx context.Context, version int16) ([]GetSourceNodesByVersionRow, error) - // Select all nodes that do not have any channels. - // Ignore any of our source nodes. - GetUnconnectedNodes(ctx context.Context) ([]GetUnconnectedNodesRow, error) // NOTE: this is V1 specific since for V1, disabled is a // simple, single boolean. The proposed V2 policy // structure will have a more complex disabled bit vector diff --git a/sqldb/sqlc/queries/graph.sql b/sqldb/sqlc/queries/graph.sql index 681f80def..1617f8916 100644 --- a/sqldb/sqlc/queries/graph.sql +++ b/sqldb/sqlc/queries/graph.sql @@ -65,21 +65,21 @@ SELECT EXISTS ( AND n.pub_key = $1 ); --- name: GetUnconnectedNodes :many -SELECT n.id, n.pub_key -FROM nodes n --- Select all nodes that do not have any channels. -WHERE NOT EXISTS ( - SELECT 1 - FROM channels c - WHERE c.node_id_1 = n.id OR c.node_id_2 = n.id -) --- Ignore any of our source nodes. -AND NOT EXISTS ( - SELECT 1 - FROM source_nodes sn - WHERE sn.node_id = n.id -); +-- name: DeleteUnconnectedNodes :many +DELETE FROM nodes +WHERE + -- Ignore any of our source nodes. + NOT EXISTS ( + SELECT 1 + FROM source_nodes sn + WHERE sn.node_id = nodes.id + ) + -- Select all nodes that do not have any channels. + AND NOT EXISTS ( + SELECT 1 + FROM channels c + WHERE c.node_id_1 = nodes.id OR c.node_id_2 = nodes.id +) RETURNING pub_key; -- name: DeleteNodeByPubKey :execresult DELETE FROM nodes