From 1050da350a21e29cfe5226c7dcf2fe87c8fb5e2d Mon Sep 17 00:00:00 2001 From: Eduard S Date: Tue, 2 Mar 2021 14:00:00 +0100 Subject: [PATCH] Wipe StateDB in cli command wipesql Also, in the cli commands `wipesql` and `discard`, always rebuild the current checkpoint of the stateDBs to make sure it's in a consistent non-corrupted state and do a reset afterwards. These commands will allow reverting the StateDB to a valid and consistent state in case a crash leaves the StateDB in a corrupted state. --- cli/node/main.go | 100 +++++++++++++++++++++++++++++++++++++++++++++-- db/kvdb/kvdb.go | 6 ++- 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/cli/node/main.go b/cli/node/main.go index 0138640..9a3f2b6 100644 --- a/cli/node/main.go +++ b/cli/node/main.go @@ -5,13 +5,16 @@ import ( "fmt" "os" "os/signal" + "path" "strings" ethKeystore "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/crypto" + "github.com/hermeznetwork/hermez-node/common" "github.com/hermeznetwork/hermez-node/config" dbUtils "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" + "github.com/hermeznetwork/hermez-node/db/kvdb" "github.com/hermeznetwork/hermez-node/db/l2db" "github.com/hermeznetwork/hermez-node/log" "github.com/hermeznetwork/hermez-node/node" @@ -72,6 +75,86 @@ func cmdImportKey(c *cli.Context) error { return nil } +func resetStateDBs(cfg *Config, batchNum common.BatchNum) error { + log.Infof("Reset Synchronizer StateDB to batchNum %v...", batchNum) + + // Manually make a checkpoint from batchNum to current to force current + // to be a valid checkpoint. This is useful because in case of a + // crash, current can be corrupted and the first thing that + // `kvdb.NewKVDB` does is read the current checkpoint, which wouldn't + // succeed in case of corruption. + dbPath := cfg.node.StateDB.Path + source := path.Join(dbPath, fmt.Sprintf("%s%d", kvdb.PathBatchNum, batchNum)) + current := path.Join(dbPath, kvdb.PathCurrent) + last := path.Join(dbPath, kvdb.PathLast) + if err := os.RemoveAll(last); err != nil { + return tracerr.Wrap(fmt.Errorf("os.RemoveAll: %w", err)) + } + if batchNum == 0 { + if err := os.RemoveAll(current); err != nil { + return tracerr.Wrap(fmt.Errorf("os.RemoveAll: %w", err)) + } + } else { + if err := kvdb.PebbleMakeCheckpoint(source, current); err != nil { + return tracerr.Wrap(fmt.Errorf("kvdb.PebbleMakeCheckpoint: %w", err)) + } + } + db, err := kvdb.NewKVDB(kvdb.Config{ + Path: dbPath, + NoGapsCheck: true, + NoLast: true, + }) + if err != nil { + return tracerr.Wrap(fmt.Errorf("kvdb.NewKVDB: %w", err)) + } + if err := db.Reset(batchNum); err != nil { + return tracerr.Wrap(fmt.Errorf("db.Reset: %w", err)) + } + + if cfg.mode == node.ModeCoordinator { + log.Infof("Wipe Coordinator StateDBs...") + + // We wipe the Coordinator StateDBs entirely (by deleting + // current and resetting to batchNum 0) because the Coordinator + // StateDBs are always reset from Synchronizer when the + // coordinator pipeline starts. + dbPath := cfg.node.Coordinator.TxSelector.Path + current := path.Join(dbPath, kvdb.PathCurrent) + if err := os.RemoveAll(current); err != nil { + return tracerr.Wrap(fmt.Errorf("os.RemoveAll: %w", err)) + } + db, err := kvdb.NewKVDB(kvdb.Config{ + Path: dbPath, + NoGapsCheck: true, + NoLast: true, + }) + if err != nil { + return tracerr.Wrap(fmt.Errorf("kvdb.NewKVDB: %w", err)) + } + if err := db.Reset(0); err != nil { + return tracerr.Wrap(fmt.Errorf("db.Reset: %w", err)) + } + + dbPath = cfg.node.Coordinator.BatchBuilder.Path + current = path.Join(dbPath, kvdb.PathCurrent) + if err := os.RemoveAll(current); err != nil { + return tracerr.Wrap(fmt.Errorf("os.RemoveAll: %w", err)) + } + db, err = kvdb.NewKVDB(kvdb.Config{ + Path: dbPath, + NoGapsCheck: true, + NoLast: true, + }) + if err != nil { + return tracerr.Wrap(fmt.Errorf("statedb.NewKVDB: %w", err)) + } + if err := db.Reset(0); err != nil { + return tracerr.Wrap(fmt.Errorf("db.Reset: %w", err)) + } + } + return nil +} + func cmdWipeSQL(c *cli.Context) error { _cfg, err := parseCli(c) if err != nil { @@ -80,7 +163,8 @@ func cmdWipeSQL(c *cli.Context) error { cfg := _cfg.node yes := c.Bool(flagYes) if !yes { - fmt.Print("*WARNING* Are you sure you want to delete the SQL DB? [y/N]: ") + fmt.Print("*WARNING* Are you sure you want to delete " + + "the SQL DB and StateDBs? [y/N]: ") var input string if _, err := fmt.Scanln(&input); err != nil { return tracerr.Wrap(err) @@ -102,7 +186,12 @@ func cmdWipeSQL(c *cli.Context) error { } log.Info("Wiping SQL DB...") if err := dbUtils.MigrationsDown(db.DB); err != nil { - return tracerr.Wrap(err) + return tracerr.Wrap(fmt.Errorf("dbUtils.MigrationsDown: %w", err)) + } + + log.Info("Wiping StateDBs...") + if err := resetStateDBs(_cfg, 0); err != nil { + return tracerr.Wrap(fmt.Errorf("resetStateDBs: %w", err)) } return nil } @@ -201,6 +290,11 @@ func cmdDiscard(c *cli.Context) error { return tracerr.Wrap(fmt.Errorf("l2DB.Reorg: %w", err)) } + log.Info("Resetting StateDBs...") + if err := resetStateDBs(_cfg, batchNum); err != nil { + return tracerr.Wrap(fmt.Errorf("resetStateDBs: %w", err)) + } + return nil } @@ -288,7 +382,7 @@ func main() { { Name: "wipesql", Aliases: []string{}, - Usage: "Wipe the SQL DB (HistoryDB and L2DB), " + + Usage: "Wipe the SQL DB (HistoryDB and L2DB) and the StateDBs, " + "leaving the DB in a clean state", Action: cmdWipeSQL, Flags: []cli.Flag{ diff --git a/db/kvdb/kvdb.go b/db/kvdb/kvdb.go index a367bf3..16a0588 100644 --- a/db/kvdb/kvdb.go +++ b/db/kvdb/kvdb.go @@ -544,10 +544,12 @@ func (k *KVDB) MakeCheckpointFromTo(fromBatchNum common.BatchNum, dest string) e // synchronizer to the same batchNum k.m.Lock() defer k.m.Unlock() - return pebbleMakeCheckpoint(source, dest) + return PebbleMakeCheckpoint(source, dest) } -func pebbleMakeCheckpoint(source, dest string) error { +// PebbleMakeCheckpoint is a hepler function to make a pebble checkpoint from +// source to dest. +func PebbleMakeCheckpoint(source, dest string) error { // Remove dest folder (if it exists) before doing the checkpoint if _, err := os.Stat(dest); os.IsNotExist(err) { } else if err != nil {