Merge pull request #5362 from filecoin-project/feat/fullnode-restore
Implement full-node restore option
This commit is contained in:
commit
2b3d66da3b
@ -594,9 +594,13 @@ func (cs *ChainStore) takeHeaviestTipSet(ctx context.Context, ts *types.TipSet)
|
|||||||
// FlushValidationCache removes all results of block validation from the
|
// FlushValidationCache removes all results of block validation from the
|
||||||
// chain metadata store. Usually the first step after a new chain import.
|
// chain metadata store. Usually the first step after a new chain import.
|
||||||
func (cs *ChainStore) FlushValidationCache() error {
|
func (cs *ChainStore) FlushValidationCache() error {
|
||||||
|
return FlushValidationCache(cs.ds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FlushValidationCache(ds datastore.Batching) error {
|
||||||
log.Infof("clearing block validation cache...")
|
log.Infof("clearing block validation cache...")
|
||||||
|
|
||||||
dsWalk, err := cs.ds.Query(query.Query{
|
dsWalk, err := ds.Query(query.Query{
|
||||||
// Potential TODO: the validation cache is not a namespace on its own
|
// Potential TODO: the validation cache is not a namespace on its own
|
||||||
// but is rather constructed as prefixed-key `foo:bar` via .Instance(), which
|
// but is rather constructed as prefixed-key `foo:bar` via .Instance(), which
|
||||||
// in turn does not work with the filter, which can match only on `foo/bar`
|
// in turn does not work with the filter, which can match only on `foo/bar`
|
||||||
@ -616,7 +620,7 @@ func (cs *ChainStore) FlushValidationCache() error {
|
|||||||
return xerrors.Errorf("failed to run key listing query: %w", err)
|
return xerrors.Errorf("failed to run key listing query: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
batch, err := cs.ds.Batch()
|
batch, err := ds.Batch()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("failed to open a DS batch: %w", err)
|
return xerrors.Errorf("failed to open a DS batch: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,121 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
dstore "github.com/ipfs/go-datastore"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
"gopkg.in/cheggaaa/pb.v1"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-jsonrpc"
|
"github.com/filecoin-project/go-jsonrpc"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/store"
|
||||||
lcli "github.com/filecoin-project/lotus/cli"
|
lcli "github.com/filecoin-project/lotus/cli"
|
||||||
|
"github.com/filecoin-project/lotus/lib/backupds"
|
||||||
|
"github.com/filecoin-project/lotus/node/config"
|
||||||
"github.com/filecoin-project/lotus/node/repo"
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var backupCmd = lcli.BackupCmd("repo", repo.FullNode, func(cctx *cli.Context) (lcli.BackupAPI, jsonrpc.ClientCloser, error) {
|
var backupCmd = lcli.BackupCmd("repo", repo.FullNode, func(cctx *cli.Context) (lcli.BackupAPI, jsonrpc.ClientCloser, error) {
|
||||||
return lcli.GetFullNodeAPI(cctx)
|
return lcli.GetFullNodeAPI(cctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
func restore(cctx *cli.Context, r repo.Repo) error {
|
||||||
|
bf, err := homedir.Expand(cctx.Path("restore"))
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("expand backup file path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
st, err := os.Stat(bf)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("stat backup file (%s): %w", bf, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(bf)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("opening backup file: %w", err)
|
||||||
|
}
|
||||||
|
defer f.Close() // nolint:errcheck
|
||||||
|
|
||||||
|
lr, err := r.Lock(repo.FullNode)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer lr.Close() // nolint:errcheck
|
||||||
|
|
||||||
|
if cctx.IsSet("restore-config") {
|
||||||
|
log.Info("Restoring config")
|
||||||
|
|
||||||
|
cf, err := homedir.Expand(cctx.String("restore-config"))
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("expanding config path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = os.Stat(cf)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("stat config file (%s): %w", cf, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cerr error
|
||||||
|
err = lr.SetConfig(func(raw interface{}) {
|
||||||
|
rcfg, ok := raw.(*config.FullNode)
|
||||||
|
if !ok {
|
||||||
|
cerr = xerrors.New("expected miner config")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ff, err := config.FromFile(cf, rcfg)
|
||||||
|
if err != nil {
|
||||||
|
cerr = xerrors.Errorf("loading config: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*rcfg = *ff.(*config.FullNode)
|
||||||
|
})
|
||||||
|
if cerr != nil {
|
||||||
|
return cerr
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("setting config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.Warn("--restore-config NOT SET, WILL USE DEFAULT VALUES")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Restoring metadata backup")
|
||||||
|
|
||||||
|
mds, err := lr.Datastore("/metadata")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bar := pb.New64(st.Size())
|
||||||
|
br := bar.NewProxyReader(f)
|
||||||
|
bar.ShowTimeLeft = true
|
||||||
|
bar.ShowPercent = true
|
||||||
|
bar.ShowSpeed = true
|
||||||
|
bar.Units = pb.U_BYTES
|
||||||
|
|
||||||
|
bar.Start()
|
||||||
|
err = backupds.RestoreInto(br, mds)
|
||||||
|
bar.Finish()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("restoring metadata: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Resetting chainstore metadata")
|
||||||
|
|
||||||
|
chainHead := dstore.NewKey("head")
|
||||||
|
if err := mds.Delete(chainHead); err != nil {
|
||||||
|
return xerrors.Errorf("clearing chain head: %w", err)
|
||||||
|
}
|
||||||
|
if err := store.FlushValidationCache(mds); err != nil {
|
||||||
|
return xerrors.Errorf("clearing chain validation cache: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -144,6 +144,14 @@ var DaemonCmd = &cli.Command{
|
|||||||
Name: "api-max-req-size",
|
Name: "api-max-req-size",
|
||||||
Usage: "maximum API request size accepted by the JSON RPC server",
|
Usage: "maximum API request size accepted by the JSON RPC server",
|
||||||
},
|
},
|
||||||
|
&cli.PathFlag{
|
||||||
|
Name: "restore",
|
||||||
|
Usage: "restore from backup file",
|
||||||
|
},
|
||||||
|
&cli.PathFlag{
|
||||||
|
Name: "restore-config",
|
||||||
|
Usage: "config file to use when restoring from backup",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: func(cctx *cli.Context) error {
|
Action: func(cctx *cli.Context) error {
|
||||||
isLite := cctx.Bool("lite")
|
isLite := cctx.Bool("lite")
|
||||||
@ -203,9 +211,11 @@ var DaemonCmd = &cli.Command{
|
|||||||
r.SetConfigPath(cctx.String("config"))
|
r.SetConfigPath(cctx.String("config"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.Init(repo.FullNode); err != nil && err != repo.ErrRepoExists {
|
err = r.Init(repo.FullNode)
|
||||||
|
if err != nil && err != repo.ErrRepoExists {
|
||||||
return xerrors.Errorf("repo init error: %w", err)
|
return xerrors.Errorf("repo init error: %w", err)
|
||||||
}
|
}
|
||||||
|
freshRepo := err != repo.ErrRepoExists
|
||||||
|
|
||||||
if !isLite {
|
if !isLite {
|
||||||
if err := paramfetch.GetParams(lcli.ReqContext(cctx), build.ParametersJSON(), 0); err != nil {
|
if err := paramfetch.GetParams(lcli.ReqContext(cctx), build.ParametersJSON(), 0); err != nil {
|
||||||
@ -223,6 +233,15 @@ var DaemonCmd = &cli.Command{
|
|||||||
genBytes = build.MaybeGenesis()
|
genBytes = build.MaybeGenesis()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cctx.IsSet("restore") {
|
||||||
|
if !freshRepo {
|
||||||
|
return xerrors.Errorf("restoring from backup is only possible with a fresh repo!")
|
||||||
|
}
|
||||||
|
if err := restore(cctx, r); err != nil {
|
||||||
|
return xerrors.Errorf("restoring from backup: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
chainfile := cctx.String("import-chain")
|
chainfile := cctx.String("import-chain")
|
||||||
snapshot := cctx.String("import-snapshot")
|
snapshot := cctx.String("import-snapshot")
|
||||||
if chainfile != "" || snapshot != "" {
|
if chainfile != "" || snapshot != "" {
|
||||||
|
Loading…
Reference in New Issue
Block a user