From 32790984fe53ceb8497d82eaea1bdadbd16158b5 Mon Sep 17 00:00:00 2001 From: jholdstock Date: Tue, 27 Sep 2022 14:50:49 +0100 Subject: [PATCH] Make database backups optional. Periodic database backups are no longer started automatically in database.Open(), and the backup written by database.Close() can now be disabled. Only vspd itself requires backups, they are not useful for test code or future upcoming tools such as vote-validator. --- .gitignore | 1 - cmd/vspd/main.go | 8 ++++++-- database/database.go | 30 +++++++++++++++++++----------- database/database_test.go | 22 ++++++---------------- webapi/setaltsignaddr_test.go | 20 ++++++-------------- 5 files changed, 37 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index 69eaef4..de71701 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ cov.out *mem.out /webapi/test.db /database/test.db -/database/test.db-backup # Go workspace go.work diff --git a/cmd/vspd/main.go b/cmd/vspd/main.go index 630b77c..4831b60 100644 --- a/cmd/vspd/main.go +++ b/cmd/vspd/main.go @@ -78,14 +78,18 @@ func run() int { defer log.Criticalf("Shutdown complete") // Open database. - db, err := database.Open(shutdownCtx, &shutdownWg, dbLog, cfg.dbPath, cfg.BackupInterval, maxVoteChangeRecords) + db, err := database.Open(cfg.dbPath, dbLog, maxVoteChangeRecords) if err != nil { log.Errorf("Database error: %v", err) requestShutdown() shutdownWg.Wait() return 1 } - defer db.Close() + + writeBackup := true + defer db.Close(writeBackup) + + db.WritePeriodicBackups(shutdownCtx, &shutdownWg, cfg.BackupInterval) // Create RPC client for local dcrd instance (used for broadcasting and // checking the status of fee transactions). diff --git a/database/database.go b/database/database.go index 0463f0e..62114fb 100644 --- a/database/database.go +++ b/database/database.go @@ -175,9 +175,7 @@ func CreateNew(dbFile, feeXPub string, log slog.Logger) error { // Open initializes and returns an open database. An error is returned if no // database file is found at the provided path. -func Open(shutdownCtx context.Context, shutdownWg *sync.WaitGroup, log slog.Logger, dbFile string, - backupInterval time.Duration, maxVoteChangeRecords int) (*VspDatabase, error) { - +func Open(dbFile string, log slog.Logger, maxVoteChangeRecords int) (*VspDatabase, error) { // Error if db file does not exist. This is needed because bolt.Open will // silently create a new empty database if the file does not exist. A new // vspd database should be created with the CreateNew() function. @@ -216,7 +214,15 @@ func Open(shutdownCtx context.Context, shutdownWg *sync.WaitGroup, log slog.Logg return nil, fmt.Errorf("upgrade failed: %w", err) } - // Periodically update the database backup file. + return vdb, nil +} + +// WritePeriodicBackups starts a goroutine to periodically write a database backup file. +// It can be stopped by cancelling the provided context, and uses the provided +// WaitGroup to signal that it has finished. +func (vdb *VspDatabase) WritePeriodicBackups(shutdownCtx context.Context, shutdownWg *sync.WaitGroup, + backupInterval time.Duration) { + shutdownWg.Add(1) go func() { for { @@ -224,7 +230,7 @@ func Open(shutdownCtx context.Context, shutdownWg *sync.WaitGroup, log slog.Logg case <-time.After(backupInterval): err := vdb.writeHotBackupFile() if err != nil { - log.Errorf("Failed to write database backup: %v", err) + vdb.log.Errorf("Failed to write database backup: %v", err) } case <-shutdownCtx.Done(): shutdownWg.Done() @@ -232,13 +238,11 @@ func Open(shutdownCtx context.Context, shutdownWg *sync.WaitGroup, log slog.Logg } } }() - - return vdb, nil } -// Close will close the database and then make a copy of the database to the -// backup location. -func (vdb *VspDatabase) Close() { +// Close will close the database and, if requested, make a copy of the database +// to the backup location. +func (vdb *VspDatabase) Close(writeBackup bool) { // Make a copy of the db path here because once the db is closed, db.Path // returns empty string. @@ -257,6 +261,10 @@ func (vdb *VspDatabase) Close() { vdb.log.Debug("Database closed") + if !writeBackup { + return + } + // Ensure the database backup file is up-to-date. backupPath := dbPath + "-backup" tempPath := backupPath + "~" @@ -291,7 +299,7 @@ func (vdb *VspDatabase) Close() { return } - vdb.log.Tracef("Database backup written to %s", backupPath) + vdb.log.Debugf("Database backup written to %s", backupPath) } // KeyPair retrieves the keypair used to sign API responses from the database. diff --git a/database/database_test.go b/database/database_test.go index b0ee598..721eb6e 100644 --- a/database/database_test.go +++ b/database/database_test.go @@ -5,7 +5,6 @@ package database import ( - "context" "crypto/ed25519" "io" "math/rand" @@ -13,7 +12,6 @@ import ( "net/http/httptest" "os" "strconv" - "sync" "testing" "time" @@ -22,7 +20,6 @@ import ( const ( testDb = "test.db" - backupDb = "test.db-backup" feeXPub = "feexpub" maxVoteChangeRecords = 3 @@ -70,7 +67,6 @@ func stdoutLogger() slog.Logger { func TestDatabase(t *testing.T) { // Ensure we are starting with a clean environment. os.Remove(testDb) - os.Remove(backupDb) // All sub-tests to run. tests := map[string]func(*testing.T){ @@ -95,14 +91,13 @@ func TestDatabase(t *testing.T) { for testName, test := range tests { // Create a new blank database for each sub-test. - var err error - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.TODO()) - err = CreateNew(testDb, feeXPub, log) + err := CreateNew(testDb, feeXPub, log) if err != nil { t.Fatalf("error creating test database: %v", err) } - db, err = Open(ctx, &wg, log, testDb, time.Hour, maxVoteChangeRecords) + + // Open the newly created database so it is ready to use. + db, err = Open(testDb, log, maxVoteChangeRecords) if err != nil { t.Fatalf("error opening test database: %v", err) } @@ -110,14 +105,9 @@ func TestDatabase(t *testing.T) { // Run the sub-test. t.Run(testName, test) - // Request database shutdown and wait for it to complete. - cancel() - wg.Wait() - - db.Close() - + writeBackup := false + db.Close(writeBackup) os.Remove(testDb) - os.Remove(backupDb) } } diff --git a/webapi/setaltsignaddr_test.go b/webapi/setaltsignaddr_test.go index 98cf6e4..6cf424e 100644 --- a/webapi/setaltsignaddr_test.go +++ b/webapi/setaltsignaddr_test.go @@ -6,7 +6,6 @@ package webapi import ( "bytes" - "context" "crypto/ed25519" "encoding/json" "errors" @@ -15,7 +14,6 @@ import ( "net/http" "net/http/httptest" "os" - "sync" "testing" "time" @@ -33,7 +31,6 @@ const ( // (base64 encoding). sigCharset = "0123456789ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/=" testDb = "test.db" - backupDb = "test.db-backup" ) var ( @@ -72,17 +69,15 @@ func TestMain(m *testing.M) { // Create a database to use. // Ensure we are starting with a clean environment. os.Remove(testDb) - os.Remove(backupDb) // Create a new blank database for all tests. - var err error - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - err = database.CreateNew(testDb, feeXPub, log) + err := database.CreateNew(testDb, feeXPub, log) if err != nil { panic(fmt.Errorf("error creating test database: %w", err)) } - db, err := database.Open(ctx, &wg, log, testDb, time.Hour, maxVoteChangeRecords) + + // Open the newly created database so it is ready to use. + db, err := database.Open(testDb, log, maxVoteChangeRecords) if err != nil { panic(fmt.Errorf("error opening test database: %w", err)) } @@ -97,12 +92,9 @@ func TestMain(m *testing.M) { // Run tests. exitCode := m.Run() - // Request database shutdown and wait for it to complete. - cancel() - wg.Wait() - db.Close() + writeBackup := false + db.Close(writeBackup) os.Remove(testDb) - os.Remove(backupDb) os.Exit(exitCode) }