multi: extract database initialization
This commit is contained in:
4 files changed
+265
-195
No files matched your search
@@ -3,9 +3,20 @@ package lnd
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btclog"
|
||||
proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
"github.com/lightningnetwork/lnd/lncfg"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
|
||||
"github.com/lightningnetwork/lnd/macaroons"
|
||||
"github.com/lightningnetwork/lnd/watchtower"
|
||||
"github.com/lightningnetwork/lnd/watchtower/wtclient"
|
||||
"github.com/lightningnetwork/lnd/watchtower/wtdb"
|
||||
"google.golang.org/grpc"
|
||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||
)
|
||||
@@ -46,6 +57,15 @@ type ExternalValidator interface {
|
||||
Permissions() map[string][]bakery.Op
|
||||
}
|
||||
|
||||
// DatabaseBuilder is an interface that must be satisfied by the implementation
|
||||
// that provides lnd's main database backend instances.
|
||||
type DatabaseBuilder interface {
|
||||
// BuildDatabase extracts the current databases that we'll use for
|
||||
// normal operation in the daemon. A function closure that closes all
|
||||
// opened databases is also returned.
|
||||
BuildDatabase(ctx context.Context) (*DatabaseInstances, func(), error)
|
||||
}
|
||||
|
||||
// ImplementationCfg is a struct that holds all configuration items for
|
||||
// components that can be implemented outside lnd itself.
|
||||
type ImplementationCfg struct {
|
||||
@@ -60,6 +80,10 @@ type ImplementationCfg struct {
|
||||
// ExternalValidator is a type that can provide external macaroon
|
||||
// validation.
|
||||
ExternalValidator
|
||||
|
||||
// DatabaseBuilder is a type that can provide lnd's main database
|
||||
// backend instances.
|
||||
DatabaseBuilder
|
||||
}
|
||||
|
||||
// DefaultWalletImpl is the default implementation of our normal, btcwallet
|
||||
@@ -113,3 +137,209 @@ func (d *DefaultWalletImpl) ValidateMacaroon(ctx context.Context,
|
||||
func (d *DefaultWalletImpl) Permissions() map[string][]bakery.Op {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DatabaseInstances is a struct that holds all instances to the actual
|
||||
// databases that are used in lnd.
|
||||
type DatabaseInstances struct {
|
||||
// GraphDB is the database that stores the channel graph used for path
|
||||
// finding.
|
||||
//
|
||||
// NOTE/TODO: This currently _needs_ to be the same instance as the
|
||||
// ChanStateDB below until the separation of the two databases is fully
|
||||
// complete!
|
||||
GraphDB *channeldb.DB
|
||||
|
||||
// ChanStateDB is the database that stores all of our node's channel
|
||||
// state.
|
||||
//
|
||||
// NOTE/TODO: This currently _needs_ to be the same instance as the
|
||||
// GraphDB above until the separation of the two databases is fully
|
||||
// complete!
|
||||
ChanStateDB *channeldb.DB
|
||||
|
||||
// HeightHintDB is the database that stores height hints for spends.
|
||||
HeightHintDB kvdb.Backend
|
||||
|
||||
// MacaroonDB is the database that stores macaroon root keys.
|
||||
MacaroonDB kvdb.Backend
|
||||
|
||||
// DecayedLogDB is the database that stores p2p related encryption
|
||||
// information.
|
||||
DecayedLogDB kvdb.Backend
|
||||
|
||||
// TowerClientDB is the database that stores the watchtower client's
|
||||
// configuration.
|
||||
TowerClientDB wtclient.DB
|
||||
|
||||
// TowerServerDB is the database that stores the watchtower server's
|
||||
// configuration.
|
||||
TowerServerDB watchtower.DB
|
||||
|
||||
// WalletDB is the configuration for loading the wallet database using
|
||||
// the btcwallet's loader.
|
||||
WalletDB btcwallet.LoaderOption
|
||||
}
|
||||
|
||||
// DefaultDatabaseBuilder is a type that builds the default database backends
|
||||
// for lnd, using the given configuration to decide what actual implementation
|
||||
// to use.
|
||||
type DefaultDatabaseBuilder struct {
|
||||
cfg *Config
|
||||
logger btclog.Logger
|
||||
}
|
||||
|
||||
// NewDefaultDatabaseBuilder returns a new instance of the default database
|
||||
// builder.
|
||||
func NewDefaultDatabaseBuilder(cfg *Config,
|
||||
logger btclog.Logger) *DefaultDatabaseBuilder {
|
||||
|
||||
return &DefaultDatabaseBuilder{
|
||||
cfg: cfg,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// BuildDatabase extracts the current databases that we'll use for normal
|
||||
// operation in the daemon. A function closure that closes all opened databases
|
||||
// is also returned.
|
||||
func (d *DefaultDatabaseBuilder) BuildDatabase(
|
||||
ctx context.Context) (*DatabaseInstances, func(), error) {
|
||||
|
||||
d.logger.Infof("Opening the main database, this might take a few " +
|
||||
"minutes...")
|
||||
|
||||
cfg := d.cfg
|
||||
if cfg.DB.Backend == lncfg.BoltBackend {
|
||||
d.logger.Infof("Opening bbolt database, sync_freelist=%v, "+
|
||||
"auto_compact=%v", !cfg.DB.Bolt.NoFreelistSync,
|
||||
cfg.DB.Bolt.AutoCompact)
|
||||
}
|
||||
|
||||
startOpenTime := time.Now()
|
||||
|
||||
databaseBackends, err := cfg.DB.GetBackends(
|
||||
ctx, cfg.graphDatabaseDir(), cfg.networkDir, filepath.Join(
|
||||
cfg.Watchtower.TowerDir,
|
||||
cfg.registeredChains.PrimaryChain().String(),
|
||||
lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
|
||||
), cfg.WtClient.Active, cfg.Watchtower.Active,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to obtain database "+
|
||||
"backends: %v", err)
|
||||
}
|
||||
|
||||
// With the full remote mode we made sure both the graph and channel
|
||||
// state DB point to the same local or remote DB and the same namespace
|
||||
// within that DB.
|
||||
dbs := &DatabaseInstances{
|
||||
HeightHintDB: databaseBackends.HeightHintDB,
|
||||
MacaroonDB: databaseBackends.MacaroonDB,
|
||||
DecayedLogDB: databaseBackends.DecayedLogDB,
|
||||
WalletDB: databaseBackends.WalletDB,
|
||||
}
|
||||
cleanUp := func() {
|
||||
// We can just close the returned close functions directly. Even
|
||||
// if we decorate the channel DB with an additional struct, its
|
||||
// close function still just points to the kvdb backend.
|
||||
for name, closeFunc := range databaseBackends.CloseFuncs {
|
||||
if err := closeFunc(); err != nil {
|
||||
d.logger.Errorf("Error closing %s "+
|
||||
"database: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if databaseBackends.Remote {
|
||||
d.logger.Infof("Using remote %v database! Creating "+
|
||||
"graph and channel state DB instances", cfg.DB.Backend)
|
||||
} else {
|
||||
d.logger.Infof("Creating local graph and channel state DB " +
|
||||
"instances")
|
||||
}
|
||||
|
||||
dbOptions := []channeldb.OptionModifier{
|
||||
channeldb.OptionSetRejectCacheSize(cfg.Caches.RejectCacheSize),
|
||||
channeldb.OptionSetChannelCacheSize(cfg.Caches.ChannelCacheSize),
|
||||
channeldb.OptionSetBatchCommitInterval(cfg.DB.BatchCommitInterval),
|
||||
channeldb.OptionDryRunMigration(cfg.DryRunMigration),
|
||||
}
|
||||
|
||||
// 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 {
|
||||
dbOptions = append(
|
||||
dbOptions, channeldb.OptionSetPreAllocCacheNumNodes(
|
||||
channeldb.DefaultPreAllocCacheNumNodes,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Otherwise, we'll open two instances, one for the state we only need
|
||||
// locally, and the other for things we want to ensure are replicated.
|
||||
dbs.GraphDB, err = channeldb.CreateWithBackend(
|
||||
databaseBackends.GraphDB, dbOptions...,
|
||||
)
|
||||
switch {
|
||||
// Give the DB a chance to dry run the migration. Since we know that
|
||||
// both the channel state and graph DBs are still always behind the same
|
||||
// backend, we know this would be applied to both of those DBs.
|
||||
case err == channeldb.ErrDryRunMigrationOK:
|
||||
d.logger.Infof("Graph DB dry run migration successful")
|
||||
return nil, nil, err
|
||||
|
||||
case err != nil:
|
||||
cleanUp()
|
||||
|
||||
err := fmt.Errorf("unable to open graph DB: %v", err)
|
||||
d.logger.Error(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// For now, we don't _actually_ split the graph and channel state DBs on
|
||||
// the code level. Since they both are based upon the *channeldb.DB
|
||||
// struct it will require more refactoring to fully separate them. With
|
||||
// the full remote mode we at least know for now that they both point to
|
||||
// the same DB backend (and also namespace within that) so we only need
|
||||
// to apply any migration once.
|
||||
//
|
||||
// TODO(guggero): Once the full separation of anything graph related
|
||||
// from the channeldb.DB is complete, the decorated instance of the
|
||||
// channel state DB should be created here individually instead of just
|
||||
// using the same struct (and DB backend) instance.
|
||||
dbs.ChanStateDB = dbs.GraphDB
|
||||
|
||||
// Wrap the watchtower client DB and make sure we clean up.
|
||||
if cfg.WtClient.Active {
|
||||
dbs.TowerClientDB, err = wtdb.OpenClientDB(
|
||||
databaseBackends.TowerClientDB,
|
||||
)
|
||||
if err != nil {
|
||||
cleanUp()
|
||||
|
||||
err := fmt.Errorf("unable to open %s database: %v",
|
||||
lncfg.NSTowerClientDB, err)
|
||||
d.logger.Error(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the watchtower server DB and make sure we clean up.
|
||||
if cfg.Watchtower.Active {
|
||||
dbs.TowerServerDB, err = wtdb.OpenTowerDB(
|
||||
databaseBackends.TowerServerDB,
|
||||
)
|
||||
if err != nil {
|
||||
cleanUp()
|
||||
|
||||
err := fmt.Errorf("unable to open %s database: %v",
|
||||
lncfg.NSTowerServerDB, err)
|
||||
d.logger.Error(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
openTime := time.Since(startOpenTime)
|
||||
d.logger.Infof("Database(s) now open (time_to_open=%v)!", openTime)
|
||||
|
||||
return dbs, cleanUp, nil
|
||||
}
|
||||
Reference in new issue
Block a user