Skip to content

Commit

Permalink
client/{core,db}: options for core startup/shutdown
Browse files Browse the repository at this point in the history
This adds two new core.Config options aimed at improving startup
and shutdown depending on the user's use case:
 - NoAutoWalletLock instructs Core to skip locking the wallet on
   shutdown. This can be helpful if the user wants the wallet to remain
   unlocked. e.g. They started with the wallet unlocked, or they intend
   to start Core again and wish to avoid the time to unlock a locked
   wallet on startup.
 - NoAutoDBBackup instructs the DB to skip the creation of a backup DB
   file on shutdown. This is useful if the consumer is using the
   BackupDB method, or simply creating manual backups of the DB file
   after shutdown.

These options are intended for direct Core consumers, such as headless
applications that drive Core directly instead of via the browser UI.

These options are created so that the zero-value corresponds to legacy
behavior. That is, the defaults are the existing behavior. However, in
client/db/bolt, the BackupOnShutdown is reversed from Core's
NoAutoDBBackup flag. The NewDB constructor accepts an optional db.Opts
struct, and if it is not provided, a defaultOpts is used that has
BackupOnShutdown set to true.
  • Loading branch information
chappjc committed Dec 15, 2022
1 parent 2ae50bb commit c35709c
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 23 deletions.
40 changes: 29 additions & 11 deletions client/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,17 @@ type Config struct {
TorIsolation bool
// Language. A BCP 47 language tag. Default is en-US.
Language string

// NoAutoWalletLock instructs Core to skip locking the wallet on shutdown or
// logout. This can be helpful if the user wants the wallet to remain
// unlocked. e.g. They started with the wallet unlocked, or they intend to
// start Core again and wish to avoid the time to unlock a locked wallet on
// startup.
NoAutoWalletLock bool // zero value is legacy behavior
// NoAutoDBBackup instructs the DB to skip the creation of a backup DB file
// on shutdown. This is useful if the consumer is using the BackupDB method,
// or simply creating manual backups of the DB file after shutdown.
NoAutoDBBackup bool // zero value is legacy behavior
}

// Core is the core client application. Core manages DEX connections, wallets,
Expand Down Expand Up @@ -1234,7 +1245,10 @@ func New(cfg *Config) (*Core, error) {
if cfg.Logger == nil {
return nil, fmt.Errorf("Core.Config must specify a Logger")
}
boltDB, err := bolt.NewDB(cfg.DBPath, cfg.Logger.SubLogger("DB"))
dbOpts := bolt.Opts{
BackupOnShutdown: !cfg.NoAutoDBBackup,
}
boltDB, err := bolt.NewDB(cfg.DBPath, cfg.Logger.SubLogger("DB"), dbOpts)
if err != nil {
return nil, fmt.Errorf("database initialization error: %w", err)
}
Expand Down Expand Up @@ -1431,10 +1445,12 @@ func (c *Core) Run(ctx context.Context) {
if !wallet.connected() {
continue
}
symb := strings.ToUpper(unbip(assetID))
c.log.Infof("Locking %s wallet", symb)
if err := wallet.Lock(5 * time.Second); err != nil {
c.log.Errorf("Failed to lock %v wallet: %v", symb, err)
if !c.cfg.NoAutoWalletLock {
symb := strings.ToUpper(unbip(assetID))
c.log.Infof("Locking %s wallet", symb) // no-op if Logout did it
if err := wallet.Lock(5 * time.Second); err != nil {
c.log.Errorf("Failed to lock %v wallet: %v", symb, err)
}
}
wallet.Disconnect()
}
Expand Down Expand Up @@ -3802,12 +3818,14 @@ func (c *Core) Logout() error {
}

// Lock wallets
for _, w := range c.xcWallets() {
if w.connected() {
if err := w.Lock(5 * time.Second); err != nil {
// A failure to lock the wallet need not block the ability to
// lock the DEX accounts or shutdown Core gracefully.
c.log.Warnf("Unable to lock %v wallet: %v", unbip(w.AssetID), err)
if !c.cfg.NoAutoWalletLock {
for _, w := range c.xcWallets() {
if w.connected() {
if err := w.Lock(5 * time.Second); err != nil {
// A failure to lock the wallet need not block the ability to
// lock the DEX accounts or shutdown Core gracefully.
c.log.Warnf("Unable to lock %v wallet: %v", unbip(w.AssetID), err)
}
}
}
}
Expand Down
38 changes: 26 additions & 12 deletions client/db/bolt/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,28 @@ var (
disabledRateSourceKey = []byte("disabledRateSources")
)

// Opts is a set of options for the DB.
type Opts struct {
BackupOnShutdown bool // default is true
}

var defaultOpts = Opts{
BackupOnShutdown: true,
}

// BoltDB is a bbolt-based database backend for a DEX client. BoltDB satisfies
// the db.DB interface defined at decred.org/dcrdex/client/db.
type BoltDB struct {
*bbolt.DB
log dex.Logger
opts Opts
log dex.Logger
}

// Check that BoltDB satisfies the db.DB interface.
var _ dexdb.DB = (*BoltDB)(nil)

// NewDB is a constructor for a *BoltDB.
func NewDB(dbPath string, logger dex.Logger) (dexdb.DB, error) {
func NewDB(dbPath string, logger dex.Logger, opts ...Opts) (dexdb.DB, error) {
_, err := os.Stat(dbPath)
isNew := os.IsNotExist(err)

Expand All @@ -128,8 +138,12 @@ func NewDB(dbPath string, logger dex.Logger) (dexdb.DB, error) {

// Release the file lock on exit.
bdb := &BoltDB{
DB: db,
log: logger,
DB: db,
opts: defaultOpts,
log: logger,
}
if len(opts) > 0 {
bdb.opts = opts[0]
}

if err = bdb.makeTopLevelBuckets([][]byte{
Expand Down Expand Up @@ -183,11 +197,11 @@ func (db *BoltDB) Run(ctx context.Context) {
<-ctx.Done() // wait for shutdown to backup and compact

// Create a backup in the backups folder.
db.log.Infof("Backing up database...")
err := db.Backup()
if err != nil {
db.Close()
db.log.Errorf("Unable to backup database: %v", err)
if db.opts.BackupOnShutdown {
db.log.Infof("Backing up database...")
if err := db.Backup(); err != nil {
db.log.Errorf("Unable to backup database: %v", err)
}
}

// Only compact the current DB file if there are excessive free pages.
Expand All @@ -204,9 +218,9 @@ func (db *BoltDB) Run(ctx context.Context) {
// Compact the database by writing into a temporary file, closing the source
// DB, and overwriting the original with the compacted temporary file.
db.log.Infof("Compacting database...")
srcPath := db.Path() // before db.Close
compFile := srcPath + ".tmp" // deterministic on *same fs*
err = db.BackupTo(compFile, true, true) // overwrite and compact
srcPath := db.Path() // before db.Close
compFile := srcPath + ".tmp" // deterministic on *same fs*
err := db.BackupTo(compFile, true, true) // overwrite and compact
if err != nil {
db.Close()
db.log.Errorf("Unable to compact database: %v", err)
Expand Down

0 comments on commit c35709c

Please sign in to comment.