github.com/decred/dcrlnd@v0.7.6/lncfg/db.go (about) 1 package lncfg 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/decred/dcrlnd/kvdb" 9 "github.com/decred/dcrlnd/kvdb/etcd" 10 "github.com/decred/dcrlnd/kvdb/postgres" 11 walletloader "github.com/decred/dcrlnd/lnwallet/dcrwallet/loader" 12 ) 13 14 const ( 15 channelDBName = "channel.db" 16 macaroonDBName = "macaroons.db" 17 decayedLogDbName = "sphinxreplay.db" 18 towerClientDBName = "wtclient.db" 19 towerServerDBName = "watchtower.db" 20 21 BoltBackend = "bolt" 22 EtcdBackend = "etcd" 23 PostgresBackend = "postgres" 24 DefaultBatchCommitInterval = 500 * time.Millisecond 25 26 defaultPostgresMaxConnections = 50 27 28 // NSChannelDB is the namespace name that we use for the combined graph 29 // and channel state DB. 30 NSChannelDB = "channeldb" 31 32 // NSMacaroonDB is the namespace name that we use for the macaroon DB. 33 NSMacaroonDB = "macaroondb" 34 35 // NSDecayedLogDB is the namespace name that we use for the sphinx 36 // replay a.k.a. decayed log DB. 37 NSDecayedLogDB = "decayedlogdb" 38 39 // NSTowerClientDB is the namespace name that we use for the watchtower 40 // client DB. 41 NSTowerClientDB = "towerclientdb" 42 43 // NSTowerServerDB is the namespace name that we use for the watchtower 44 // server DB. 45 NSTowerServerDB = "towerserverdb" 46 47 // NSWalletDB is the namespace name that we use for the wallet DB. 48 NSWalletDB = "walletdb" 49 ) 50 51 // DB holds database configuration for LND. 52 type DB struct { 53 Backend string `long:"backend" description:"The selected database backend."` 54 55 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."` 56 57 Etcd *etcd.Config `group:"etcd" namespace:"etcd" description:"Etcd settings."` 58 59 Bolt *kvdb.BoltConfig `group:"bolt" namespace:"bolt" description:"Bolt settings."` 60 61 Postgres *postgres.Config `group:"postgres" namespace:"postgres" description:"Postgres settings."` 62 63 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."` 64 } 65 66 // DefaultDB creates and returns a new default DB config. 67 func DefaultDB() *DB { 68 return &DB{ 69 Backend: BoltBackend, 70 BatchCommitInterval: DefaultBatchCommitInterval, 71 Bolt: &kvdb.BoltConfig{ 72 NoFreelistSync: true, 73 AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge, 74 DBTimeout: kvdb.DefaultDBTimeout, 75 }, 76 Etcd: &etcd.Config{ 77 // Allow at most 32 MiB messages by default. 78 MaxMsgSize: 32768 * 1024, 79 }, 80 Postgres: &postgres.Config{ 81 MaxConnections: defaultPostgresMaxConnections, 82 }, 83 } 84 } 85 86 // Validate validates the DB config. 87 func (db *DB) Validate() error { 88 switch db.Backend { 89 case BoltBackend: 90 case PostgresBackend: 91 if db.Postgres.Dsn == "" { 92 return fmt.Errorf("postgres dsn must be set") 93 } 94 95 case EtcdBackend: 96 if !db.Etcd.Embedded && db.Etcd.Host == "" { 97 return fmt.Errorf("etcd host must be set") 98 } 99 100 default: 101 return fmt.Errorf("unknown backend, must be either '%v' or "+ 102 "'%v'", BoltBackend, EtcdBackend) 103 } 104 105 // The path finding uses a manual read transaction that's open for a 106 // potentially long time. That works fine with the locking model of 107 // bbolt but can lead to locks or rolled back transactions with etcd or 108 // postgres. And since we already have a smaller memory footprint for 109 // remote database setups (due to not needing to memory-map the bbolt DB 110 // files), we can keep the graph in memory instead. But for mobile 111 // devices the tradeoff between a smaller memory footprint and the 112 // longer time needed for path finding might be a desirable one. 113 if db.NoGraphCache && db.Backend != BoltBackend { 114 return fmt.Errorf("cannot use no-graph-cache with database "+ 115 "backend '%v'", db.Backend) 116 } 117 118 return nil 119 } 120 121 // Init should be called upon start to pre-initialize database access dependent 122 // on configuration. 123 func (db *DB) Init(ctx context.Context, dbPath string) error { 124 // Start embedded etcd server if requested. 125 switch { 126 case db.Backend == EtcdBackend && db.Etcd.Embedded: 127 cfg, _, err := kvdb.StartEtcdTestBackend( 128 dbPath, db.Etcd.EmbeddedClientPort, 129 db.Etcd.EmbeddedPeerPort, db.Etcd.EmbeddedLogFile, 130 ) 131 if err != nil { 132 return err 133 } 134 135 // Override the original config with the config for 136 // the embedded instance. 137 db.Etcd = cfg 138 139 case db.Backend == PostgresBackend: 140 postgres.Init(db.Postgres.MaxConnections) 141 } 142 143 return nil 144 } 145 146 // DatabaseBackends is a two-tuple that holds the set of active database 147 // backends for the daemon. The two backends we expose are the graph database 148 // backend, and the channel state backend. 149 type DatabaseBackends struct { 150 // GraphDB points to the database backend that contains the less 151 // critical data that is accessed often, such as the channel graph and 152 // chain height hints. 153 GraphDB kvdb.Backend 154 155 // ChanStateDB points to a possibly networked replicated backend that 156 // contains the critical channel state related data. 157 ChanStateDB kvdb.Backend 158 159 // HeightHintDB points to a possibly networked replicated backend that 160 // contains the chain height hint related data. 161 HeightHintDB kvdb.Backend 162 163 // MacaroonDB points to a database backend that stores the macaroon root 164 // keys. 165 MacaroonDB kvdb.Backend 166 167 // DecayedLogDB points to a database backend that stores the decayed log 168 // data. 169 DecayedLogDB kvdb.Backend 170 171 // TowerClientDB points to a database backend that stores the watchtower 172 // client data. This might be nil if the watchtower client is disabled. 173 TowerClientDB kvdb.Backend 174 175 // TowerServerDB points to a database backend that stores the watchtower 176 // server data. This might be nil if the watchtower server is disabled. 177 TowerServerDB kvdb.Backend 178 179 // WalletDB is an option that instructs the wallet loader where to load 180 // the underlying wallet database from. 181 WalletDB walletloader.LoaderOption 182 183 // Remote indicates whether the database backends are remote, possibly 184 // replicated instances or local bbolt backed databases. 185 Remote bool 186 187 // CloseFuncs is a map of close functions for each of the initialized 188 // DB backends keyed by their namespace name. 189 CloseFuncs map[string]func() error 190 } 191 192 // GetBackends returns a set of kvdb.Backends as set in the DB config. 193 func (db *DB) GetBackends(ctx context.Context, chanDBPath, 194 walletDBPath, towerServerDBPath string, towerClientEnabled, 195 towerServerEnabled bool) (*DatabaseBackends, error) { 196 197 // We keep track of all the kvdb backends we actually open and return a 198 // reference to their close function so they can be cleaned up properly 199 // on error or shutdown. 200 closeFuncs := make(map[string]func() error) 201 202 // If we need to return early because of an error, we invoke any close 203 // function that has been initialized so far. 204 returnEarly := true 205 defer func() { 206 if !returnEarly { 207 return 208 } 209 210 for _, closeFunc := range closeFuncs { 211 _ = closeFunc() 212 } 213 }() 214 215 switch db.Backend { 216 case EtcdBackend: 217 // As long as the graph data, channel state and height hint 218 // cache are all still in the channel.db file in bolt, we 219 // replicate the same behavior here and use the same etcd 220 // backend for those three sub DBs. But we namespace it properly 221 // to make such a split even easier in the future. This will 222 // break lnd for users that ran on etcd with 0.13.x since that 223 // code used the root namespace. We assume that nobody used etcd 224 // for mainnet just yet since that feature was clearly marked as 225 // experimental in 0.13.x. 226 etcdBackend, err := kvdb.Open( 227 kvdb.EtcdBackendName, ctx, 228 db.Etcd.CloneWithSubNamespace(NSChannelDB), 229 ) 230 if err != nil { 231 return nil, fmt.Errorf("error opening etcd DB: %v", err) 232 } 233 closeFuncs[NSChannelDB] = etcdBackend.Close 234 235 etcdMacaroonBackend, err := kvdb.Open( 236 kvdb.EtcdBackendName, ctx, 237 db.Etcd.CloneWithSubNamespace(NSMacaroonDB), 238 ) 239 if err != nil { 240 return nil, fmt.Errorf("error opening etcd macaroon "+ 241 "DB: %v", err) 242 } 243 closeFuncs[NSMacaroonDB] = etcdMacaroonBackend.Close 244 245 etcdDecayedLogBackend, err := kvdb.Open( 246 kvdb.EtcdBackendName, ctx, 247 db.Etcd.CloneWithSubNamespace(NSDecayedLogDB), 248 ) 249 if err != nil { 250 return nil, fmt.Errorf("error opening etcd decayed "+ 251 "log DB: %v", err) 252 } 253 closeFuncs[NSDecayedLogDB] = etcdDecayedLogBackend.Close 254 255 etcdTowerClientBackend, err := kvdb.Open( 256 kvdb.EtcdBackendName, ctx, 257 db.Etcd.CloneWithSubNamespace(NSTowerClientDB), 258 ) 259 if err != nil { 260 return nil, fmt.Errorf("error opening etcd tower "+ 261 "client DB: %v", err) 262 } 263 closeFuncs[NSTowerClientDB] = etcdTowerClientBackend.Close 264 265 etcdTowerServerBackend, err := kvdb.Open( 266 kvdb.EtcdBackendName, ctx, 267 db.Etcd.CloneWithSubNamespace(NSTowerServerDB), 268 ) 269 if err != nil { 270 return nil, fmt.Errorf("error opening etcd tower "+ 271 "server DB: %v", err) 272 } 273 closeFuncs[NSTowerServerDB] = etcdTowerServerBackend.Close 274 275 etcdWalletBackend, err := kvdb.Open( 276 kvdb.EtcdBackendName, ctx, 277 db.Etcd. 278 CloneWithSubNamespace(NSWalletDB). 279 CloneWithSingleWriter(), 280 ) 281 if err != nil { 282 return nil, fmt.Errorf("error opening etcd macaroon "+ 283 "DB: %v", err) 284 } 285 closeFuncs[NSWalletDB] = etcdWalletBackend.Close 286 287 returnEarly = false 288 return &DatabaseBackends{ 289 GraphDB: etcdBackend, 290 ChanStateDB: etcdBackend, 291 HeightHintDB: etcdBackend, 292 MacaroonDB: etcdMacaroonBackend, 293 DecayedLogDB: etcdDecayedLogBackend, 294 TowerClientDB: etcdTowerClientBackend, 295 TowerServerDB: etcdTowerServerBackend, 296 // The wallet loader will attempt to use/create the 297 // wallet in the replicated remote DB if we're running 298 // in a clustered environment. This will ensure that all 299 // members of the cluster have access to the same wallet 300 // state. 301 WalletDB: walletloader.LoaderWithExternalWalletDB( 302 etcdBackend, 303 ), 304 Remote: true, 305 CloseFuncs: closeFuncs, 306 }, nil 307 308 case PostgresBackend: 309 postgresBackend, err := kvdb.Open( 310 kvdb.PostgresBackendName, ctx, 311 db.Postgres, NSChannelDB, 312 ) 313 if err != nil { 314 return nil, fmt.Errorf("error opening postgres graph "+ 315 "DB: %v", err) 316 } 317 closeFuncs[NSChannelDB] = postgresBackend.Close 318 319 postgresMacaroonBackend, err := kvdb.Open( 320 kvdb.PostgresBackendName, ctx, 321 db.Postgres, NSMacaroonDB, 322 ) 323 if err != nil { 324 return nil, fmt.Errorf("error opening postgres "+ 325 "macaroon DB: %v", err) 326 } 327 closeFuncs[NSMacaroonDB] = postgresMacaroonBackend.Close 328 329 postgresDecayedLogBackend, err := kvdb.Open( 330 kvdb.PostgresBackendName, ctx, 331 db.Postgres, NSDecayedLogDB, 332 ) 333 if err != nil { 334 return nil, fmt.Errorf("error opening postgres "+ 335 "decayed log DB: %v", err) 336 } 337 closeFuncs[NSDecayedLogDB] = postgresDecayedLogBackend.Close 338 339 postgresTowerClientBackend, err := kvdb.Open( 340 kvdb.PostgresBackendName, ctx, 341 db.Postgres, NSTowerClientDB, 342 ) 343 if err != nil { 344 return nil, fmt.Errorf("error opening postgres tower "+ 345 "client DB: %v", err) 346 } 347 closeFuncs[NSTowerClientDB] = postgresTowerClientBackend.Close 348 349 postgresTowerServerBackend, err := kvdb.Open( 350 kvdb.PostgresBackendName, ctx, 351 db.Postgres, NSTowerServerDB, 352 ) 353 if err != nil { 354 return nil, fmt.Errorf("error opening postgres tower "+ 355 "server DB: %v", err) 356 } 357 closeFuncs[NSTowerServerDB] = postgresTowerServerBackend.Close 358 359 postgresWalletBackend, err := kvdb.Open( 360 kvdb.PostgresBackendName, ctx, 361 db.Postgres, NSWalletDB, 362 ) 363 if err != nil { 364 return nil, fmt.Errorf("error opening postgres macaroon "+ 365 "DB: %v", err) 366 } 367 closeFuncs[NSWalletDB] = postgresWalletBackend.Close 368 369 returnEarly = false 370 return &DatabaseBackends{ 371 GraphDB: postgresBackend, 372 ChanStateDB: postgresBackend, 373 HeightHintDB: postgresBackend, 374 MacaroonDB: postgresMacaroonBackend, 375 DecayedLogDB: postgresDecayedLogBackend, 376 TowerClientDB: postgresTowerClientBackend, 377 TowerServerDB: postgresTowerServerBackend, 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: walletloader.LoaderWithExternalWalletDB( 384 postgresWalletBackend, 385 ), 386 Remote: true, 387 CloseFuncs: closeFuncs, 388 }, nil 389 } 390 391 // We're using all bbolt based databases by default. 392 boltBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{ 393 DBPath: chanDBPath, 394 DBFileName: channelDBName, 395 DBTimeout: db.Bolt.DBTimeout, 396 NoFreelistSync: db.Bolt.NoFreelistSync, 397 AutoCompact: db.Bolt.AutoCompact, 398 AutoCompactMinAge: db.Bolt.AutoCompactMinAge, 399 }) 400 if err != nil { 401 return nil, fmt.Errorf("error opening bolt DB: %v", err) 402 } 403 closeFuncs[NSChannelDB] = boltBackend.Close 404 405 macaroonBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{ 406 DBPath: walletDBPath, 407 DBFileName: macaroonDBName, 408 DBTimeout: db.Bolt.DBTimeout, 409 NoFreelistSync: db.Bolt.NoFreelistSync, 410 AutoCompact: db.Bolt.AutoCompact, 411 AutoCompactMinAge: db.Bolt.AutoCompactMinAge, 412 }) 413 if err != nil { 414 return nil, fmt.Errorf("error opening macaroon DB: %v", err) 415 } 416 closeFuncs[NSMacaroonDB] = macaroonBackend.Close 417 418 decayedLogBackend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{ 419 DBPath: chanDBPath, 420 DBFileName: decayedLogDbName, 421 DBTimeout: db.Bolt.DBTimeout, 422 NoFreelistSync: db.Bolt.NoFreelistSync, 423 AutoCompact: db.Bolt.AutoCompact, 424 AutoCompactMinAge: db.Bolt.AutoCompactMinAge, 425 }) 426 if err != nil { 427 return nil, fmt.Errorf("error opening decayed log DB: %v", err) 428 } 429 closeFuncs[NSDecayedLogDB] = decayedLogBackend.Close 430 431 // The tower client is optional and might not be enabled by the user. We 432 // handle it being nil properly in the main server. 433 var towerClientBackend kvdb.Backend 434 if towerClientEnabled { 435 towerClientBackend, err = kvdb.GetBoltBackend( 436 &kvdb.BoltBackendConfig{ 437 DBPath: chanDBPath, 438 DBFileName: towerClientDBName, 439 DBTimeout: db.Bolt.DBTimeout, 440 NoFreelistSync: db.Bolt.NoFreelistSync, 441 AutoCompact: db.Bolt.AutoCompact, 442 AutoCompactMinAge: db.Bolt.AutoCompactMinAge, 443 }, 444 ) 445 if err != nil { 446 return nil, fmt.Errorf("error opening tower client "+ 447 "DB: %v", err) 448 } 449 closeFuncs[NSTowerClientDB] = towerClientBackend.Close 450 } 451 452 // The tower server is optional and might not be enabled by the user. We 453 // handle it being nil properly in the main server. 454 var towerServerBackend kvdb.Backend 455 if towerServerEnabled { 456 towerServerBackend, err = kvdb.GetBoltBackend( 457 &kvdb.BoltBackendConfig{ 458 DBPath: towerServerDBPath, 459 DBFileName: towerServerDBName, 460 DBTimeout: db.Bolt.DBTimeout, 461 NoFreelistSync: db.Bolt.NoFreelistSync, 462 AutoCompact: db.Bolt.AutoCompact, 463 AutoCompactMinAge: db.Bolt.AutoCompactMinAge, 464 }, 465 ) 466 if err != nil { 467 return nil, fmt.Errorf("error opening tower server "+ 468 "DB: %v", err) 469 } 470 closeFuncs[NSTowerServerDB] = towerServerBackend.Close 471 } 472 473 returnEarly = false 474 return &DatabaseBackends{ 475 GraphDB: boltBackend, 476 ChanStateDB: boltBackend, 477 HeightHintDB: boltBackend, 478 MacaroonDB: macaroonBackend, 479 DecayedLogDB: decayedLogBackend, 480 TowerClientDB: towerClientBackend, 481 TowerServerDB: towerServerBackend, 482 // When "running locally", LND will use the bbolt wallet.db to 483 // store the wallet located in the chain data dir, parametrized 484 // by the active network. 485 WalletDB: walletloader.LoaderWithLocalWalletDB( 486 walletDBPath, db.Bolt.NoFreelistSync, db.Bolt.DBTimeout, 487 ), 488 CloseFuncs: closeFuncs, 489 }, nil 490 } 491 492 // Compile-time constraint to ensure Workers implements the Validator interface. 493 var _ Validator = (*DB)(nil)