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

lightningnetwork / lnd / 12312390362

13 Dec 2024 08:44AM UTC coverage: 57.458% (+8.5%) from 48.92%
12312390362

Pull #9343

github

ellemouton
fn: rework the ContextGuard and add tests

In this commit, the ContextGuard struct is re-worked such that the
context that its new main WithCtx method provides is cancelled in sync
with a parent context being cancelled or with it's quit channel being
cancelled. Tests are added to assert the behaviour. In order for the
close of the quit channel to be consistent with the cancelling of the
derived context, the quit channel _must_ be contained internal to the
ContextGuard so that callers are only able to close the channel via the
exposed Quit method which will then take care to first cancel any
derived context that depend on the quit channel before returning.
Pull Request #9343: fn: expand the ContextGuard and add tests

101853 of 177264 relevant lines covered (57.46%)

24972.93 hits per line

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

4.44
/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

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

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

41
        defaultPostgresMaxConnections = 50
42
        defaultSqliteMaxConnections   = 2
43

44
        defaultSqliteBusyTimeout = 5 * time.Second
45

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

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

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

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

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

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

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

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

78
        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."`
79

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

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

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

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

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

90
        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."`
91

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

94
        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."`
95
}
96

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

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

131
        case SqliteBackend:
×
132
        case PostgresBackend:
×
133
                if err := db.Postgres.Validate(); err != nil {
×
134
                        return err
×
135
                }
×
136

137
        case EtcdBackend:
×
138
                if db.UseNativeSQL {
×
139
                        return fmt.Errorf("cannot use native SQL with etcd " +
×
140
                                "backend")
×
141
                }
×
142

143
                if !db.Etcd.Embedded && db.Etcd.Host == "" {
×
144
                        return fmt.Errorf("etcd host must be set")
×
145
                }
×
146

147
        default:
×
148
                return fmt.Errorf("unknown backend, must be either '%v', "+
×
149
                        "'%v', '%v' or '%v'", BoltBackend, EtcdBackend,
×
150
                        PostgresBackend, SqliteBackend)
×
151
        }
152

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

166
        return nil
×
167
}
168

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

183
                // Override the original config with the config for
184
                // the embedded instance.
185
                db.Etcd = cfg
×
186

187
        case db.Backend == PostgresBackend:
×
188
                sqlbase.Init(db.Postgres.MaxConnections)
×
189

190
        case db.Backend == SqliteBackend:
×
191
                sqlbase.Init(db.Sqlite.MaxConnections)
×
192
        }
193

194
        return nil
×
195
}
196

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

206
        // ChanStateDB points to a possibly networked replicated backend that
207
        // contains the critical channel state related data.
208
        ChanStateDB kvdb.Backend
209

210
        // HeightHintDB points to a possibly networked replicated backend that
211
        // contains the chain height hint related data.
212
        HeightHintDB kvdb.Backend
213

214
        // MacaroonDB points to a database backend that stores the macaroon root
215
        // keys.
216
        MacaroonDB kvdb.Backend
217

218
        // DecayedLogDB points to a database backend that stores the decayed log
219
        // data.
220
        DecayedLogDB kvdb.Backend
221

222
        // TowerClientDB points to a database backend that stores the watchtower
223
        // client data. This might be nil if the watchtower client is disabled.
224
        TowerClientDB kvdb.Backend
225

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

230
        // WalletDB is an option that instructs the wallet loader where to load
231
        // the underlying wallet database from.
232
        WalletDB btcwallet.LoaderOption
233

234
        // NativeSQLStore is a pointer to a native SQL store that can be used
235
        // for native SQL queries for tables that already support it. This may
236
        // be nil if the use-native-sql flag was not set.
237
        NativeSQLStore *sqldb.BaseDB
238

239
        // Remote indicates whether the database backends are remote, possibly
240
        // replicated instances or local bbolt or sqlite backed databases.
241
        Remote bool
242

243
        // CloseFuncs is a map of close functions for each of the initialized
244
        // DB backends keyed by their namespace name.
245
        CloseFuncs map[string]func() error
246
}
247

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

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

268
// GetBackends returns a set of kvdb.Backends as set in the DB config.
269
func (db *DB) GetBackends(ctx context.Context, chanDBPath,
270
        walletDBPath, towerServerDBPath string, towerClientEnabled,
271
        towerServerEnabled bool, logger btclog.Logger) (*DatabaseBackends,
272
        error) {
×
273

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

×
279
        // If we need to return early because of an error, we invoke any close
×
280
        // function that has been initialized so far.
×
281
        returnEarly := true
×
282
        defer func() {
×
283
                if !returnEarly {
×
284
                        return
×
285
                }
×
286

287
                for _, closeFunc := range closeFuncs {
×
288
                        _ = closeFunc()
×
289
                }
×
290
        }()
291

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

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

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

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

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

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

×
364
                returnEarly = false
×
365

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

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

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

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

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

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

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

×
442
                postgresWalletBackend, err := kvdb.Open(
×
443
                        kvdb.PostgresBackendName, ctx,
×
444
                        postgresConfig, NSWalletDB,
×
445
                )
×
446
                if err != nil {
×
447
                        return nil, fmt.Errorf("error opening postgres macaroon "+
×
448
                                "DB: %v", err)
×
449
                }
×
450
                closeFuncs[NSWalletDB] = postgresWalletBackend.Close
×
451

×
452
                var nativeSQLStore *sqldb.BaseDB
×
453
                if db.UseNativeSQL {
×
454
                        nativePostgresStore, err := sqldb.NewPostgresStore(
×
455
                                db.Postgres,
×
456
                        )
×
457
                        if err != nil {
×
458
                                return nil, fmt.Errorf("error opening "+
×
459
                                        "native postgres store: %v", err)
×
460
                        }
×
461

462
                        nativeSQLStore = nativePostgresStore.BaseDB
×
463
                        closeFuncs[PostgresBackend] = nativePostgresStore.Close
×
464
                }
465

466
                // Warn if the user is trying to switch over to a Postgres DB
467
                // while there is a wallet or channel bbolt DB still present.
468
                warnExistingBoltDBs(
×
469
                        logger, "postgres", walletDBPath, WalletDBName,
×
470
                )
×
471
                warnExistingBoltDBs(
×
472
                        logger, "postgres", chanDBPath, ChannelDBName,
×
473
                )
×
474

×
475
                returnEarly = false
×
476

×
477
                return &DatabaseBackends{
×
478
                        GraphDB:       postgresBackend,
×
479
                        ChanStateDB:   postgresBackend,
×
480
                        HeightHintDB:  postgresBackend,
×
481
                        MacaroonDB:    postgresMacaroonBackend,
×
482
                        DecayedLogDB:  postgresDecayedLogBackend,
×
483
                        TowerClientDB: postgresTowerClientBackend,
×
484
                        TowerServerDB: postgresTowerServerBackend,
×
485
                        // The wallet loader will attempt to use/create the
×
486
                        // wallet in the replicated remote DB if we're running
×
487
                        // in a clustered environment. This will ensure that all
×
488
                        // members of the cluster have access to the same wallet
×
489
                        // state.
×
490
                        WalletDB: btcwallet.LoaderWithExternalWalletDB(
×
491
                                postgresWalletBackend,
×
492
                        ),
×
493
                        NativeSQLStore: nativeSQLStore,
×
494
                        Remote:         true,
×
495
                        CloseFuncs:     closeFuncs,
×
496
                }, nil
×
497

498
        case SqliteBackend:
×
499
                // Convert the sqldb SqliteConfig to a kvdb sqlite.Config.
×
500
                // This is a temporary measure until we migrate all kvdb SQL
×
501
                // users to native SQL.
×
502
                sqliteConfig := GetSqliteConfigKVDB(db.Sqlite)
×
503

×
504
                // Note that for sqlite, we put kv tables for the channel.db,
×
505
                // wtclient.db and sphinxreplay.db all in the channel.sqlite db.
×
506
                // The tables for wallet.db and macaroon.db are in the
×
507
                // chain.sqlite db and watchtower.db tables are in the
×
508
                // watchtower.sqlite db. The reason for the multiple sqlite dbs
×
509
                // is twofold. The first reason is that it maintains the file
×
510
                // structure that users are used to. The second reason is the
×
511
                // fact that sqlite only supports one writer at a time which
×
512
                // would cause deadlocks in the code due to the wallet db often
×
513
                // being accessed during a write to another db.
×
514
                sqliteBackend, err := kvdb.Open(
×
515
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
516
                        SqliteChannelDBName, NSChannelDB,
×
517
                )
×
518
                if err != nil {
×
519
                        return nil, fmt.Errorf("error opening sqlite graph "+
×
520
                                "DB: %v", err)
×
521
                }
×
522
                closeFuncs[NSChannelDB] = sqliteBackend.Close
×
523

×
524
                sqliteMacaroonBackend, err := kvdb.Open(
×
525
                        kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
×
526
                        SqliteChainDBName, NSMacaroonDB,
×
527
                )
×
528
                if err != nil {
×
529
                        return nil, fmt.Errorf("error opening sqlite "+
×
530
                                "macaroon DB: %v", err)
×
531
                }
×
532
                closeFuncs[NSMacaroonDB] = sqliteMacaroonBackend.Close
×
533

×
534
                sqliteDecayedLogBackend, err := kvdb.Open(
×
535
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
536
                        SqliteChannelDBName, NSDecayedLogDB,
×
537
                )
×
538
                if err != nil {
×
539
                        return nil, fmt.Errorf("error opening sqlite decayed "+
×
540
                                "log DB: %v", err)
×
541
                }
×
542
                closeFuncs[NSDecayedLogDB] = sqliteDecayedLogBackend.Close
×
543

×
544
                sqliteTowerClientBackend, err := kvdb.Open(
×
545
                        kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
×
546
                        SqliteChannelDBName, NSTowerClientDB,
×
547
                )
×
548
                if err != nil {
×
549
                        return nil, fmt.Errorf("error opening sqlite tower "+
×
550
                                "client DB: %v", err)
×
551
                }
×
552
                closeFuncs[NSTowerClientDB] = sqliteTowerClientBackend.Close
×
553

×
554
                sqliteTowerServerBackend, err := kvdb.Open(
×
555
                        kvdb.SqliteBackendName, ctx, sqliteConfig,
×
556
                        towerServerDBPath, SqliteTowerDBName, NSTowerServerDB,
×
557
                )
×
558
                if err != nil {
×
559
                        return nil, fmt.Errorf("error opening sqlite tower "+
×
560
                                "server DB: %v", err)
×
561
                }
×
562
                closeFuncs[NSTowerServerDB] = sqliteTowerServerBackend.Close
×
563

×
564
                sqliteWalletBackend, err := kvdb.Open(
×
565
                        kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
×
566
                        SqliteChainDBName, NSWalletDB,
×
567
                )
×
568
                if err != nil {
×
569
                        return nil, fmt.Errorf("error opening sqlite macaroon "+
×
570
                                "DB: %v", err)
×
571
                }
×
572
                closeFuncs[NSWalletDB] = sqliteWalletBackend.Close
×
573

×
574
                var nativeSQLStore *sqldb.BaseDB
×
575
                if db.UseNativeSQL {
×
576
                        nativeSQLiteStore, err := sqldb.NewSqliteStore(
×
577
                                db.Sqlite,
×
578
                                path.Join(chanDBPath, SqliteNativeDBName),
×
579
                        )
×
580
                        if err != nil {
×
581
                                return nil, fmt.Errorf("error opening "+
×
582
                                        "native SQLite store: %v", err)
×
583
                        }
×
584

585
                        nativeSQLStore = nativeSQLiteStore.BaseDB
×
586
                        closeFuncs[SqliteBackend] = nativeSQLiteStore.Close
×
587
                }
588

589
                // Warn if the user is trying to switch over to a sqlite DB
590
                // while there is a wallet or channel bbolt DB still present.
591
                warnExistingBoltDBs(
×
592
                        logger, "sqlite", walletDBPath, WalletDBName,
×
593
                )
×
594
                warnExistingBoltDBs(
×
595
                        logger, "sqlite", chanDBPath, ChannelDBName,
×
596
                )
×
597

×
598
                returnEarly = false
×
599

×
600
                return &DatabaseBackends{
×
601
                        GraphDB:       sqliteBackend,
×
602
                        ChanStateDB:   sqliteBackend,
×
603
                        HeightHintDB:  sqliteBackend,
×
604
                        MacaroonDB:    sqliteMacaroonBackend,
×
605
                        DecayedLogDB:  sqliteDecayedLogBackend,
×
606
                        TowerClientDB: sqliteTowerClientBackend,
×
607
                        TowerServerDB: sqliteTowerServerBackend,
×
608
                        // The wallet loader will attempt to use/create the
×
609
                        // wallet in the replicated remote DB if we're running
×
610
                        // in a clustered environment. This will ensure that all
×
611
                        // members of the cluster have access to the same wallet
×
612
                        // state.
×
613
                        WalletDB: btcwallet.LoaderWithExternalWalletDB(
×
614
                                sqliteWalletBackend,
×
615
                        ),
×
616
                        NativeSQLStore: nativeSQLStore,
×
617
                        CloseFuncs:     closeFuncs,
×
618
                }, nil
×
619
        }
620

621
        // We're using all bbolt based databases by default.
622
        boltBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
×
623
                DBPath:            chanDBPath,
×
624
                DBFileName:        ChannelDBName,
×
625
                DBTimeout:         db.Bolt.DBTimeout,
×
626
                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
627
                AutoCompact:       db.Bolt.AutoCompact,
×
628
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
629
        })
×
630
        if err != nil {
×
631
                return nil, fmt.Errorf("error opening bolt DB: %w", err)
×
632
        }
×
633
        closeFuncs[NSChannelDB] = boltBackend.Close
×
634

×
635
        macaroonBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
×
636
                DBPath:            walletDBPath,
×
637
                DBFileName:        MacaroonDBName,
×
638
                DBTimeout:         db.Bolt.DBTimeout,
×
639
                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
640
                AutoCompact:       db.Bolt.AutoCompact,
×
641
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
642
        })
×
643
        if err != nil {
×
644
                return nil, fmt.Errorf("error opening macaroon DB: %w", err)
×
645
        }
×
646
        closeFuncs[NSMacaroonDB] = macaroonBackend.Close
×
647

×
648
        decayedLogBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
×
649
                DBPath:            chanDBPath,
×
650
                DBFileName:        DecayedLogDbName,
×
651
                DBTimeout:         db.Bolt.DBTimeout,
×
652
                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
653
                AutoCompact:       db.Bolt.AutoCompact,
×
654
                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
655
        })
×
656
        if err != nil {
×
657
                return nil, fmt.Errorf("error opening decayed log DB: %w", err)
×
658
        }
×
659
        closeFuncs[NSDecayedLogDB] = decayedLogBackend.Close
×
660

×
661
        // The tower client is optional and might not be enabled by the user. We
×
662
        // handle it being nil properly in the main server.
×
663
        var towerClientBackend kvdb.Backend
×
664
        if towerClientEnabled {
×
665
                towerClientBackend, err = kvdb.GetBoltBackend(
×
666
                        &kvdb.BoltBackendConfig{
×
667
                                DBPath:            chanDBPath,
×
668
                                DBFileName:        TowerClientDBName,
×
669
                                DBTimeout:         db.Bolt.DBTimeout,
×
670
                                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
671
                                AutoCompact:       db.Bolt.AutoCompact,
×
672
                                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
673
                        },
×
674
                )
×
675
                if err != nil {
×
676
                        return nil, fmt.Errorf("error opening tower client "+
×
677
                                "DB: %v", err)
×
678
                }
×
679
                closeFuncs[NSTowerClientDB] = towerClientBackend.Close
×
680
        }
681

682
        // The tower server is optional and might not be enabled by the user. We
683
        // handle it being nil properly in the main server.
684
        var towerServerBackend kvdb.Backend
×
685
        if towerServerEnabled {
×
686
                towerServerBackend, err = kvdb.GetBoltBackend(
×
687
                        &kvdb.BoltBackendConfig{
×
688
                                DBPath:            towerServerDBPath,
×
689
                                DBFileName:        TowerServerDBName,
×
690
                                DBTimeout:         db.Bolt.DBTimeout,
×
691
                                NoFreelistSync:    db.Bolt.NoFreelistSync,
×
692
                                AutoCompact:       db.Bolt.AutoCompact,
×
693
                                AutoCompactMinAge: db.Bolt.AutoCompactMinAge,
×
694
                        },
×
695
                )
×
696
                if err != nil {
×
697
                        return nil, fmt.Errorf("error opening tower server "+
×
698
                                "DB: %v", err)
×
699
                }
×
700
                closeFuncs[NSTowerServerDB] = towerServerBackend.Close
×
701
        }
702

703
        returnEarly = false
×
704

×
705
        return &DatabaseBackends{
×
706
                GraphDB:       boltBackend,
×
707
                ChanStateDB:   boltBackend,
×
708
                HeightHintDB:  boltBackend,
×
709
                MacaroonDB:    macaroonBackend,
×
710
                DecayedLogDB:  decayedLogBackend,
×
711
                TowerClientDB: towerClientBackend,
×
712
                TowerServerDB: towerServerBackend,
×
713
                // When "running locally", LND will use the bbolt wallet.db to
×
714
                // store the wallet located in the chain data dir, parametrized
×
715
                // by the active network. The wallet loader has its own cleanup
×
716
                // method so we don't need to add anything to our map (in fact
×
717
                // nothing is opened just yet).
×
718
                WalletDB: btcwallet.LoaderWithLocalWalletDB(
×
719
                        walletDBPath, db.Bolt.NoFreelistSync, db.Bolt.DBTimeout,
×
720
                ),
×
721
                CloseFuncs: closeFuncs,
×
722
        }, nil
×
723
}
724

725
// warnExistingBoltDBs checks if there is an existing bbolt database in the
726
// given location and logs a warning if so.
727
func warnExistingBoltDBs(log btclog.Logger, dbType, dir, fileName string) {
×
728
        if lnrpc.FileExists(filepath.Join(dir, fileName)) {
×
729
                log.Warnf("Found existing bbolt database file in %s/%s while "+
×
730
                        "using database type %s. Existing data will NOT be "+
×
731
                        "migrated to %s automatically!", dir, fileName, dbType,
×
732
                        dbType)
×
733
        }
×
734
}
735

736
// Compile-time constraint to ensure Workers implements the Validator interface.
737
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