What changed, and why it matters
This change improves how a background database service shuts down cleanly. It creates a cancel signal during startup and checks it inside long database loops, so shutdown isn't delayed by cache-loading work. There is no attacker-controlled path or security vulnerability shown in the diff; it is a robustness/clean-shutdown improvement.
Treat as normal code-quality/maintenance patch. No security response required. Review that all long-running startup paths respect ctx cancellation consistently.
Security signals we found
No security-relevant signal in diff: change is defensive shutdown/cancellation hygiene
Context cancellation can reduce shutdown latency and resource exhaustion window
No new attack surface, no untrusted input handling, no privilege boundary
Evidence from the diff
ChannelGraph.Start() now uses context.WithCancel(context.Background()) instead of context.TODO(), stores the cancel function in an fn.Option field, and Stop() calls that cancel. The KVStore’s ForEachChannelCacheable and ForEachNodeCacheable now accept and check ctx.Err() inside their iteration callbacks so long-running cache population can exit promptly on shutdown. No input validation, authorization, or cryptographic changes are present.
Changed components
graph/db/graph.go: ChannelGraph.Start/Stop lifecyclegraph/db/kv_store.go: ForEachChannelCacheable, ForEachNodeCacheableInspect captured patch +17 / −5
diff --git a/graph/db/graph.go b/graph/db/graph.go
index eb84603..7662253 100644
--- a/graph/db/graph.go
+++ b/graph/db/graph.go
@@ -15,6 +15,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/batch"
+ "github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
@@ -39,8 +40,9 @@ type ChannelGraph struct {
db Store
*topologyManager
- quit chan struct{}
- wg sync.WaitGroup
+ quit chan struct{}
+ wg sync.WaitGroup
+ cancel fn.Option[context.CancelFunc]
}
// NewChannelGraph creates a new ChannelGraph instance with the given backend.
@@ -77,7 +79,8 @@ func (c *ChannelGraph) Start() error {
log.Debugf("ChannelGraph starting")
defer log.Debug("ChannelGraph started")
- ctx := context.TODO()
+ ctx, cancel := context.WithCancel(context.Background())
+ c.cancel = fn.Some(cancel)
if err := c.populateCache(ctx); err != nil {
return fmt.Errorf("could not populate the graph cache: %w", err)
@@ -98,6 +101,7 @@ func (c *ChannelGraph) Stop() error {
log.Debugf("ChannelGraph shutting down...")
defer log.Debug("ChannelGraph shutdown complete")
+ c.cancel.WhenSome(func(fn context.CancelFunc) { fn() })
close(c.quit)
c.wg.Wait()
diff --git a/graph/db/kv_store.go b/graph/db/kv_store.go
index 2a993d7..3021fe9 100644
--- a/graph/db/kv_store.go
+++ b/graph/db/kv_store.go
@@ -493,7 +493,7 @@ func forEachChannel(db kvdb.Backend, cb func(*models.ChannelEdgeInfo,
//
// NOTE: this method is like ForEachChannel but fetches only the data required
// for the graph cache.
-func (c *KVStore) ForEachChannelCacheable(_ context.Context,
+func (c *KVStore) ForEachChannelCacheable(ctx context.Context,
v lnwire.GossipVersion, cb func(*models.CachedEdgeInfo,
*models.CachedEdgePolicy, *models.CachedEdgePolicy) error,
reset func()) error {
@@ -524,6 +524,10 @@ func (c *KVStore) ForEachChannelCacheable(_ context.Context,
// loaded above and invoke the callback.
return kvdb.ForAll(
edgeIndex, func(k, edgeInfoBytes []byte) error {
+ if err := ctx.Err(); err != nil {
+ return err
+ }
+
var chanID [8]byte
copy(chanID[:], k)
@@ -895,7 +899,7 @@ func forEachNode(db kvdb.Backend,
// graph, executing the passed callback with each node encountered. If the
// callback returns an error, then the transaction is aborted and the iteration
// stops early.
-func (c *KVStore) ForEachNodeCacheable(_ context.Context,
+func (c *KVStore) ForEachNodeCacheable(ctx context.Context,
v lnwire.GossipVersion, cb func(route.Vertex,
*lnwire.FeatureVector) error, reset func()) error {
@@ -912,6 +916,10 @@ func (c *KVStore) ForEachNodeCacheable(_ context.Context,
}
return nodes.ForEach(func(pubKey, nodeBytes []byte) error {
+ if err := ctx.Err(); err != nil {
+ return err
+ }
+
// If this is the source key, then we skip this
// iteration as the value for this key is a pubKey
// rather than raw node information.
Why this scored 21/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.