• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 14495430837

16 Apr 2025 02:36PM UTC coverage: 58.591% (-10.5%) from 69.084%
14495430837

Pull #9723

github

web-flow
Merge fee68593a into 06f1ef47f
Pull Request #9723: Add the global lock for the wallet db back for postgres

0 of 11 new or added lines in 1 file covered. (0.0%)

28188 existing lines in 450 files now uncovered.

97163 of 165833 relevant lines covered (58.59%)

1.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

25.0
/lncfg/db.go
1
package lncfg
2

3
import (
4
        "context"
5
        "fmt"
6
        "path"
7
        "path/filepath"
8
        "time"
9

10
        "github.com/btcsuite/btclog/v2"
11
        "github.com/lightningnetwork/lnd/kvdb"
12
        "github.com/lightningnetwork/lnd/kvdb/etcd"
13
        "github.com/lightningnetwork/lnd/kvdb/postgres"
14
        "github.com/lightningnetwork/lnd/kvdb/sqlbase"
15
        "github.com/lightningnetwork/lnd/kvdb/sqlite"
16
        "github.com/lightningnetwork/lnd/lnrpc"
17
        "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
18
        "github.com/lightningnetwork/lnd/sqldb"
19
)
20

21
const (
22
        ChannelDBName     = "channel.db"
23
        MacaroonDBName    = "macaroons.db"
24
        DecayedLogDbName  = "sphinxreplay.db"
25
        TowerClientDBName = "wtclient.db"
26
        TowerServerDBName = "watchtower.db"
27
        WalletDBName      = "wallet.db"
28
        NeutrinoDBName    = "neutrino.db"
29

30
        SqliteChannelDBName  = "channel.sqlite"
31
        SqliteChainDBName    = "chain.sqlite"
32
        SqliteNeutrinoDBName = "neutrino.sqlite"
33
        SqliteTowerDBName    = "watchtower.sqlite"
34
        SqliteNativeDBName   = "lnd.sqlite"
35

36
        BoltBackend                = "bolt"
37
        EtcdBackend                = "etcd"
38
        PostgresBackend            = "postgres"
39
        SqliteBackend              = "sqlite"
40
        DefaultBatchCommitInterval = 500 * time.Millisecond
41

42
        defaultPostgresMaxConnections = 50
43
        defaultSqliteMaxConnections   = 2
44

45
        defaultSqliteBusyTimeout = 5 * time.Second
46

47
        // NSChannelDB is the namespace name that we use for the combined graph
48
        // and channel state DB.
49
        NSChannelDB = "channeldb"
50

51
        // NSMacaroonDB is the namespace name that we use for the macaroon DB.
52
        NSMacaroonDB = "macaroondb"
53

54
        // NSDecayedLogDB is the namespace name that we use for the sphinx
55
        // replay a.k.a. decayed log DB.
56
        NSDecayedLogDB = "decayedlogdb"
57

58
        // NSTowerClientDB is the namespace name that we use for the watchtower
59
        // client DB.
60
        NSTowerClientDB = "towerclientdb"
61

62
        // NSTowerServerDB is the namespace name that we use for the watchtower
63
        // server DB.
64
        NSTowerServerDB = "towerserverdb"
65

66
        // NSWalletDB is the namespace name that we use for the wallet DB.
67
        NSWalletDB = "walletdb"
68

69
        // NSNeutrinoDB is the namespace name that we use for the neutrino DB.
70
        NSNeutrinoDB = "neutrinodb"
71
)
72

73
// DB holds database configuration for LND.
74
//
75
//nolint:ll
76
type DB struct {
77
        Backend string `long:"backend" description:"The selected database backend."`
78

79
        BatchCommitInterval time.Duration `long:"batch-commit-interval" description:"The maximum duration the channel graph batch schedulers will wait before attempting to commit a batch of pending updates. This can be tradeoff database contenion for commit latency."`
80

81
        Etcd *etcd.Config `group:"etcd" namespace:"etcd" description:"Etcd settings."`
82

83
        Bolt *kvdb.BoltConfig `group:"bolt" namespace:"bolt" description:"Bolt settings."`
84

85
        Postgres *sqldb.PostgresConfig `group:"postgres" namespace:"postgres" description:"Postgres settings."`
86

87
        Sqlite *sqldb.SqliteConfig `group:"sqlite" namespace:"sqlite" description:"Sqlite settings."`
88

89
        UseNativeSQL bool `long:"use-native-sql" description:"Use native SQL for tables that already support it."`
90

91
        SkipNativeSQLMigration bool `long:"skip-native-sql-migration" description:"Do not run the KV to native SQL migration. It should only be used if errors are encountered normally."`
92

93
        NoGraphCache bool `long:"no-graph-cache" description:"Don't use the in-memory graph cache for path finding. Much slower but uses less RAM. Can only be used with a bolt database backend."`
94

95
        PruneRevocation bool `long:"prune-revocation" description:"Run the optional migration that prunes the revocation logs to save disk space."`
96

97
        NoRevLogAmtData bool `long:"no-rev-log-amt-data" description:"If set, the to-local and to-remote output amounts of revoked commitment transactions will not be stored in the revocation log. Note that once this data is lost, a watchtower client will not be able to back up the revoked state."`
98
}
99

100
// DefaultDB creates and returns a new default DB config.
101
func DefaultDB() *DB {
3✔
102
        return &DB{
3✔
103
                Backend:             BoltBackend,
3✔
104
                BatchCommitInterval: DefaultBatchCommitInterval,
3✔
105
                Bolt: &kvdb.BoltConfig{
3✔
106
                        NoFreelistSync:    true,
3✔
107
                        AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge,
3✔
108
                        DBTimeout:         kvdb.DefaultDBTimeout,
3✔
109
                },
3✔
110
                Etcd: &etcd.Config{
3✔
111
                        // Allow at most 32 MiB messages by default.
3✔
112
                        MaxMsgSize: 32768 * 1024,
3✔
113
                },
3✔
114
                Postgres: &sqldb.PostgresConfig{
3✔
115
                        MaxConnections: defaultPostgresMaxConnections,
3✔
116
                },
3✔
117
                Sqlite: &sqldb.SqliteConfig{
3✔
118
                        MaxConnections: defaultSqliteMaxConnections,
3✔
119
                        BusyTimeout:    defaultSqliteBusyTimeout,
3✔
120
                },
3✔
121
                UseNativeSQL:           false,
3✔
122
                SkipNativeSQLMigration: false,
3✔
123
        }
3✔
124
}
3✔
125

126
// Validate validates the DB config.
127
func (db *DB) Validate() error {
3✔
128
        switch db.Backend {
3✔
129
        case BoltBackend:
3✔
130
                if db.UseNativeSQL {
3✔
131
                        return fmt.Errorf("cannot use native SQL with bolt " +
×
132
                                "backend")
×
133
                }
×
134

135
        case SqliteBackend:
×
136
        case PostgresBackend:
×
137
                if err := db.Postgres.Validate(); err != nil {
×
138
                        return err
×
139
                }
×
140

141
        case EtcdBackend:
×
142
                if db.UseNativeSQL {
×
143
                        return fmt.Errorf("cannot use native SQL with etcd " +
×
144
                                "backend")
×
145
                }
×
146

147
                if !db.Etcd.Embedded && db.Etcd.Host == "" {
×
148
                        return fmt.Errorf("etcd host must be set")
×
149
                }
×
150

151
        default:
×
152
                return fmt.Errorf("unknown backend, must be either '%v', "+
×
153
                        "'%v', '%v' or '%v'", BoltBackend, EtcdBackend,
×
154
                        PostgresBackend, SqliteBackend)
×
155
        }
156

157
        // The path finding uses a manual read transaction that's open for a
158
        // potentially long time. That works fine with the locking model of
159
        // bbolt but can lead to locks or rolled back transactions with etcd or
160
        // postgres. And since we already have a smaller memory footprint for
161
        // remote database setups (due to not needing to memory-map the bbolt DB
162
        // files), we can keep the graph in memory instead. But for mobile
163
        // devices the tradeoff between a smaller memory footprint and the
164
        // longer time needed for path finding might be a desirable one.
165
        if db.NoGraphCache && db.Backend != BoltBackend {
3✔
166
                return fmt.Errorf("cannot use no-graph-cache with database "+
×
167
                        "backend '%v'", db.Backend)
×
168
        }
×
169

170
        return nil
3✔
171
}
172

173
// Init should be called upon start to pre-initialize database access dependent
174
// on configuration.
175
func (db *DB) Init(ctx context.Context, dbPath string) error {
3✔
176
        // Start embedded etcd server if requested.
3✔
177
        switch {
3✔
178
        case db.Backend == EtcdBackend && db.Etcd.Embedded:
×
179
                cfg, _, err := kvdb.StartEtcdTestBackend(
×
180
                        dbPath, db.Etcd.EmbeddedClientPort,
×
181
                        db.Etcd.EmbeddedPeerPort, db.Etcd.EmbeddedLogFile,
×
182
                )
×
183
                if err != nil {
×
184
                        return err
×
185
                }
×
186

187
                // Override the original config with the config for
188
                // the embedded instance.
189
                db.Etcd = cfg
×
190

191
        case db.Backend == PostgresBackend:
×
192
                sqlbase.Init(db.Postgres.MaxConnections)
×
193

194
        case db.Backend == SqliteBackend:
×
195
                sqlbase.Init(db.Sqlite.MaxConnections)
×
196
        }
197

198
        return nil
3✔
199
}
200

201
// DatabaseBackends is a two-tuple that holds the set of active database
202
// backends for the daemon. The two backends we expose are the graph database
203
// backend, and the channel state backend.
204
type DatabaseBackends struct {
205
        // GraphDB points to the database backend that contains the less
206
        // critical data that is accessed often, such as the channel graph and
207
        // chain height hints.
208
        GraphDB kvdb.Backend
209

210
        // ChanStateDB points to a possibly networked replicated backend that
211
        // contains the critical channel state related data.
212
        ChanStateDB kvdb.Backend
213

214
        // HeightHintDB points to a possibly networked replicated backend that
215
        // contains the chain height hint related data.
216
        HeightHintDB kvdb.Backend
217

218
        // MacaroonDB points to a database backend that stores the macaroon root
219
        // keys.
220
        MacaroonDB kvdb.Backend
221

222
        // DecayedLogDB points to a database backend that stores the decayed log
223
        // data.
224
        DecayedLogDB kvdb.Backend
225

226
        // TowerClientDB points to a database backend that stores the watchtower
227
        // client data. This might be nil if the watchtower client is disabled.
228
        TowerClientDB kvdb.Backend
229

230
        // TowerServerDB points to a database backend that stores the watchtower
231
        // server data. This might be nil if the watchtower server is disabled.
232
        TowerServerDB kvdb.Backend
233

234
        // WalletDB is an option that instructs the wallet loader where to load
235
        // the underlying wallet database from.
236
        WalletDB btcwallet.LoaderOption
237

238
        // NativeSQLStore holds a reference to the native SQL store that can
239
        // be used for native SQL queries for tables that already support it.
240
        // This may be nil if the use-native-sql flag was not set.
241
        NativeSQLStore sqldb.DB
242

243
        // Remote indicates whether the database backends are remote, possibly
244
        // replicated instances or local bbolt or sqlite backed databases.
245
        Remote bool
246

247
        // CloseFuncs is a map of close functions for each of the initialized
248
        // DB backends keyed by their namespace name.
249
        CloseFuncs map[string]func() error
250
}
251

252
// GetPostgresConfigKVDB converts a sqldb.PostgresConfig to a kvdb
253
// postgres.Config.
254
func GetPostgresConfigKVDB(cfg *sqldb.PostgresConfig) *postgres.Config {
×
255
        return &postgres.Config{
×
256
                Dsn:            cfg.Dsn,
×
257
                Timeout:        cfg.Timeout,
×
258
                MaxConnections: cfg.MaxConnections,
×
259
        }
×
260
}
×
261

262
// GetSqliteConfigKVDB converts a sqldb.SqliteConfig to a kvdb sqlite.Config.
263
func GetSqliteConfigKVDB(cfg *sqldb.SqliteConfig) *sqlite.Config {
×
264
        return &sqlite.Config{
×
265
                Timeout:        cfg.Timeout,
×
266
                BusyTimeout:    cfg.BusyTimeout,
×
267
                MaxConnections: cfg.MaxConnections,
×
268
                PragmaOptions:  cfg.PragmaOptions,
×
269
        }
×
270
}
×
271

272
// GetBackends returns a set of kvdb.Backends as set in the DB config.
273
func (db *DB) GetBackends(ctx context.Context, chanDBPath,
274
        walletDBPath, towerServerDBPath string, towerClientEnabled,
275
        towerServerEnabled bool, logger btclog.Logger) (*DatabaseBackends,
276
        error) {
3✔
277

3✔
278
        // We keep track of all the kvdb backends we actually open and return a
3✔
279
        // reference to their close function so they can be cleaned up properly
3✔
280
        // on error or shutdown.
3✔
281
        closeFuncs := make(map[string]func() error)
3✔
282

3✔
283
        // If we need to return early because of an error, we invoke any close
3✔
284
        // function that has been initialized so far.
3✔
285
        returnEarly := true
3✔
286
        defer func() {
6✔
287
                if !returnEarly {
6✔
288
                        return
3✔
289
                }
3✔
290

291
                for _, closeFunc := range closeFuncs {
×
292
                        _ = closeFunc()
×
293
                }
×
294
        }()
295

296
        switch db.Backend {
3✔
297
        case EtcdBackend:
×
298
                // As long as the graph data, channel state and height hint
×
299
                // cache are all still in the channel.db file in bolt, we
×
300
                // replicate the same behavior here and use the same etcd
×
301
                // backend for those three sub DBs. But we namespace it properly
×
302
                // to make such a split even easier in the future. This will
×
303
                // break lnd for users that ran on etcd with 0.13.x since that
×
304
                // code used the root namespace. We assume that nobody used etcd
×
305
                // for mainnet just yet since that feature was clearly marked as
×
306
                // experimental in 0.13.x.
×
307
                etcdBackend, err := kvdb.Open(
×
308
                        kvdb.EtcdBackendName, ctx,
×
309
                        db.Etcd.CloneWithSubNamespace(NSChannelDB),
×
310
                )
×
311
                if err != nil {
×
312
                        return nil, fmt.Errorf("error opening etcd DB: %w", err)
×
313
                }
×
314
                closeFuncs[NSChannelDB] = etcdBackend.Close
×
315

×
316
                etcdMacaroonBackend, err := kvdb.Open(
×
317
                        kvdb.EtcdBackendName, ctx,
×
318
                        db.Etcd.CloneWithSubNamespace(NSMacaroonDB),
×
319
                )
×
320
                if err != nil {
×
321
                        return nil, fmt.Errorf("error opening etcd macaroon "+
×
322
                                "DB: %v", err)
×
323
                }
×
324
                closeFuncs[NSMacaroonDB] = etcdMacaroonBackend.Close
×
325

×
326
                etcdDecayedLogBackend, err := kvdb.Open(
×
327
                        kvdb.EtcdBackendName, ctx,
×
328
                        db.Etcd.CloneWithSubNamespace(NSDecayedLogDB),
×
329
                )
×
330
                if err != nil {
×
331
                        return nil, fmt.Errorf("error opening etcd decayed "+
×
332
                                "log DB: %v", err)
×
333
                }
×
334
                closeFuncs[NSDecayedLogDB] = etcdDecayedLogBackend.Close
×
335

×
336
                etcdTowerClientBackend, err := kvdb.Open(
×
337
                        kvdb.EtcdBackendName, ctx,
×
338
                        db.Etcd.CloneWithSubNamespace(NSTowerClientDB),
×
339
                )
×
340
                if err != nil {
×
341
                        return nil, fmt.Errorf("error opening etcd tower "+
×
342
                                "client DB: %v", err)
×
343
                }
×
344
                closeFuncs[NSTowerClientDB] = etcdTowerClientBackend.Close
×
345

×
346
                etcdTowerServerBackend, err := kvdb.Open(
×
347
                        kvdb.EtcdBackendName, ctx,
×
348
                        db.Etcd.CloneWithSubNamespace(NSTowerServerDB),
×
349
                )
×
350
                if err != nil {
×
351
                        return nil, fmt.Errorf("error opening etcd tower "+
×
352
                                "server DB: %v", err)
×
353
                }
×
354
                closeFuncs[NSTowerServerDB] = etcdTowerServerBackend.Close
×
355

×
356
                etcdWalletBackend, err := kvdb.Open(
×
357
                        kvdb.EtcdBackendName, ctx,
×
358
                        db.Etcd.
×
359
                                CloneWithSubNamespace(NSWalletDB).
×
360
                                CloneWithSingleWriter(),
×
361
                )
×
362
                if err != nil {
×
363
                        return nil, fmt.Errorf("error opening etcd macaroon "+
×
364
                                "DB: %v", err)
×
365
                }
×
366
                closeFuncs[NSWalletDB] = etcdWalletBackend.Close
×
367

×
368
                returnEarly = false
×
369

×
370
                return &DatabaseBackends{
×
371
                        GraphDB:       etcdBackend,
×
372
                        ChanStateDB:   etcdBackend,
×
373
                        HeightHintDB:  etcdBackend,
×
374
                        MacaroonDB:    etcdMacaroonBackend,
×
375
                        DecayedLogDB:  etcdDecayedLogBackend,
×
376
                        TowerClientDB: etcdTowerClientBackend,
×
377
                        TowerServerDB: etcdTowerServerBackend,
×
378
                        // The wallet loader will attempt to use/create the
×
379
                        // wallet in the replicated remote DB if we're running
×
380
                        // in a clustered environment. This will ensure that all
×
381
                        // members of the cluster have access to the same wallet
×
382
                        // state.
×
383
                        WalletDB: btcwallet.LoaderWithExternalWalletDB(
×
384
                                etcdWalletBackend,
×
385
                        ),
×
386
                        Remote:     true,
×
387
                        CloseFuncs: closeFuncs,
×
388
                }, nil
×
389

390
        case PostgresBackend:
×
391
                // Convert the sqldb PostgresConfig to a kvdb postgres.Config.
×
392
                // This is a temporary measure until we migrate all kvdb SQL
×
393
                // users to native SQL.
×
394
                postgresConfig := GetPostgresConfigKVDB(db.Postgres)
×
395

×
396
                postgresBackend, err := kvdb.Open(
×
397
                        kvdb.PostgresBackendName, ctx,
×
398
                        postgresConfig, NSChannelDB,
×
399
                )
×
400
                if err != nil {
×
401
                        return nil, fmt.Errorf("error opening postgres graph "+
×
402
                                "DB: %v", err)
×
403
                }
×
404
                closeFuncs[NSChannelDB] = postgresBackend.Close
×
405

×
406
                postgresMacaroonBackend, err := kvdb.Open(
×
407
                        kvdb.PostgresBackendName, ctx,
×
408
                        postgresConfig, NSMacaroonDB,
×
409
                )
×
410
                if err != nil {
×
411
                        return nil, fmt.Errorf("error opening postgres "+
×
412
                                "macaroon DB: %v", err)
×
413
                }
×
414
                closeFuncs[NSMacaroonDB] = postgresMacaroonBackend.Close
×
415

×
416
                postgresDecayedLogBackend, err := kvdb.Open(
×
417
                        kvdb.PostgresBackendName, ctx,
×
418
                        postgresConfig, NSDecayedLogDB,
×
419
                )
×
420
                if err != nil {
×
421
                        return nil, fmt.Errorf("error opening postgres "+
×
422
                                "decayed log DB: %v", err)
×
423
                }
×
424
                closeFuncs[NSDecayedLogDB] = postgresDecayedLogBackend.Close
×
425

×
426
                postgresTowerClientBackend, err := kvdb.Open(
×
427
                        kvdb.PostgresBackendName, ctx,
×
428
                        postgresConfig, NSTowerClientDB,
×
429
                )
×
430
                if err != nil {
×
431
                        return nil, fmt.Errorf("error opening postgres tower "+
×
432
                                "client DB: %v", err)
×
433
                }
×
434
                closeFuncs[NSTowerClientDB] = postgresTowerClientBackend.Close
×
435

×
436
                postgresTowerServerBackend, err := kvdb.Open(
×
437
                        kvdb.PostgresBackendName, ctx,
×
438
                        postgresConfig, NSTowerServerDB,
×
439
                )
×
440
                if err != nil {
×
441
                        return nil, fmt.Errorf("error opening postgres tower "+
×
442
                                "server DB: %v", err)
×
443
                }
×
444
                closeFuncs[NSTowerServerDB] = postgresTowerServerBackend.Close
×
445

×
NEW
446
                // The wallet subsystem is still not robust enough to run it
×
NEW
447
                // without a single writer in postgres therefore we create a
×
NEW
448
                // new config with the global lock enabled.
×
NEW
449
                //
×
NEW
450
                // NOTE: This is a temporary measure and should be removed as
×
NEW
451
                // soon as the wallet code is more robust.
×
NEW
452
                postgresConfigWalletDB := GetPostgresConfigKVDB(db.Postgres)
×
NEW
453
                postgresConfigWalletDB.WithGlobalLock = true
×
NEW
454

×
455
                postgresWalletBackend, err := kvdb.Open(
×
456
                        kvdb.PostgresBackendName, ctx,
×
NEW
457
                        postgresConfigWalletDB, NSWalletDB,
×
458
                )
×
459
                if err != nil {
×
NEW
460
                        return nil, fmt.Errorf("error opening postgres wallet "+
×
461
                                "DB: %v", err)
×
462
                }
×
463
                closeFuncs[NSWalletDB] = postgresWalletBackend.Close
×
464

×
465
                var nativeSQLStore sqldb.DB
×
466
                if db.UseNativeSQL {
×
467
                        nativePostgresStore, err := sqldb.NewPostgresStore(
×
468
                                db.Postgres,
×
469
                        )
×
470
                        if err != nil {
×
471
                                return nil, fmt.Errorf("error opening "+
×
472
                                        "native postgres store: %v", err)
×
473
                        }
×
474

475
                        nativeSQLStore = nativePostgresStore
×
476
                        closeFuncs[PostgresBackend] = nativePostgresStore.Close
×
477
                }
478

479
                // Warn if the user is trying to switch over to a Postgres DB
480
                // while there is a wallet or channel bbolt DB still present.
481
                warnExistingBoltDBs(
×
482
                        logger, "postgres", walletDBPath, WalletDBName,
×
483
                )
×
484
                warnExistingBoltDBs(
×
485
                        logger, "postgres", chanDBPath, ChannelDBName,
×
486
                )
×
487

×
488
                returnEarly = false
×
489

×
490
                return &DatabaseBackends{
×
491
                        GraphDB:       postgresBackend,
×
492
                        ChanStateDB:   postgresBackend,
×
493
                        HeightHintDB:  postgresBackend,
×
494
                        MacaroonDB:    postgresMacaroonBackend,
×
495
                        DecayedLogDB:  postgresDecayedLogBackend,
×
496
                        TowerClientDB: postgresTowerClientBackend,
×
497
                        TowerServerDB: postgresTowerServerBackend,
×
498
                        // The wallet loader will attempt to use/create the
×
499
                        // wallet in the replicated remote DB if we're running
×
500
                        // in a clustered environment. This will ensure that all
×
501
                        // members of the cluster have access to the same wallet
×
502
                        // state.
×
503
                        WalletDB: btcwallet.LoaderWithExternalWalletDB(
×
504
                                postgresWalletBackend,
×
505
                        ),
×
506
                        NativeSQLStore: nativeSQLStore,
×
507
                        Remote:         true,
×
508
                        CloseFuncs:     closeFuncs,
×
509
                }, nil
×
510

511
        case SqliteBackend:
×
512
                // Convert the sqldb SqliteConfig to a kvdb sqlite.Config.
×
513
                // This is a temporary measure until we migrate all kvdb SQL
×
514
                // users to native SQL.
×
515
                sqliteConfig := GetSqliteConfigKVDB(db.Sqlite)
×
516

×
517
                // Note that for sqlite, we put kv tables for the channel.db,
×
518
                // wtclient.db and sphinxreplay.db all in the channel.sqlite db.
×
519
                // The tables for wallet.db and macaroon.db are in the
×
520
                // chain.sqlite db and watchtower.db tables are in the
×
521
                // watchtower.sqlite db. The reason for the multiple sqlite dbs
×
522
                // is twofold. The first reason is that it maintains the file
×
523
                // structure that users are used to. The second reason is the
×
524
                // fact that sqlite only supports one writer at a time which
×
525
                // would cause deadlocks in the code due to the wallet db often
×
526
                // being accessed during a write to another db.
×
527
                sqliteBackend, err := kvdb.Open(
×
528
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
529
                        SqliteChannelDBName, NSChannelDB,
×
530
                )
×
531
                if err != nil {
×
532
                        return nil, fmt.Errorf("error opening sqlite graph "+
×
533
                                "DB: %v", err)
×
534
                }
×
535
                closeFuncs[NSChannelDB] = sqliteBackend.Close
×
536

×
537
                sqliteMacaroonBackend, err := kvdb.Open(
×
538
                        kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
×
539
                        SqliteChainDBName, NSMacaroonDB,
×
540
                )
×
541
                if err != nil {
×
542
                        return nil, fmt.Errorf("error opening sqlite "+
×
543
                                "macaroon DB: %v", err)
×
544
                }
×
545
                closeFuncs[NSMacaroonDB] = sqliteMacaroonBackend.Close
×
546

×
547
                sqliteDecayedLogBackend, err := kvdb.Open(
×
548
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
549
                        SqliteChannelDBName, NSDecayedLogDB,
×
550
                )
×
551
                if err != nil {
×
552
                        return nil, fmt.Errorf("error opening sqlite decayed "+
×
553
                                "log DB: %v", err)
×
554
                }
×
555
                closeFuncs[NSDecayedLogDB] = sqliteDecayedLogBackend.Close
×
556

×
557
                sqliteTowerClientBackend, err := kvdb.Open(
×
558
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
559
                        SqliteChannelDBName, NSTowerClientDB,
×
560
                )
×
561
                if err != nil {
×
562
                        return nil, fmt.Errorf("error opening sqlite tower "+
×
563
                                "client DB: %v", err)
×
564
                }
×
565
                closeFuncs[NSTowerClientDB] = sqliteTowerClientBackend.Close
×
566

×
567
                sqliteTowerServerBackend, err := kvdb.Open(
×
568
                        kvdb.SqliteBackendName, ctx, sqliteConfig,
×
569
                        towerServerDBPath, SqliteTowerDBName, NSTowerServerDB,
×
570
                )
×
571
                if err != nil {
×
572
                        return nil, fmt.Errorf("error opening sqlite tower "+
×
573
                                "server DB: %v", err)
×
574
                }
×
575
                closeFuncs[NSTowerServerDB] = sqliteTowerServerBackend.Close
×
576

×
577
                sqliteWalletBackend, err := kvdb.Open(
×
578
                        kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
×
579
                        SqliteChainDBName, NSWalletDB,
×
580
                )
×
581
                if err != nil {
×
582
                        return nil, fmt.Errorf("error opening sqlite macaroon "+
×
583
                                "DB: %v", err)
×
584
                }
×
585
                closeFuncs[NSWalletDB] = sqliteWalletBackend.Close
×
586

×
587
                var nativeSQLStore sqldb.DB
×
588
                if db.UseNativeSQL {
×
589
                        nativeSQLiteStore, err := sqldb.NewSqliteStore(
×
590
                                db.Sqlite,
×
591
                                path.Join(chanDBPath, SqliteNativeDBName),
×
592
                        )
×
593
                        if err != nil {
×
594
                                return nil, fmt.Errorf("error opening "+
×
595
                                        "native SQLite store: %v", err)
×
596
                        }
×
597

598
                        nativeSQLStore = nativeSQLiteStore
×
599
                        closeFuncs[SqliteBackend] = nativeSQLiteStore.Close
×
600
                }
601

602
                // Warn if the user is trying to switch over to a sqlite DB
603
                // while there is a wallet or channel bbolt DB still present.
604
                warnExistingBoltDBs(
×
605
                        logger, "sqlite", walletDBPath, WalletDBName,
×
606
                )
×
607
                warnExistingBoltDBs(
×
608
                        logger, "sqlite", chanDBPath, ChannelDBName,
×
609
                )
×
610

×
611
                returnEarly = false
×
612

×
613
                return &DatabaseBackends{
×
614
                        GraphDB:       sqliteBackend,
×
615
                        ChanStateDB:   sqliteBackend,
×
616
                        HeightHintDB:  sqliteBackend,
×
617
                        MacaroonDB:    sqliteMacaroonBackend,
×
618
                        DecayedLogDB:  sqliteDecayedLogBackend,
×
619
                        TowerClientDB: sqliteTowerClientBackend,
×
620
                        TowerServerDB: sqliteTowerServerBackend,
×
621
                        // The wallet loader will attempt to use/create the
×
622
                        // wallet in the replicated remote DB if we're running
×
623
                        // in a clustered environment. This will ensure that all
×
624
                        // members of the cluster have access to the same wallet
×
625
                        // state.
×
626
                        WalletDB: btcwallet.LoaderWithExternalWalletDB(
×
627
                                sqliteWalletBackend,
×
628
                        ),
×
629
                        NativeSQLStore: nativeSQLStore,
×
630
                        CloseFuncs:     closeFuncs,
×
631
                }, nil
×
632
        }
633

634
        // We're using all bbolt based databases by default.
635
        boltBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
3✔
636
                DBPath:            chanDBPath,
3✔
637
                DBFileName:        ChannelDBName,
3✔
638
                DBTimeout:         db.Bolt.DBTimeout,
3✔
639
                NoFreelistSync:    db.Bolt.NoFreelistSync,
3✔
640
                AutoCompact:       db.Bolt.AutoCompact,
3✔
641
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
3✔
642
        })
3✔
643
        if err != nil {
3✔
644
                return nil, fmt.Errorf("error opening bolt DB: %w", err)
×
645
        }
×
646
        closeFuncs[NSChannelDB] = boltBackend.Close
3✔
647

3✔
648
        macaroonBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
3✔
649
                DBPath:            walletDBPath,
3✔
650
                DBFileName:        MacaroonDBName,
3✔
651
                DBTimeout:         db.Bolt.DBTimeout,
3✔
652
                NoFreelistSync:    db.Bolt.NoFreelistSync,
3✔
653
                AutoCompact:       db.Bolt.AutoCompact,
3✔
654
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
3✔
655
        })
3✔
656
        if err != nil {
3✔
657
                return nil, fmt.Errorf("error opening macaroon DB: %w", err)
×
658
        }
×
659
        closeFuncs[NSMacaroonDB] = macaroonBackend.Close
3✔
660

3✔
661
        decayedLogBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
3✔
662
                DBPath:            chanDBPath,
3✔
663
                DBFileName:        DecayedLogDbName,
3✔
664
                DBTimeout:         db.Bolt.DBTimeout,
3✔
665
                NoFreelistSync:    db.Bolt.NoFreelistSync,
3✔
666
                AutoCompact:       db.Bolt.AutoCompact,
3✔
667
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
3✔
668
        })
3✔
669
        if err != nil {
3✔
670
                return nil, fmt.Errorf("error opening decayed log DB: %w", err)
×
671
        }
×
672
        closeFuncs[NSDecayedLogDB] = decayedLogBackend.Close
3✔
673

3✔
674
        // The tower client is optional and might not be enabled by the user. We
3✔
675
        // handle it being nil properly in the main server.
3✔
676
        var towerClientBackend kvdb.Backend
3✔
677
        if towerClientEnabled {
6✔
678
                towerClientBackend, err = kvdb.GetBoltBackend(
3✔
679
                        &kvdb.BoltBackendConfig{
3✔
680
                                DBPath:            chanDBPath,
3✔
681
                                DBFileName:        TowerClientDBName,
3✔
682
                                DBTimeout:         db.Bolt.DBTimeout,
3✔
683
                                NoFreelistSync:    db.Bolt.NoFreelistSync,
3✔
684
                                AutoCompact:       db.Bolt.AutoCompact,
3✔
685
                                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
3✔
686
                        },
3✔
687
                )
3✔
688
                if err != nil {
3✔
689
                        return nil, fmt.Errorf("error opening tower client "+
×
690
                                "DB: %v", err)
×
691
                }
×
692
                closeFuncs[NSTowerClientDB] = towerClientBackend.Close
3✔
693
        }
694

695
        // The tower server is optional and might not be enabled by the user. We
696
        // handle it being nil properly in the main server.
697
        var towerServerBackend kvdb.Backend
3✔
698
        if towerServerEnabled {
6✔
699
                towerServerBackend, err = kvdb.GetBoltBackend(
3✔
700
                        &kvdb.BoltBackendConfig{
3✔
701
                                DBPath:            towerServerDBPath,
3✔
702
                                DBFileName:        TowerServerDBName,
3✔
703
                                DBTimeout:         db.Bolt.DBTimeout,
3✔
704
                                NoFreelistSync:    db.Bolt.NoFreelistSync,
3✔
705
                                AutoCompact:       db.Bolt.AutoCompact,
3✔
706
                                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
3✔
707
                        },
3✔
708
                )
3✔
709
                if err != nil {
3✔
710
                        return nil, fmt.Errorf("error opening tower server "+
×
711
                                "DB: %v", err)
×
712
                }
×
713
                closeFuncs[NSTowerServerDB] = towerServerBackend.Close
3✔
714
        }
715

716
        returnEarly = false
3✔
717

3✔
718
        return &DatabaseBackends{
3✔
719
                GraphDB:       boltBackend,
3✔
720
                ChanStateDB:   boltBackend,
3✔
721
                HeightHintDB:  boltBackend,
3✔
722
                MacaroonDB:    macaroonBackend,
3✔
723
                DecayedLogDB:  decayedLogBackend,
3✔
724
                TowerClientDB: towerClientBackend,
3✔
725
                TowerServerDB: towerServerBackend,
3✔
726
                // When "running locally", LND will use the bbolt wallet.db to
3✔
727
                // store the wallet located in the chain data dir, parametrized
3✔
728
                // by the active network. The wallet loader has its own cleanup
3✔
729
                // method so we don't need to add anything to our map (in fact
3✔
730
                // nothing is opened just yet).
3✔
731
                WalletDB: btcwallet.LoaderWithLocalWalletDB(
3✔
732
                        walletDBPath, db.Bolt.NoFreelistSync, db.Bolt.DBTimeout,
3✔
733
                ),
3✔
734
                CloseFuncs: closeFuncs,
3✔
735
        }, nil
3✔
736
}
737

738
// warnExistingBoltDBs checks if there is an existing bbolt database in the
739
// given location and logs a warning if so.
740
func warnExistingBoltDBs(log btclog.Logger, dbType, dir, fileName string) {
×
741
        if lnrpc.FileExists(filepath.Join(dir, fileName)) {
×
742
                log.Warnf("Found existing bbolt database file in %s/%s while "+
×
743
                        "using database type %s. Existing data will NOT be "+
×
744
                        "migrated to %s automatically!", dir, fileName, dbType,
×
745
                        dbType)
×
746
        }
×
747
}
748

749
// Compile-time constraint to ensure Workers implements the Validator interface.
750
var _ Validator = (*DB)(nil)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc