@@ -22,6 +22,7 @@ import (
22
22
"github.com/lightningnetwork/lnd/aliasmgr"
23
23
"github.com/lightningnetwork/lnd/batch"
24
24
"github.com/lightningnetwork/lnd/channeldb/models"
25
+ "github.com/lightningnetwork/lnd/fn"
25
26
"github.com/lightningnetwork/lnd/input"
26
27
"github.com/lightningnetwork/lnd/kvdb"
27
28
"github.com/lightningnetwork/lnd/lnwire"
@@ -192,6 +193,9 @@ type ChannelGraph struct {
192
193
193
194
// cacheErr returns errors from the graph cache loading goroutine.
194
195
cacheErr chan error
196
+
197
+ // cacheUpdates stores deferred in-flight cache writes.
198
+ cacheUpdates * fn.ConcurrentQueue [func ()]
195
199
}
196
200
197
201
// NewChannelGraph allocates a new ChannelGraph backed by a DB instance. The
@@ -217,6 +221,7 @@ func NewChannelGraph(db kvdb.Backend, rejectCacheSize, chanCacheSize int,
217
221
g .nodeScheduler = batch .NewTimeScheduler (
218
222
db , nil , batchCommitInterval ,
219
223
)
224
+ g .cacheUpdates = fn.NewConcurrentQueue [func ()](1000 )
220
225
221
226
// The graph cache can be turned off (e.g. for mobile users) for a
222
227
// speed/memory usage tradeoff.
@@ -226,6 +231,7 @@ func NewChannelGraph(db kvdb.Backend, rejectCacheSize, chanCacheSize int,
226
231
// Start populating the cache asynchronously.
227
232
g .cacheReady = make (chan struct {})
228
233
g .cacheErr = make (chan error , 1 )
234
+ g .cacheUpdates .Start ()
229
235
go g .populateGraphCache ()
230
236
}
231
237
@@ -266,6 +272,14 @@ func (c *ChannelGraph) populateGraphCache() {
266
272
return
267
273
}
268
274
275
+ go func () {
276
+ for update := range c .cacheUpdates .ChanOut () {
277
+ c .cacheMu .Lock ()
278
+ update ()
279
+ c .cacheMu .Unlock ()
280
+ }
281
+ }()
282
+
269
283
if c .graphCache != nil {
270
284
log .Debugf ("Finished populating in-memory channel graph (took " +
271
285
"%v, %s)" , time .Since (startTime ), c .graphCache .Stats ())
@@ -289,16 +303,46 @@ func (c *ChannelGraph) populateGraphCache() {
289
303
// This method is non-blocking and should be used to safely access the graph
290
304
// cache from concurrent goroutines.
291
305
func (c * ChannelGraph ) getGraphCache () (* GraphCache , error ) {
306
+ // If graph cache is not being used, return nil
292
307
if c .graphCache == nil {
293
308
return nil , nil
294
309
}
295
310
311
+ // Check if there was an error during population
312
+ if err := c .checkCacheErr (); err != nil {
313
+ return nil , fmt .Errorf ("graph cache population failed: %w" , err )
314
+ }
315
+
316
+ // At this point, we know the cache exists and there were no errors
317
+ // We can return it regardless of whether it's fully populated or not.
318
+ return c .graphCache , nil
319
+ }
320
+
321
+ func (c * ChannelGraph ) waitUntilGraphReady () {
322
+ <- c .cacheReady
323
+ }
324
+
325
+ // checkCacheErr is a helper function to check for errors in cache population
326
+ func (c * ChannelGraph ) checkCacheErr () error {
296
327
select {
297
- case <- c .cacheReady :
298
- return c .graphCache , nil
299
328
case err := <- c .cacheErr :
300
- return nil , fmt .Errorf ("graph cache population failed: %w" , err )
329
+ return err
330
+ default :
331
+ return nil
332
+ }
333
+ }
334
+
335
+ // enqueueCacheUpdate either defers a graph cache write or writes it
336
+ // right away, depending on the cache state.
337
+ func (c * ChannelGraph ) enqueueCacheUpdate (update func ()) {
338
+ if c .graphCache == nil {
339
+ // If we're not using the graph cache, execute the update
340
+ update ()
341
+ return
301
342
}
343
+
344
+ // Otherwise, queue the update
345
+ c .cacheUpdates .ChanIn () <- update
302
346
}
303
347
304
348
// getChannelMap loads all channel edge policies from the database and stores
@@ -892,16 +936,15 @@ func (c *ChannelGraph) AddLightningNode(node *LightningNode,
892
936
893
937
r := & batch.Request {
894
938
Update : func (tx kvdb.RwTx ) error {
895
- graphCache , err := c .getGraphCache ()
896
- if err == nil && graphCache != nil {
897
- cNode := newGraphCacheNode (
898
- node .PubKeyBytes , node .Features ,
899
- )
900
- err := graphCache .AddNode (tx , cNode )
901
- if err != nil {
902
- return err
939
+ c .enqueueCacheUpdate (func () {
940
+ graphCache , err := c .getGraphCache ()
941
+ if err == nil && graphCache != nil {
942
+ cNode := newGraphCacheNode (
943
+ node .PubKeyBytes , node .Features ,
944
+ )
945
+ graphCache .AddNode (tx , cNode )
903
946
}
904
- }
947
+ })
905
948
906
949
return addLightningNode (tx , node )
907
950
},
@@ -981,10 +1024,12 @@ func (c *ChannelGraph) DeleteLightningNode(nodePub route.Vertex) error {
981
1024
return ErrGraphNodeNotFound
982
1025
}
983
1026
984
- graphCache , err := c .getGraphCache ()
985
- if err == nil && graphCache != nil {
986
- graphCache .RemoveNode (nodePub )
987
- }
1027
+ c .enqueueCacheUpdate (func () {
1028
+ graphCache , err := c .getGraphCache ()
1029
+ if err == nil && graphCache != nil {
1030
+ graphCache .RemoveNode (nodePub )
1031
+ }
1032
+ })
988
1033
989
1034
return c .deleteLightningNode (nodes , nodePub [:])
990
1035
}, func () {})
@@ -1113,10 +1158,12 @@ func (c *ChannelGraph) addChannelEdge(tx kvdb.RwTx,
1113
1158
return ErrEdgeAlreadyExist
1114
1159
}
1115
1160
1116
- graphCache , err := c .getGraphCache ()
1117
- if err == nil && graphCache != nil {
1118
- graphCache .AddChannel (edge , nil , nil )
1119
- }
1161
+ c .enqueueCacheUpdate (func () {
1162
+ graphCache , err := c .getGraphCache ()
1163
+ if err == nil && graphCache != nil {
1164
+ graphCache .AddChannel (edge , nil , nil )
1165
+ }
1166
+ })
1120
1167
1121
1168
// Before we insert the channel into the database, we'll ensure that
1122
1169
// both nodes already exist in the channel graph. If either node
@@ -1317,10 +1364,12 @@ func (c *ChannelGraph) UpdateChannelEdge(edge *models.ChannelEdgeInfo) error {
1317
1364
return ErrEdgeNotFound
1318
1365
}
1319
1366
1320
- graphCache , err := c .getGraphCache ()
1321
- if err == nil && graphCache != nil {
1322
- graphCache .UpdateChannel (edge )
1323
- }
1367
+ c .enqueueCacheUpdate (func () {
1368
+ graphCache , err := c .getGraphCache ()
1369
+ if err == nil && graphCache != nil {
1370
+ graphCache .UpdateChannel (edge )
1371
+ }
1372
+ })
1324
1373
1325
1374
return putChanEdgeInfo (edgeIndex , edge , chanKey )
1326
1375
}, func () {})
@@ -1565,10 +1614,12 @@ func (c *ChannelGraph) pruneGraphNodes(nodes kvdb.RwBucket,
1565
1614
continue
1566
1615
}
1567
1616
1568
- graphCache , err := c .getGraphCache ()
1569
- if err == nil && graphCache != nil {
1570
- graphCache .RemoveNode (nodePubKey )
1571
- }
1617
+ c .enqueueCacheUpdate (func () {
1618
+ graphCache , err := c .getGraphCache ()
1619
+ if err == nil && graphCache != nil {
1620
+ graphCache .RemoveNode (nodePubKey )
1621
+ }
1622
+ })
1572
1623
1573
1624
// If we reach this point, then there are no longer any edges
1574
1625
// that connect this node, so we can delete it.
@@ -2597,13 +2648,15 @@ func (c *ChannelGraph) delChannelEdgeUnsafe(edges, edgeIndex, chanIndex,
2597
2648
return err
2598
2649
}
2599
2650
2600
- graphCache , err := c .getGraphCache ()
2601
- if err == nil && graphCache != nil {
2602
- graphCache .RemoveChannel (
2603
- edgeInfo .NodeKey1Bytes , edgeInfo .NodeKey2Bytes ,
2604
- edgeInfo .ChannelID ,
2605
- )
2606
- }
2651
+ c .enqueueCacheUpdate (func () {
2652
+ graphCache , err := c .getGraphCache ()
2653
+ if err == nil && graphCache != nil {
2654
+ graphCache .RemoveChannel (
2655
+ edgeInfo .NodeKey1Bytes , edgeInfo .NodeKey2Bytes ,
2656
+ edgeInfo .ChannelID ,
2657
+ )
2658
+ }
2659
+ })
2607
2660
2608
2661
// We'll also remove the entry in the edge update index bucket before
2609
2662
// we delete the edges themselves so we can access their last update
@@ -2736,10 +2789,9 @@ func (c *ChannelGraph) UpdateEdgePolicy(edge *models.ChannelEdgePolicy,
2736
2789
},
2737
2790
Update : func (tx kvdb.RwTx ) error {
2738
2791
var err error
2739
- graphCache , _ := c .getGraphCache ()
2740
2792
2741
2793
isUpdate1 , err = updateEdgePolicy (
2742
- tx , edge , graphCache ,
2794
+ tx , edge , c ,
2743
2795
)
2744
2796
2745
2797
// Silence ErrEdgeNotFound so that the batch can
@@ -2806,7 +2858,7 @@ func (c *ChannelGraph) updateEdgeCache(e *models.ChannelEdgePolicy,
2806
2858
// true if the updated policy belongs to node1, and false if the policy belonged
2807
2859
// to node2.
2808
2860
func updateEdgePolicy (tx kvdb.RwTx , edge * models.ChannelEdgePolicy ,
2809
- graphCache * GraphCache ) (bool , error ) {
2861
+ c * ChannelGraph ) (bool , error ) {
2810
2862
2811
2863
edges := tx .ReadWriteBucket (edgeBucket )
2812
2864
if edges == nil {
@@ -2857,11 +2909,14 @@ func updateEdgePolicy(tx kvdb.RwTx, edge *models.ChannelEdgePolicy,
2857
2909
copy (fromNodePubKey [:], fromNode )
2858
2910
copy (toNodePubKey [:], toNode )
2859
2911
2860
- if graphCache != nil {
2861
- graphCache .UpdatePolicy (
2862
- edge , fromNodePubKey , toNodePubKey , isUpdate1 ,
2863
- )
2864
- }
2912
+ c .enqueueCacheUpdate (func () {
2913
+ graphCache , _ := c .getGraphCache ()
2914
+ if graphCache != nil {
2915
+ graphCache .UpdatePolicy (
2916
+ edge , fromNodePubKey , toNodePubKey , isUpdate1 ,
2917
+ )
2918
+ }
2919
+ })
2865
2920
2866
2921
return isUpdate1 , nil
2867
2922
}
@@ -3732,10 +3787,12 @@ func (c *ChannelGraph) MarkEdgeZombie(chanID uint64,
3732
3787
"bucket: %w" , err )
3733
3788
}
3734
3789
3735
- graphCache , cacheErr := c .getGraphCache ()
3736
- if cacheErr == nil && graphCache != nil {
3737
- graphCache .RemoveChannel (pubKey1 , pubKey2 , chanID )
3738
- }
3790
+ c .enqueueCacheUpdate (func () {
3791
+ graphCache , cacheErr := c .getGraphCache ()
3792
+ if cacheErr == nil && graphCache != nil {
3793
+ graphCache .RemoveChannel (pubKey1 , pubKey2 , chanID )
3794
+ }
3795
+ })
3739
3796
3740
3797
return markEdgeZombie (zombieIndex , chanID , pubKey1 , pubKey2 )
3741
3798
})
@@ -3825,10 +3882,12 @@ func (c *ChannelGraph) markEdgeLiveUnsafe(tx kvdb.RwTx, chanID uint64) error {
3825
3882
}
3826
3883
3827
3884
for _ , edgeInfo := range edgeInfos {
3828
- graphCache .AddChannel (
3829
- edgeInfo .Info , edgeInfo .Policy1 ,
3830
- edgeInfo .Policy2 ,
3831
- )
3885
+ c .enqueueCacheUpdate (func () {
3886
+ graphCache .AddChannel (
3887
+ edgeInfo .Info , edgeInfo .Policy1 ,
3888
+ edgeInfo .Policy2 ,
3889
+ )
3890
+ })
3832
3891
}
3833
3892
}
3834
3893
0 commit comments