Skip to content

Commit d67e6d5

Browse files
authored
Merge pull request #6111 from bottlepay/cache-loading
kvdb+channeldb: speed up graph cache
2 parents 408e40f + 352008a commit d67e6d5

File tree

12 files changed

+383
-87
lines changed

12 files changed

+383
-87
lines changed

channeldb/graph.go

Lines changed: 155 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -216,22 +216,101 @@ func NewChannelGraph(db kvdb.Backend, rejectCacheSize, chanCacheSize int,
216216
startTime := time.Now()
217217
log.Debugf("Populating in-memory channel graph, this might " +
218218
"take a while...")
219+
219220
err := g.ForEachNodeCacheable(
220221
func(tx kvdb.RTx, node GraphCacheNode) error {
221-
return g.graphCache.AddNode(tx, node)
222+
g.graphCache.AddNodeFeatures(node)
223+
224+
return nil
222225
},
223226
)
224227
if err != nil {
225228
return nil, err
226229
}
227230

231+
err = g.ForEachChannel(func(info *ChannelEdgeInfo,
232+
policy1, policy2 *ChannelEdgePolicy) error {
233+
234+
g.graphCache.AddChannel(info, policy1, policy2)
235+
236+
return nil
237+
})
238+
if err != nil {
239+
return nil, err
240+
}
241+
228242
log.Debugf("Finished populating in-memory channel graph (took "+
229243
"%v, %s)", time.Since(startTime), g.graphCache.Stats())
230244
}
231245

232246
return g, nil
233247
}
234248

249+
// channelMapKey is the key structure used for storing channel edge policies.
250+
type channelMapKey struct {
251+
nodeKey route.Vertex
252+
chanID [8]byte
253+
}
254+
255+
// getChannelMap loads all channel edge policies from the database and stores
256+
// them in a map.
257+
func (c *ChannelGraph) getChannelMap(edges kvdb.RBucket) (
258+
map[channelMapKey]*ChannelEdgePolicy, error) {
259+
260+
// Create a map to store all channel edge policies.
261+
channelMap := make(map[channelMapKey]*ChannelEdgePolicy)
262+
263+
err := kvdb.ForAll(edges, func(k, edgeBytes []byte) error {
264+
// Skip embedded buckets.
265+
if bytes.Equal(k, edgeIndexBucket) ||
266+
bytes.Equal(k, edgeUpdateIndexBucket) ||
267+
bytes.Equal(k, zombieBucket) ||
268+
bytes.Equal(k, disabledEdgePolicyBucket) ||
269+
bytes.Equal(k, channelPointBucket) {
270+
271+
return nil
272+
}
273+
274+
// Validate key length.
275+
if len(k) != 33+8 {
276+
return fmt.Errorf("invalid edge key %x encountered", k)
277+
}
278+
279+
var key channelMapKey
280+
copy(key.nodeKey[:], k[:33])
281+
copy(key.chanID[:], k[33:])
282+
283+
// No need to deserialize unknown policy.
284+
if bytes.Equal(edgeBytes, unknownPolicy) {
285+
return nil
286+
}
287+
288+
edgeReader := bytes.NewReader(edgeBytes)
289+
edge, err := deserializeChanEdgePolicyRaw(
290+
edgeReader,
291+
)
292+
293+
switch {
294+
// If the db policy was missing an expected optional field, we
295+
// return nil as if the policy was unknown.
296+
case err == ErrEdgePolicyOptionalFieldNotFound:
297+
return nil
298+
299+
case err != nil:
300+
return err
301+
}
302+
303+
channelMap[key] = edge
304+
305+
return nil
306+
})
307+
if err != nil {
308+
return nil, err
309+
}
310+
311+
return channelMap, nil
312+
}
313+
235314
var graphTopLevelBuckets = [][]byte{
236315
nodeBucket,
237316
edgeBucket,
@@ -332,50 +411,47 @@ func (c *ChannelGraph) NewPathFindTx() (kvdb.RTx, error) {
332411
func (c *ChannelGraph) ForEachChannel(cb func(*ChannelEdgeInfo,
333412
*ChannelEdgePolicy, *ChannelEdgePolicy) error) error {
334413

335-
// TODO(roasbeef): ptr map to reduce # of allocs? no duplicates
336-
337-
return kvdb.View(c.db, func(tx kvdb.RTx) error {
338-
// First, grab the node bucket. This will be used to populate
339-
// the Node pointers in each edge read from disk.
340-
nodes := tx.ReadBucket(nodeBucket)
341-
if nodes == nil {
342-
return ErrGraphNotFound
343-
}
344-
345-
// Next, grab the edge bucket which stores the edges, and also
346-
// the index itself so we can group the directed edges together
347-
// logically.
414+
return c.db.View(func(tx kvdb.RTx) error {
348415
edges := tx.ReadBucket(edgeBucket)
349416
if edges == nil {
350417
return ErrGraphNoEdgesFound
351418
}
419+
420+
// First, load all edges in memory indexed by node and channel
421+
// id.
422+
channelMap, err := c.getChannelMap(edges)
423+
if err != nil {
424+
return err
425+
}
426+
352427
edgeIndex := edges.NestedReadBucket(edgeIndexBucket)
353428
if edgeIndex == nil {
354429
return ErrGraphNoEdgesFound
355430
}
356431

357-
// For each edge pair within the edge index, we fetch each edge
358-
// itself and also the node information in order to fully
359-
// populated the object.
360-
return edgeIndex.ForEach(func(chanID, edgeInfoBytes []byte) error {
361-
infoReader := bytes.NewReader(edgeInfoBytes)
362-
edgeInfo, err := deserializeChanEdgeInfo(infoReader)
363-
if err != nil {
364-
return err
365-
}
366-
edgeInfo.db = c.db
432+
// Load edge index, recombine each channel with the policies
433+
// loaded above and invoke the callback.
434+
return kvdb.ForAll(edgeIndex, func(k, edgeInfoBytes []byte) error {
435+
var chanID [8]byte
436+
copy(chanID[:], k)
367437

368-
edge1, edge2, err := fetchChanEdgePolicies(
369-
edgeIndex, edges, nodes, chanID, c.db,
370-
)
438+
edgeInfoReader := bytes.NewReader(edgeInfoBytes)
439+
info, err := deserializeChanEdgeInfo(edgeInfoReader)
371440
if err != nil {
372441
return err
373442
}
374443

375-
// With both edges read, execute the call back. IF this
376-
// function returns an error then the transaction will
377-
// be aborted.
378-
return cb(&edgeInfo, edge1, edge2)
444+
policy1 := channelMap[channelMapKey{
445+
nodeKey: info.NodeKey1Bytes,
446+
chanID: chanID,
447+
}]
448+
449+
policy2 := channelMap[channelMapKey{
450+
nodeKey: info.NodeKey2Bytes,
451+
chanID: chanID,
452+
}]
453+
454+
return cb(&info, policy1, policy2)
379455
})
380456
}, func() {})
381457
}
@@ -628,7 +704,6 @@ func (c *ChannelGraph) ForEachNodeCacheable(cb func(kvdb.RTx,
628704
return ErrGraphNotFound
629705
}
630706

631-
cacheableNode := newGraphCacheNode(route.Vertex{}, nil)
632707
return nodes.ForEach(func(pubKey, nodeBytes []byte) error {
633708
// If this is the source key, then we skip this
634709
// iteration as the value for this key is a pubKey
@@ -638,8 +713,8 @@ func (c *ChannelGraph) ForEachNodeCacheable(cb func(kvdb.RTx,
638713
}
639714

640715
nodeReader := bytes.NewReader(nodeBytes)
641-
err := deserializeLightningNodeCacheable(
642-
nodeReader, cacheableNode,
716+
cacheableNode, err := deserializeLightningNodeCacheable(
717+
nodeReader,
643718
)
644719
if err != nil {
645720
return err
@@ -2740,8 +2815,6 @@ func (c *ChannelGraph) FetchLightningNode(nodePub route.Vertex) (
27402815
type graphCacheNode struct {
27412816
pubKeyBytes route.Vertex
27422817
features *lnwire.FeatureVector
2743-
2744-
nodeScratch [8]byte
27452818
}
27462819

27472820
// newGraphCacheNode returns a new cache optimized node.
@@ -4090,51 +4163,60 @@ func fetchLightningNode(nodeBucket kvdb.RBucket,
40904163
return deserializeLightningNode(nodeReader)
40914164
}
40924165

4093-
func deserializeLightningNodeCacheable(r io.Reader, node *graphCacheNode) error {
4166+
func deserializeLightningNodeCacheable(r io.Reader) (*graphCacheNode, error) {
40944167
// Always populate a feature vector, even if we don't have a node
40954168
// announcement and short circuit below.
4096-
node.features = lnwire.EmptyFeatureVector()
4169+
node := newGraphCacheNode(
4170+
route.Vertex{},
4171+
lnwire.EmptyFeatureVector(),
4172+
)
4173+
4174+
var nodeScratch [8]byte
40974175

40984176
// Skip ahead:
40994177
// - LastUpdate (8 bytes)
4100-
if _, err := r.Read(node.nodeScratch[:]); err != nil {
4101-
return err
4178+
if _, err := r.Read(nodeScratch[:]); err != nil {
4179+
return nil, err
41024180
}
41034181

41044182
if _, err := io.ReadFull(r, node.pubKeyBytes[:]); err != nil {
4105-
return err
4183+
return nil, err
41064184
}
41074185

41084186
// Read the node announcement flag.
4109-
if _, err := r.Read(node.nodeScratch[:2]); err != nil {
4110-
return err
4187+
if _, err := r.Read(nodeScratch[:2]); err != nil {
4188+
return nil, err
41114189
}
4112-
hasNodeAnn := byteOrder.Uint16(node.nodeScratch[:2])
4190+
hasNodeAnn := byteOrder.Uint16(nodeScratch[:2])
41134191

41144192
// The rest of the data is optional, and will only be there if we got a
41154193
// node announcement for this node.
41164194
if hasNodeAnn == 0 {
4117-
return nil
4195+
return node, nil
41184196
}
41194197

41204198
// We did get a node announcement for this node, so we'll have the rest
41214199
// of the data available.
41224200
var rgb uint8
41234201
if err := binary.Read(r, byteOrder, &rgb); err != nil {
4124-
return err
4202+
return nil, err
41254203
}
41264204
if err := binary.Read(r, byteOrder, &rgb); err != nil {
4127-
return err
4205+
return nil, err
41284206
}
41294207
if err := binary.Read(r, byteOrder, &rgb); err != nil {
4130-
return err
4208+
return nil, err
41314209
}
41324210

41334211
if _, err := wire.ReadVarString(r, 0); err != nil {
4134-
return err
4212+
return nil, err
4213+
}
4214+
4215+
if err := node.features.Decode(r); err != nil {
4216+
return nil, err
41354217
}
41364218

4137-
return node.features.Decode(r)
4219+
return node, nil
41384220
}
41394221

41404222
func deserializeLightningNode(r io.Reader) (LightningNode, error) {
@@ -4652,6 +4734,27 @@ func serializeChanEdgePolicy(w io.Writer, edge *ChannelEdgePolicy,
46524734
func deserializeChanEdgePolicy(r io.Reader,
46534735
nodes kvdb.RBucket) (*ChannelEdgePolicy, error) {
46544736

4737+
// Deserialize the policy. Note that in case an optional field is not
4738+
// found, both an error and a populated policy object are returned.
4739+
edge, deserializeErr := deserializeChanEdgePolicyRaw(r)
4740+
if deserializeErr != nil &&
4741+
deserializeErr != ErrEdgePolicyOptionalFieldNotFound {
4742+
4743+
return nil, deserializeErr
4744+
}
4745+
4746+
// Populate full LightningNode struct.
4747+
pub := edge.Node.PubKeyBytes[:]
4748+
node, err := fetchLightningNode(nodes, pub)
4749+
if err != nil {
4750+
return nil, fmt.Errorf("unable to fetch node: %x, %v", pub, err)
4751+
}
4752+
edge.Node = &node
4753+
4754+
return edge, deserializeErr
4755+
}
4756+
4757+
func deserializeChanEdgePolicyRaw(r io.Reader) (*ChannelEdgePolicy, error) {
46554758
edge := &ChannelEdgePolicy{}
46564759

46574760
var err error
@@ -4701,13 +4804,9 @@ func deserializeChanEdgePolicy(r io.Reader,
47014804
if _, err := r.Read(pub[:]); err != nil {
47024805
return nil, err
47034806
}
4704-
4705-
node, err := fetchLightningNode(nodes, pub[:])
4706-
if err != nil {
4707-
return nil, fmt.Errorf("unable to fetch node: %x, %v",
4708-
pub[:], err)
4807+
edge.Node = &LightningNode{
4808+
PubKeyBytes: pub,
47094809
}
4710-
edge.Node = &node
47114810

47124811
// We'll try and see if there are any opaque bytes left, if not, then
47134812
// we'll ignore the EOF error and return the edge as is.

channeldb/graph_cache.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,8 @@ func (c *GraphCache) Stats() string {
205205
numChannels)
206206
}
207207

208-
// AddNode adds a graph node, including all the (directed) channels of that
209-
// node.
210-
func (c *GraphCache) AddNode(tx kvdb.RTx, node GraphCacheNode) error {
208+
// AddNodeFeatures adds a graph node and its features to the cache.
209+
func (c *GraphCache) AddNodeFeatures(node GraphCacheNode) {
211210
nodePubKey := node.PubKey()
212211

213212
// Only hold the lock for a short time. The `ForEachChannel()` below is
@@ -217,6 +216,12 @@ func (c *GraphCache) AddNode(tx kvdb.RTx, node GraphCacheNode) error {
217216
c.mtx.Lock()
218217
c.nodeFeatures[nodePubKey] = node.Features()
219218
c.mtx.Unlock()
219+
}
220+
221+
// AddNode adds a graph node, including all the (directed) channels of that
222+
// node.
223+
func (c *GraphCache) AddNode(tx kvdb.RTx, node GraphCacheNode) error {
224+
c.AddNodeFeatures(node)
220225

221226
return node.ForEachChannel(
222227
tx, func(tx kvdb.RTx, info *ChannelEdgeInfo,

0 commit comments

Comments
 (0)