Skip to content

Commit

Permalink
Restore database CLI command (#8061)
Browse files Browse the repository at this point in the history
* restore beacon node db

* revert image name

* move restore out of the kv folder

* remove files from kv folder

* go mod tidy

* Remove usage of prometheus testutil

* add yes/no to prompt text

* restore slasher db

* organize imports

* go mod tidy

* restore validator db

* close slasher db

* defer close backup db in tests

* simplify function literal
  • Loading branch information
rkapka committed Dec 7, 2020
1 parent fbbdd94 commit 9d70527
Show file tree
Hide file tree
Showing 27 changed files with 541 additions and 19 deletions.
1 change: 1 addition & 0 deletions beacon-chain/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/db:go_default_library",
"//beacon-chain/flags:go_default_library",
"//beacon-chain/node:go_default_library",
"//shared/cmd:go_default_library",
Expand Down
8 changes: 8 additions & 0 deletions beacon-chain/db/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ go_library(
name = "go_default_library",
srcs = [
"alias.go",
"cmd.go",
"restore.go",
] + select({
":kafka_disabled": [
"db.go",
Expand All @@ -29,7 +31,13 @@ go_library(
"//beacon-chain/cache:go_default_library",
"//beacon-chain/db/iface:go_default_library",
"//beacon-chain/db/kv:go_default_library",
"//shared/cmd:go_default_library",
"//shared/fileutil:go_default_library",
"//shared/promptutil:go_default_library",
"//shared/tos:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
] + select({
"//conditions:default": [
"//beacon-chain/db/kafka:go_default_library",
Expand Down
32 changes: 32 additions & 0 deletions beacon-chain/db/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package db

import (
"github.com/prysmaticlabs/prysm/shared/cmd"
"github.com/prysmaticlabs/prysm/shared/tos"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)

// DatabaseCommands for Prysm beacon node.
var DatabaseCommands = &cli.Command{
Name: "db",
Category: "db",
Usage: "defines commands for interacting with eth2 beacon node database",
Subcommands: []*cli.Command{
{
Name: "restore",
Description: `restores a database from a backup file`,
Flags: cmd.WrapFlags([]cli.Flag{
cmd.RestoreSourceFileFlag,
cmd.RestoreTargetDirFlag,
}),
Before: tos.VerifyTosAcceptedOrPrompt,
Action: func(cliCtx *cli.Context) error {
if err := restore(cliCtx); err != nil {
logrus.Fatalf("Could not restore database: %v", err)
}
return nil
},
},
},
}
2 changes: 1 addition & 1 deletion beacon-chain/db/kv/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestStore_Backup(t *testing.T) {
require.NoError(t, db.Close(), "Failed to close database")

oldFilePath := filepath.Join(backupsPath, files[0].Name())
newFilePath := filepath.Join(backupsPath, databaseFileName)
newFilePath := filepath.Join(backupsPath, DatabaseFileName)
// We rename the file to match the database file name
// our NewKVStore function expects when opening a database.
require.NoError(t, os.Rename(oldFilePath, newFilePath))
Expand Down
14 changes: 9 additions & 5 deletions beacon-chain/db/kv/kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ const (
// VotesCacheSize with 1M validators will be 8MB.
VotesCacheSize = 1 << 23
// NumOfVotes specifies the vote cache size.
NumOfVotes = 1 << 20
databaseFileName = "beaconchain.db"
boltAllocSize = 8 * 1024 * 1024
NumOfVotes = 1 << 20
// BeaconNodeDbDirName is the name of the directory containing the beacon node database.
BeaconNodeDbDirName = "beaconchaindata"
// DatabaseFileName is the name of the beacon node database.
DatabaseFileName = "beaconchain.db"

boltAllocSize = 8 * 1024 * 1024
)

// BlockCacheSize specifies 1000 slots worth of blocks cached, which
Expand Down Expand Up @@ -56,7 +60,7 @@ func NewKVStore(dirPath string, stateSummaryCache *cache.StateSummaryCache) (*St
return nil, err
}
}
datafile := path.Join(dirPath, databaseFileName)
datafile := path.Join(dirPath, DatabaseFileName)
boltDB, err := bolt.Open(datafile, params.BeaconIoConfig().ReadWritePermissions, &bolt.Options{Timeout: 1 * time.Second, InitialMmapSize: 10e6})
if err != nil {
if errors.Is(err, bolt.ErrTimeout) {
Expand Down Expand Up @@ -134,7 +138,7 @@ func (s *Store) ClearDB() error {
return nil
}
prometheus.Unregister(createBoltCollector(s.db))
if err := os.Remove(path.Join(s.databasePath, databaseFileName)); err != nil {
if err := os.Remove(path.Join(s.databasePath, DatabaseFileName)); err != nil {
return errors.Wrap(err, "could not remove database file")
}
return nil
Expand Down
46 changes: 46 additions & 0 deletions beacon-chain/db/restore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package db

import (
"os"
"path"
"strings"

"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/shared/cmd"
"github.com/prysmaticlabs/prysm/shared/fileutil"
"github.com/prysmaticlabs/prysm/shared/promptutil"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)

const dbExistsYesNoPrompt = "A database file already exists in the target directory. " +
"Are you sure that you want to overwrite it? [y/n]"

func restore(cliCtx *cli.Context) error {
sourceFile := cliCtx.String(cmd.RestoreSourceFileFlag.Name)
targetDir := cliCtx.String(cmd.RestoreTargetDirFlag.Name)

restoreDir := path.Join(targetDir, kv.BeaconNodeDbDirName)
if fileutil.FileExists(path.Join(restoreDir, kv.DatabaseFileName)) {
resp, err := promptutil.ValidatePrompt(
os.Stdin, dbExistsYesNoPrompt, promptutil.ValidateYesOrNo,
)
if err != nil {
return errors.Wrap(err, "could not validate choice")
}
if strings.ToLower(resp) == "n" {
logrus.Info("Restore aborted")
return nil
}
}
if err := fileutil.MkdirAll(restoreDir); err != nil {
return err
}
if err := fileutil.CopyFile(sourceFile, path.Join(restoreDir, kv.DatabaseFileName)); err != nil {
return err
}

logrus.Info("Restore completed successfully")
return nil
}
71 changes: 71 additions & 0 deletions beacon-chain/db/restore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package db

import (
"context"
"flag"
"io/ioutil"
"os"
"path"
"testing"

"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/shared/cmd"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
logTest "github.com/sirupsen/logrus/hooks/test"
"github.com/urfave/cli/v2"
)

func TestRestore(t *testing.T) {
logHook := logTest.NewGlobal()
ctx := context.Background()

backupDb, err := kv.NewKVStore(t.TempDir(), cache.NewStateSummaryCache())
defer func() {
require.NoError(t, backupDb.Close())
}()
require.NoError(t, err)
head := testutil.NewBeaconBlock()
head.Block.Slot = 5000
require.NoError(t, backupDb.SaveBlock(ctx, head))
root, err := head.Block.HashTreeRoot()
require.NoError(t, err)
st := testutil.NewBeaconState()
require.NoError(t, backupDb.SaveState(ctx, st, root))
require.NoError(t, backupDb.SaveHeadBlockRoot(ctx, root))
require.NoError(t, err)
require.NoError(t, backupDb.Close())
// We rename the backup file so that we can later verify
// whether the restored db has been renamed correctly.
require.NoError(t, os.Rename(
path.Join(backupDb.DatabasePath(), kv.DatabaseFileName),
path.Join(backupDb.DatabasePath(), "backup.db")))

restoreDir := t.TempDir()
app := cli.App{}
set := flag.NewFlagSet("test", 0)
set.String(cmd.RestoreSourceFileFlag.Name, "", "")
set.String(cmd.RestoreTargetDirFlag.Name, "", "")
require.NoError(t, set.Set(cmd.RestoreSourceFileFlag.Name, path.Join(backupDb.DatabasePath(), "backup.db")))
require.NoError(t, set.Set(cmd.RestoreTargetDirFlag.Name, restoreDir))
cliCtx := cli.NewContext(&app, set, nil)

assert.NoError(t, restore(cliCtx))

files, err := ioutil.ReadDir(path.Join(restoreDir, kv.BeaconNodeDbDirName))
require.NoError(t, err)
assert.Equal(t, 1, len(files))
assert.Equal(t, kv.DatabaseFileName, files[0].Name())
restoredDb, err := kv.NewKVStore(path.Join(restoreDir, kv.BeaconNodeDbDirName), nil)
defer func() {
require.NoError(t, restoredDb.Close())
}()
require.NoError(t, err)
headBlock, err := restoredDb.HeadBlock(ctx)
require.NoError(t, err)
assert.Equal(t, uint64(5000), headBlock.Block.Slot, "Restored database has incorrect data")
assert.LogsContain(t, logHook, "Restore completed successfully")

}
6 changes: 6 additions & 0 deletions beacon-chain/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
gethlog "github.com/ethereum/go-ethereum/log"
golog "github.com/ipfs/go-log/v2"
joonix "github.com/joonix/log"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/beacon-chain/node"
"github.com/prysmaticlabs/prysm/shared/cmd"
Expand Down Expand Up @@ -100,6 +101,8 @@ var appFlags = []cli.Flag{
cmd.ChainConfigFileFlag,
cmd.GrpcMaxCallRecvMsgSizeFlag,
cmd.AcceptTosFlag,
cmd.RestoreSourceFileFlag,
cmd.RestoreTargetDirFlag,
}

func init() {
Expand All @@ -113,6 +116,9 @@ func main() {
app.Usage = "this is a beacon chain implementation for Ethereum 2.0"
app.Action = startNode
app.Version = version.GetVersion()
app.Commands = []*cli.Command{
db.DatabaseCommands,
}

app.Flags = appFlags

Expand Down
1 change: 1 addition & 0 deletions beacon-chain/node/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ go_library(
"//beacon-chain/cache:go_default_library",
"//beacon-chain/cache/depositcache:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/kv:go_default_library",
"//beacon-chain/flags:go_default_library",
"//beacon-chain/forkchoice:go_default_library",
"//beacon-chain/forkchoice/protoarray:go_default_library",
Expand Down
4 changes: 2 additions & 2 deletions beacon-chain/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice"
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
Expand Down Expand Up @@ -54,7 +55,6 @@ import (

var log = logrus.WithField("prefix", "node")

const beaconChainDBName = "beaconchaindata"
const testSkipPowFlag = "test-skip-pow"

// BeaconNode defines a struct that handles the services running a random beacon chain
Expand Down Expand Up @@ -291,7 +291,7 @@ func (b *BeaconNode) startForkChoice() {

func (b *BeaconNode) startDB(cliCtx *cli.Context) error {
baseDir := cliCtx.String(cmd.DataDirFlag.Name)
dbPath := filepath.Join(baseDir, beaconChainDBName)
dbPath := filepath.Join(baseDir, kv.BeaconNodeDbDirName)
clearDB := cliCtx.Bool(cmd.ClearDB.Name)
forceClearDB := cliCtx.Bool(cmd.ForceClearDB.Name)

Expand Down
2 changes: 2 additions & 0 deletions beacon-chain/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ var appHelpFlagGroups = []flagGroup{
cmd.ChainConfigFileFlag,
cmd.GrpcMaxCallRecvMsgSizeFlag,
cmd.AcceptTosFlag,
cmd.RestoreSourceFileFlag,
cmd.RestoreTargetDirFlag,
},
},
{
Expand Down
12 changes: 12 additions & 0 deletions shared/cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,18 @@ var (
Name: "accept-terms-of-use",
Usage: "Accept Terms and Conditions (for non-interactive environments)",
}
// RestoreSourceFileFlag specifies the filepath to the backed-up database file
// which will be used to restore the database.
RestoreSourceFileFlag = &cli.StringFlag{
Name: "restore-source-file",
Usage: "Filepath to the backed-up database file which will be used to restore the database",
}
// RestoreTargetDirFlag specifies the target directory of the restored database.
RestoreTargetDirFlag = &cli.StringFlag{
Name: "restore-target-dir",
Usage: "Target directory of the restored database",
Value: DefaultDataDir(),
}
)

// LoadFlagsFromConfig sets flags values from config file if ConfigFileFlag is set.
Expand Down
1 change: 1 addition & 0 deletions slasher/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ go_library(
"//shared/logutil:go_default_library",
"//shared/tos:go_default_library",
"//shared/version:go_default_library",
"//slasher/db:go_default_library",
"//slasher/flags:go_default_library",
"//slasher/node:go_default_library",
"@com_github_joonix_log//:go_default_library",
Expand Down
23 changes: 21 additions & 2 deletions slasher/db/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,38 @@ go_library(
name = "go_default_library",
srcs = [
"alias.go",
"cmd.go",
"db.go",
"restore.go",
],
importpath = "github.com/prysmaticlabs/prysm/slasher/db",
visibility = ["//slasher:__subpackages__"],
deps = [
"//shared/cmd:go_default_library",
"//shared/fileutil:go_default_library",
"//shared/promptutil:go_default_library",
"//shared/tos:go_default_library",
"//slasher/db/iface:go_default_library",
"//slasher/db/kv:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
)

go_test(
name = "go_default_test",
srcs = ["db_test.go"],
srcs = [
"db_test.go",
"restore_test.go",
],
embed = [":go_default_library"],
deps = ["//slasher/db/kv:go_default_library"],
deps = [
"//shared/cmd:go_default_library",
"//shared/testutil/assert:go_default_library",
"//shared/testutil/require:go_default_library",
"//slasher/db/kv:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
)
32 changes: 32 additions & 0 deletions slasher/db/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package db

import (
"github.com/prysmaticlabs/prysm/shared/cmd"
"github.com/prysmaticlabs/prysm/shared/tos"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)

// DatabaseCommands for Prysm slasher.
var DatabaseCommands = &cli.Command{
Name: "db",
Category: "db",
Usage: "defines commands for interacting with eth2 slasher database",
Subcommands: []*cli.Command{
{
Name: "restore",
Description: `restores a database from a backup file`,
Flags: cmd.WrapFlags([]cli.Flag{
cmd.RestoreSourceFileFlag,
cmd.RestoreTargetDirFlag,
}),
Before: tos.VerifyTosAcceptedOrPrompt,
Action: func(cliCtx *cli.Context) error {
if err := restore(cliCtx); err != nil {
logrus.Fatalf("Could not restore database: %v", err)
}
return nil
},
},
},
}

0 comments on commit 9d70527

Please sign in to comment.