From 8df00752c03b52808a5f92fc87b0091a6dbbafef Mon Sep 17 00:00:00 2001 From: jholdstock Date: Thu, 7 Sep 2023 19:18:45 +0100 Subject: [PATCH] vspd: Consolidate background task timers. Using a single select loop for background tasks removes a lot of duplicated boilerplate code and helps to simplify shutdown logic. This does reduce the amount of things which can run in parallel, but that isn't of concern for vspd. The web server still runs in its own goroutine so its responsiveness won't be affected. --- cmd/vspd/vspd.go | 72 ++++++++++++++++++++------------------------ database/database.go | 28 ++--------------- 2 files changed, 35 insertions(+), 65 deletions(-) diff --git a/cmd/vspd/vspd.go b/cmd/vspd/vspd.go index e638742..f362175 100644 --- a/cmd/vspd/vspd.go +++ b/cmd/vspd/vspd.go @@ -118,11 +118,6 @@ func (v *vspd) run() int { // through an interrupt signal. shutdownCtx := shutdownListener(v.log) - // WaitGroup for services to signal when they have shutdown cleanly. - var shutdownWg sync.WaitGroup - - v.db.WritePeriodicBackups(shutdownCtx, &shutdownWg, v.cfg.BackupInterval) - // Run database integrity checks to ensure all data in database is present // and up-to-date. err := v.checkDatabaseIntegrity() @@ -139,19 +134,8 @@ func (v *vspd) run() int { // date. v.checkWalletConsistency() - // Run voting wallet consistency check periodically. - shutdownWg.Add(1) - go func() { - for { - select { - case <-shutdownCtx.Done(): - shutdownWg.Done() - return - case <-time.After(consistencyInterval): - v.checkWalletConsistency() - } - } - }() + // WaitGroup for services to signal when they have shutdown cleanly. + var shutdownWg sync.WaitGroup // Create and start webapi server. apiCfg := webapi.Config{ @@ -176,36 +160,46 @@ func (v *vspd) run() int { return 1 } - // Start handling blockConnected notifications from dcrd. + // Start all background tasks and notification handlers. shutdownWg.Add(1) go func() { - for { - select { - case <-shutdownCtx.Done(): - shutdownWg.Done() - return - case header := <-v.blockNotifChan: - v.log.Debugf("Block notification %d (%s)", header.Height, header.BlockHash().String()) - v.blockConnected() - } - } - }() + backupTicker := time.NewTicker(v.cfg.BackupInterval) + defer backupTicker.Stop() + consistencyTicker := time.NewTicker(consistencyInterval) + defer consistencyTicker.Stop() + dcrdTicker := time.NewTicker(dcrdInterval) + defer dcrdTicker.Stop() - // Loop forever attempting ensuring a dcrd connection is available, so - // notifications are received. - shutdownWg.Add(1) - go func() { for { select { - case <-shutdownCtx.Done(): - shutdownWg.Done() - return - case <-time.After(dcrdInterval): - // Ensure dcrd client is still connected. + + // Periodically write a database backup file. + case <-backupTicker.C: + err := v.db.WriteHotBackupFile() + if err != nil { + v.log.Errorf("Failed to write database backup: %v", err) + } + + // Run voting wallet consistency check periodically. + case <-consistencyTicker.C: + v.checkWalletConsistency() + + // Ensure dcrd client is connected so notifications are received. + case <-dcrdTicker.C: _, _, err := v.dcrd.Client() if err != nil { v.log.Errorf("dcrd connect error: %v", err) } + + // Handle blockconnected notifications from dcrd. + case header := <-v.blockNotifChan: + v.log.Debugf("Block notification %d (%s)", header.Height, header.BlockHash().String()) + v.blockConnected() + + // Handle shutdown request. + case <-shutdownCtx.Done(): + shutdownWg.Done() + return } } }() diff --git a/database/database.go b/database/database.go index d12e15e..c9ac930 100644 --- a/database/database.go +++ b/database/database.go @@ -5,7 +5,6 @@ package database import ( - "context" "crypto/ed25519" "crypto/rand" "fmt" @@ -61,9 +60,9 @@ const ( // backupMtx should be held when writing to the database backup file. var backupMtx sync.Mutex -// writeHotBackupFile writes a backup of the database file while the database +// WriteHotBackupFile writes a backup of the database file while the database // is still open. -func (vdb *VspDatabase) writeHotBackupFile() error { +func (vdb *VspDatabase) WriteHotBackupFile() error { backupMtx.Lock() defer backupMtx.Unlock() @@ -216,29 +215,6 @@ func Open(dbFile string, log slog.Logger, maxVoteChangeRecords int) (*VspDatabas 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 { - select { - case <-time.After(backupInterval): - err := vdb.writeHotBackupFile() - if err != nil { - vdb.log.Errorf("Failed to write database backup: %v", err) - } - case <-shutdownCtx.Done(): - shutdownWg.Done() - return - } - } - }() -} - // Close will close the database and, if requested, make a copy of the database // to the backup location. func (vdb *VspDatabase) Close(writeBackup bool) {