graph/db: batch loading for DisconnectBlockAtHeight
What changed, and why it matters
This commit is a routine performance refactor. It changes how LND's graph database removes channel data when a block is disconnected, switching from one-by-one processing to batch processing. There is no indication this fixes a security bug; it appears aimed at efficiency and code cleanup.
No security action required; treat as normal code maintenance. Reviewers may optionally verify that batchBuildChannelInfo preserves the same removed-channel output and deletion behavior as the original loop.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors DisconnectBlockAtHeight in graph/db/sql_store.go to use a new batchBuildChannelInfo helper instead of looping over rows and calling buildNodeVertices/getAndBuildEdgeInfo individually. It also adds ChannelAndNodeIDs interface methods for GetChannelsBySCIDRangeRow in sqldb/sqlc/db_custom.go so the batch helper can operate on both row types. The change includes an early-return path when no channels need disconnection, which deletes prune-log entries in range before returning.
Changed components
graph/db/sql_store.gosqldb/sqlc/db_custom.goInspect captured patch +40 / −17
diff --git a/graph/db/sql_store.go b/graph/db/sql_store.go
index f1b34b6..33e3e1c 100644
--- a/graph/db/sql_store.go
+++ b/graph/db/sql_store.go
@@ -2627,27 +2627,29 @@ func (s *SQLStore) DisconnectBlockAtHeight(height uint32) (
return fmt.Errorf("unable to fetch channels: %w", err)
}
- chanIDsToDelete := make([]int64, len(rows))
- for i, row := range rows {
- node1, node2, err := buildNodeVertices(
- row.Node1PubKey, row.Node2PubKey,
- )
- if err != nil {
- return err
- }
-
- channel, err := getAndBuildEdgeInfo(
- ctx, db, s.cfg.ChainHash, row.GraphChannel,
- node1, node2,
+ if len(rows) == 0 {
+ // No channels to disconnect, but still clean up prune
+ // log.
+ return db.DeletePruneLogEntriesInRange(
+ ctx, sqlc.DeletePruneLogEntriesInRangeParams{
+ StartHeight: int64(height),
+ EndHeight: int64(
+ endShortChanID.BlockHeight,
+ ),
+ },
)
- if err != nil {
- return err
- }
+ }
- chanIDsToDelete[i] = row.GraphChannel.ID
- removedChans = append(removedChans, channel)
+ // Batch build all channel edges for disconnection.
+ channelEdges, chanIDsToDelete, err := batchBuildChannelInfo(
+ ctx, s.cfg, db, rows,
+ )
+ if err != nil {
+ return err
}
+ removedChans = channelEdges
+
err = s.deleteChannels(ctx, db, chanIDsToDelete)
if err != nil {
return fmt.Errorf("unable to delete channels: %w", err)
diff --git a/sqldb/sqlc/db_custom.go b/sqldb/sqlc/db_custom.go
index 2b378d4..7c9825e 100644
--- a/sqldb/sqlc/db_custom.go
+++ b/sqldb/sqlc/db_custom.go
@@ -126,3 +126,24 @@ func (r GetChannelsByOutpointsRow) Node1Pub() []byte {
func (r GetChannelsByOutpointsRow) Node2Pub() []byte {
return r.Node2Pubkey
}
+
+// Channel returns the GraphChannel associated with this interface.
+//
+// NOTE: This method is part of the ChannelAndNodeIDs interface.
+func (r GetChannelsBySCIDRangeRow) Channel() GraphChannel {
+ return r.GraphChannel
+}
+
+// Node1Pub returns the public key of the first node as a byte slice.
+//
+// NOTE: This method is part of the ChannelAndNodeIDs interface.
+func (r GetChannelsBySCIDRangeRow) Node1Pub() []byte {
+ return r.Node1PubKey
+}
+
+// Node2Pub returns the public key of the second node as a byte slice.
+//
+// NOTE: This method is part of the ChannelAndNodeIDs interface.
+func (r GetChannelsBySCIDRangeRow) Node2Pub() []byte {
+ return r.Node2PubKey
+}
Why this scored 11/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.