graph/db: let ChannelGraph init the graphCache

In this commit, we let the ChannelGraph be responsible for populating
the graphCache and then passing it to the KVStore. This is a first step
in moving the graphCache completely out of the KVStore layer.
This commit is contained in:
Elle Mouton 2025-02-18 14:23:03 -03:00
parent 00432e46f3
commit 88398e3dd9
No known key found for this signature in database
GPG Key ID: D7D916376026F177
8 changed files with 129 additions and 80 deletions

View File

@ -1030,14 +1030,17 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
graphdb.WithRejectCacheSize(cfg.Caches.RejectCacheSize),
graphdb.WithChannelCacheSize(cfg.Caches.ChannelCacheSize),
graphdb.WithBatchCommitInterval(cfg.DB.BatchCommitInterval),
}
chanGraphOpts := []graphdb.ChanGraphOption{
graphdb.WithUseGraphCache(!cfg.DB.NoGraphCache),
}
// We want to pre-allocate the channel graph cache according to what we
// expect for mainnet to speed up memory allocation.
if cfg.ActiveNetParams.Name == chaincfg.MainNetParams.Name {
graphDBOptions = append(
graphDBOptions, graphdb.WithPreAllocCacheNumNodes(
chanGraphOpts = append(
chanGraphOpts, graphdb.WithPreAllocCacheNumNodes(
graphdb.DefaultPreAllocCacheNumNodes,
),
)
@ -1046,7 +1049,7 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
dbs.GraphDB, err = graphdb.NewChannelGraph(&graphdb.Config{
KVDB: databaseBackends.GraphDB,
KVStoreOpts: graphDBOptions,
})
}, chanGraphOpts...)
if err != nil {
cleanUp()

View File

@ -323,6 +323,7 @@ The underlying functionality between those two options remain the same.
- [Refactor to hide DB transactions](https://github.com/lightningnetwork/lnd/pull/9513)
- Move the graph cache out of the graph CRUD layer:
- [1](https://github.com/lightningnetwork/lnd/pull/9533)
- [2](https://github.com/lightningnetwork/lnd/pull/9545)
* [Golang was updated to
`v1.22.11`](https://github.com/lightningnetwork/lnd/pull/9462).

View File

@ -1,6 +1,13 @@
package graphdb
import "github.com/lightningnetwork/lnd/kvdb"
import (
"time"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
// Config is a struct that holds all the necessary dependencies for a
// ChannelGraph.
@ -20,17 +27,67 @@ type Config struct {
// KVStore. Upcoming commits will move the graph cache out of the KVStore and
// into this layer so that the KVStore is only responsible for CRUD operations.
type ChannelGraph struct {
graphCache *GraphCache
*KVStore
}
// NewChannelGraph creates a new ChannelGraph instance with the given backend.
func NewChannelGraph(cfg *Config) (*ChannelGraph, error) {
func NewChannelGraph(cfg *Config, options ...ChanGraphOption) (*ChannelGraph,
error) {
opts := defaultChanGraphOptions()
for _, o := range options {
o(opts)
}
store, err := NewKVStore(cfg.KVDB, cfg.KVStoreOpts...)
if err != nil {
return nil, err
}
if !opts.useGraphCache {
return &ChannelGraph{
KVStore: store,
}, nil
}
// The graph cache can be turned off (e.g. for mobile users) for a
// speed/memory usage tradeoff.
graphCache := NewGraphCache(opts.preAllocCacheNumNodes)
startTime := time.Now()
log.Debugf("Populating in-memory channel graph, this might take a " +
"while...")
err = store.ForEachNodeCacheable(func(node route.Vertex,
features *lnwire.FeatureVector) error {
graphCache.AddNodeFeatures(node, features)
return nil
})
if err != nil {
return nil, err
}
err = store.ForEachChannel(func(info *models.ChannelEdgeInfo,
policy1, policy2 *models.ChannelEdgePolicy) error {
graphCache.AddChannel(info, policy1, policy2)
return nil
})
if err != nil {
return nil, err
}
log.Debugf("Finished populating in-memory channel graph (took %v, %s)",
time.Since(startTime), graphCache.Stats())
store.setGraphCache(graphCache)
return &ChannelGraph{
KVStore: store,
KVStore: store,
graphCache: graphCache,
}, nil
}

View File

@ -3924,6 +3924,7 @@ func TestGraphCacheForEachNodeChannel(t *testing.T) {
// Unset the channel graph cache to simulate the user running with the
// option turned off.
graph.graphCache = nil
graph.KVStore.graphCache = nil
node1, err := createTestVertex(graph.db)
require.Nil(t, err)

View File

@ -224,43 +224,18 @@ func NewKVStore(db kvdb.Backend, options ...KVStoreOptionModifier) (*KVStore,
db, nil, opts.BatchCommitInterval,
)
// The graph cache can be turned off (e.g. for mobile users) for a
// speed/memory usage tradeoff.
if opts.UseGraphCache {
g.graphCache = NewGraphCache(opts.PreAllocCacheNumNodes)
startTime := time.Now()
log.Debugf("Populating in-memory channel graph, this might " +
"take a while...")
err := g.ForEachNodeCacheable(func(node route.Vertex,
features *lnwire.FeatureVector) error {
g.graphCache.AddNodeFeatures(node, features)
return nil
})
if err != nil {
return nil, err
}
err = g.ForEachChannel(func(info *models.ChannelEdgeInfo,
policy1, policy2 *models.ChannelEdgePolicy) error {
g.graphCache.AddChannel(info, policy1, policy2)
return nil
})
if err != nil {
return nil, err
}
log.Debugf("Finished populating in-memory channel graph (took "+
"%v, %s)", time.Since(startTime), g.graphCache.Stats())
}
return g, nil
}
// setGraphCache sets the KVStore's graphCache.
//
// NOTE: this is temporary and will only be called from the ChannelGraph's
// constructor before the KVStore methods are available to be called. This will
// be removed once the graph cache is fully owned by the ChannelGraph.
func (c *KVStore) setGraphCache(cache *GraphCache) {
c.graphCache = cache
}
// channelMapKey is the key structure used for storing channel edge policies.
type channelMapKey struct {
nodeKey route.Vertex

View File

@ -20,6 +20,47 @@ const (
DefaultPreAllocCacheNumNodes = 15000
)
// chanGraphOptions holds parameters for tuning and customizing the
// ChannelGraph.
type chanGraphOptions struct {
// useGraphCache denotes whether the in-memory graph cache should be
// used or a fallback version that uses the underlying database for
// path finding.
useGraphCache bool
// preAllocCacheNumNodes is the number of nodes we expect to be in the
// graph cache, so we can pre-allocate the map accordingly.
preAllocCacheNumNodes int
}
// defaultChanGraphOptions returns a new chanGraphOptions instance populated
// with default values.
func defaultChanGraphOptions() *chanGraphOptions {
return &chanGraphOptions{
useGraphCache: true,
preAllocCacheNumNodes: DefaultPreAllocCacheNumNodes,
}
}
// ChanGraphOption describes the signature of a functional option that can be
// used to customize a ChannelGraph instance.
type ChanGraphOption func(*chanGraphOptions)
// WithUseGraphCache sets whether the in-memory graph cache should be used.
func WithUseGraphCache(use bool) ChanGraphOption {
return func(o *chanGraphOptions) {
o.useGraphCache = use
}
}
// WithPreAllocCacheNumNodes sets the number of nodes we expect to be in the
// graph cache, so we can pre-allocate the map accordingly.
func WithPreAllocCacheNumNodes(n int) ChanGraphOption {
return func(o *chanGraphOptions) {
o.preAllocCacheNumNodes = n
}
}
// KVStoreOptions holds parameters for tuning and customizing a graph.DB.
type KVStoreOptions struct {
// RejectCacheSize is the maximum number of rejectCacheEntries to hold
@ -34,15 +75,6 @@ type KVStoreOptions struct {
// wait before attempting to commit a pending set of updates.
BatchCommitInterval time.Duration
// PreAllocCacheNumNodes is the number of nodes we expect to be in the
// graph cache, so we can pre-allocate the map accordingly.
PreAllocCacheNumNodes int
// UseGraphCache denotes whether the in-memory graph cache should be
// used or a fallback version that uses the underlying database for
// path finding.
UseGraphCache bool
// NoMigration specifies that underlying backend was opened in read-only
// mode and migrations shouldn't be performed. This can be useful for
// applications that use the channeldb package as a library.
@ -52,11 +84,9 @@ type KVStoreOptions struct {
// DefaultOptions returns a KVStoreOptions populated with default values.
func DefaultOptions() *KVStoreOptions {
return &KVStoreOptions{
RejectCacheSize: DefaultRejectCacheSize,
ChannelCacheSize: DefaultChannelCacheSize,
PreAllocCacheNumNodes: DefaultPreAllocCacheNumNodes,
UseGraphCache: true,
NoMigration: false,
RejectCacheSize: DefaultRejectCacheSize,
ChannelCacheSize: DefaultChannelCacheSize,
NoMigration: false,
}
}
@ -78,13 +108,6 @@ func WithChannelCacheSize(n int) KVStoreOptionModifier {
}
}
// WithPreAllocCacheNumNodes sets the PreAllocCacheNumNodes to n.
func WithPreAllocCacheNumNodes(n int) KVStoreOptionModifier {
return func(o *KVStoreOptions) {
o.PreAllocCacheNumNodes = n
}
}
// WithBatchCommitInterval sets the batch commit interval for the interval batch
// schedulers.
func WithBatchCommitInterval(interval time.Duration) KVStoreOptionModifier {
@ -92,10 +115,3 @@ func WithBatchCommitInterval(interval time.Duration) KVStoreOptionModifier {
o.BatchCommitInterval = interval
}
}
// WithUseGraphCache sets the UseGraphCache option to the given value.
func WithUseGraphCache(use bool) KVStoreOptionModifier {
return func(o *KVStoreOptions) {
o.UseGraphCache = use
}
}

View File

@ -1093,12 +1093,10 @@ func makeTestGraph(t *testing.T, useCache bool) (*graphdb.ChannelGraph,
t.Cleanup(backendCleanup)
graph, err := graphdb.NewChannelGraph(&graphdb.Config{
KVDB: backend,
KVStoreOpts: []graphdb.KVStoreOptionModifier{
graphdb.WithUseGraphCache(useCache),
},
})
graph, err := graphdb.NewChannelGraph(
&graphdb.Config{KVDB: backend},
graphdb.WithUseGraphCache(useCache),
)
if err != nil {
return nil, nil, err
}

View File

@ -166,12 +166,10 @@ func makeTestGraph(t *testing.T, useCache bool) (*graphdb.ChannelGraph,
t.Cleanup(backendCleanup)
graph, err := graphdb.NewChannelGraph(&graphdb.Config{
KVDB: backend,
KVStoreOpts: []graphdb.KVStoreOptionModifier{
graphdb.WithUseGraphCache(useCache),
},
})
graph, err := graphdb.NewChannelGraph(
&graphdb.Config{KVDB: backend},
graphdb.WithUseGraphCache(useCache),
)
if err != nil {
return nil, nil, err
}