From 86cde4b93f9fc5b01fced53489aa3224e9b9b7ea Mon Sep 17 00:00:00 2001 From: Abdullahi Yunus Date: Tue, 11 Nov 2025 12:51:49 +0100 Subject: [PATCH 1/3] graphdb: add benchmark for isPublicNode query In this commit we add a benchmark to test the performance of IsPublicNode query. --- graph/db/graph_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/graph/db/graph_test.go b/graph/db/graph_test.go index 590c077d6..3e5c93173 100644 --- a/graph/db/graph_test.go +++ b/graph/db/graph_test.go @@ -1640,6 +1640,8 @@ func TestGraphCacheTraversal(t *testing.T) { require.Equal(t, numChannels*2*(numNodes-1), numNodeChans) } +// fillTestGraph fills the graph with a given number of nodes and create a given +// number of channels between each node. func fillTestGraph(t testing.TB, graph *ChannelGraph, numNodes, numChannels int) (map[uint64]struct{}, []*models.Node) { @@ -4042,6 +4044,28 @@ func TestNodeIsPublic(t *testing.T) { ) } +// BenchmarkIsPublicNode measures the performance of IsPublicNode when checking +// a large number of nodes. +func BenchmarkIsPublicNode(b *testing.B) { + graph := MakeTestGraph(b) + + // Create a graph with a reasonable number of nodes and channels. + numNodes := 100 + numChans := 4 + _, nodes := fillTestGraph(b, graph, numNodes, numChans) + + // Use deterministic random number generator for reproducible results. + rng := prand.New(prand.NewSource(42)) + + for b.Loop() { + // Query random nodes to avoid query caching and better + // represent real-world query patterns. + nodePub := nodes[rng.Intn(len(nodes))].PubKeyBytes + _, err := graph.IsPublicNode(nodePub) + require.NoError(b, err) + } +} + // TestDisabledChannelIDs ensures that the disabled channels within the // disabledEdgePolicyBucket are managed properly and the list returned from // DisabledChannelIDs is correct. From ac2cec462c63e62a57977b18faae593c6a360718 Mon Sep 17 00:00:00 2001 From: Abdullahi Yunus Date: Mon, 10 Nov 2025 12:16:07 +0100 Subject: [PATCH 2/3] graph: use UNION for isPublicNode query In this commit we updated the IsPublicV1Node query to use UNION instead of OR, since sqlite struggles to efficiently use multiple indexes in a single query involving OR conditions across different columns. We use UNION ALL since the query doesn't care about duplicates. --- sqldb/sqlc/graph.sql.go | 9 ++++++++- sqldb/sqlc/queries/graph.sql | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/sqldb/sqlc/graph.sql.go b/sqldb/sqlc/graph.sql.go index 8ed9333d7..0ce7780d5 100644 --- a/sqldb/sqlc/graph.sql.go +++ b/sqldb/sqlc/graph.sql.go @@ -2653,7 +2653,7 @@ const isPublicV1Node = `-- name: IsPublicV1Node :one SELECT EXISTS ( SELECT 1 FROM graph_channels c - JOIN graph_nodes n ON n.id = c.node_id_1 OR n.id = c.node_id_2 + JOIN graph_nodes n ON n.id = c.node_id_1 -- NOTE: we hard-code the version here since the clauses -- here that determine if a node is public is specific -- to the V1 gossip protocol. In V1, a node is public @@ -2665,6 +2665,13 @@ SELECT EXISTS ( WHERE c.version = 1 AND c.bitcoin_1_signature IS NOT NULL AND n.pub_key = $1 + UNION ALL + SELECT 1 + FROM graph_channels c + JOIN graph_nodes n ON n.id = c.node_id_2 + WHERE c.version = 1 + AND c.bitcoin_1_signature IS NOT NULL + AND n.pub_key = $1 ) ` diff --git a/sqldb/sqlc/queries/graph.sql b/sqldb/sqlc/queries/graph.sql index b9bee1822..a8ff040d9 100644 --- a/sqldb/sqlc/queries/graph.sql +++ b/sqldb/sqlc/queries/graph.sql @@ -77,7 +77,7 @@ LIMIT $3; SELECT EXISTS ( SELECT 1 FROM graph_channels c - JOIN graph_nodes n ON n.id = c.node_id_1 OR n.id = c.node_id_2 + JOIN graph_nodes n ON n.id = c.node_id_1 -- NOTE: we hard-code the version here since the clauses -- here that determine if a node is public is specific -- to the V1 gossip protocol. In V1, a node is public @@ -89,6 +89,13 @@ SELECT EXISTS ( WHERE c.version = 1 AND c.bitcoin_1_signature IS NOT NULL AND n.pub_key = $1 + UNION ALL + SELECT 1 + FROM graph_channels c + JOIN graph_nodes n ON n.id = c.node_id_2 + WHERE c.version = 1 + AND c.bitcoin_1_signature IS NOT NULL + AND n.pub_key = $1 ); -- name: DeleteUnconnectedNodes :many From 68f558c865de0297fd7de7caf1946f16282e4ddd Mon Sep 17 00:00:00 2001 From: Abdullahi Yunus Date: Fri, 19 Dec 2025 22:51:53 +0100 Subject: [PATCH 3/3] docs: add release note --- docs/release-notes/release-notes-0.20.1.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/release-notes/release-notes-0.20.1.md b/docs/release-notes/release-notes-0.20.1.md index 571cfc326..7cb46cff5 100644 --- a/docs/release-notes/release-notes-0.20.1.md +++ b/docs/release-notes/release-notes-0.20.1.md @@ -98,6 +98,10 @@ safe single-writer behavior until the wallet subsystem is fully concurrent-safe. +* [Modified the query for `IsPublicV1Node`](https://github.com/lightningnetwork/lnd/pull/10356) + to use `UNION ALL` instead of `OR` conditions in the `WHERE` clause, improving + performance when checking for public nodes especially in large graphs when using `SQL` backends. + ## Deprecations # Technical and Architectural Updates @@ -113,5 +117,6 @@ # Contributors (Alphabetical Order) +* Abdulkbk * bitromortac * Ziggie