Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add local snapshots management commands #16067

Merged
merged 15 commits into from
May 11, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/bank) [#15764](https://github.com/cosmos/cosmos-sdk/pull/15764) Speedup x/bank InitGenesis
* (x/auth) [#15867](https://github.com/cosmos/cosmos-sdk/pull/15867) Support better logging for signature verification failure.
* (types/query) [#16041](https://github.com/cosmos/cosmos-sdk/pull/16041) change pagination max limit to a variable in order to be modifed by application devs
* (store) [#16067](https://github.com/cosmos/cosmos-sdk/pull/16067) Add local snapshots management commands.

### State Machine Breaking

Expand Down
20 changes: 20 additions & 0 deletions client/snapshot/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package snapshot

import (
servertypes "github.com/cosmos/cosmos-sdk/server/types"
"github.com/spf13/cobra"
)

// Cmd returns the snapshots group command
func Cmd(appCreator servertypes.AppCreator) *cobra.Command {
cmd := &cobra.Command{
Use: "snapshots",
Short: "Manage local snapshots",
Long: "Manage local snapshots",
}
cmd.AddCommand(
ListSnapshotsCmd,
RestoreSnapshotCmd(appCreator),
)
return cmd
}
31 changes: 31 additions & 0 deletions client/snapshot/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package snapshot

import (
"fmt"

"github.com/cosmos/cosmos-sdk/server"
"github.com/spf13/cobra"
)

// ListSnapshotsCmd returns the command to list local snapshots
var ListSnapshotsCmd = &cobra.Command{
Use: "list",
Short: "List snapshots",
Long: "List snapshots",
yihuang marked this conversation as resolved.
Show resolved Hide resolved
RunE: func(cmd *cobra.Command, args []string) error {
ctx := server.GetServerContextFromCmd(cmd)
snapshotStore, err := server.GetSnapshotStore(ctx.Viper)
if err != nil {
return err
}
snapshots, err := snapshotStore.List()
if err != nil {
return fmt.Errorf("failed to list snapshots: %w", err)
}
for _, snapshot := range snapshots {
fmt.Println("height:", snapshot.Height, "format:", snapshot.Format, "chunks:", snapshot.Chunks)
}

return nil
},
}
51 changes: 51 additions & 0 deletions client/snapshot/restore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package snapshot

import (
"path/filepath"
"strconv"

"cosmossdk.io/log"
"github.com/spf13/cobra"

dbm "github.com/cosmos/cosmos-db"
"github.com/cosmos/cosmos-sdk/server"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
)

// RestoreSnapshotCmd returns a command to restore a snapshot
func RestoreSnapshotCmd(appCreator servertypes.AppCreator) *cobra.Command {
cmd := &cobra.Command{
Use: "restore <height> <format>",
Short: "Restore app state from local snapshot",
Long: "Restore app state from local snapshot",
yihuang marked this conversation as resolved.
Show resolved Hide resolved
RunE: func(cmd *cobra.Command, args []string) error {
ctx := server.GetServerContextFromCmd(cmd)

height, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}
format, err := strconv.ParseUint(args[1], 10, 32)
if err != nil {
return err
}

home := ctx.Config.RootDir
db, err := openDB(home, server.GetAppDBBackend(ctx.Viper))
if err != nil {
return err
}
logger := log.NewLogger(cmd.OutOrStdout())
app := appCreator(logger, db, nil, ctx.Viper)

sm := app.SnapshotManager()
return sm.RestoreLocalSnapshot(height, uint32(format))
},
}
return cmd
}

func openDB(rootDir string, backendType dbm.BackendType) (dbm.DB, error) {
dataDir := filepath.Join(rootDir, "data")
return dbm.NewDB("application", backendType, dataDir)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ require (
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -636,8 +636,9 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
Expand Down
4 changes: 4 additions & 0 deletions server/types/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"

"cosmossdk.io/log"
"cosmossdk.io/store/snapshots"
storetypes "cosmossdk.io/store/types"
abci "github.com/cometbft/cometbft/abci/types"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
Expand Down Expand Up @@ -54,6 +55,9 @@ type (

// CommitMultiStore return the multistore instance
CommitMultiStore() storetypes.CommitMultiStore

// Return the snapshot manager
SnapshotManager() *snapshots.Manager
}

// AppCreator is a function that allows us to lazily initialize an
Expand Down
30 changes: 20 additions & 10 deletions server/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,16 +468,7 @@ func DefaultBaseappOptions(appOpts types.AppOptions) []func(*baseapp.BaseApp) {
chainID = appGenesis.ChainID
}

snapshotDir := filepath.Join(homeDir, "data", "snapshots")
if err = os.MkdirAll(snapshotDir, os.ModePerm); err != nil {
panic(fmt.Errorf("failed to create snapshots directory: %w", err))
}

snapshotDB, err := dbm.NewDB("metadata", GetAppDBBackend(appOpts), snapshotDir)
if err != nil {
panic(err)
}
snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir)
snapshotStore, err := GetSnapshotStore(appOpts)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -508,3 +499,22 @@ func DefaultBaseappOptions(appOpts types.AppOptions) []func(*baseapp.BaseApp) {
baseapp.SetChainID(chainID),
}
}

func GetSnapshotStore(appOpts types.AppOptions) (*snapshots.Store, error) {
homeDir := cast.ToString(appOpts.Get(flags.FlagHome))
snapshotDir := filepath.Join(homeDir, "data", "snapshots")
if err := os.MkdirAll(snapshotDir, os.ModePerm); err != nil {
return nil, fmt.Errorf("failed to create snapshots directory: %w", err)
}

snapshotDB, err := dbm.NewDB("metadata", GetAppDBBackend(appOpts), snapshotDir)
if err != nil {
return nil, err
}
snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir)
if err != nil {
return nil, err
}

return snapshotStore, nil
}
2 changes: 2 additions & 0 deletions simapp/simd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/pruning"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/snapshot"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
serverconfig "github.com/cosmos/cosmos-sdk/server/config"
Expand Down Expand Up @@ -191,6 +192,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig, b
debug.Cmd(),
confixcmd.ConfigCommand(),
pruning.Cmd(newApp),
snapshot.Cmd(newApp),
)

server.AddCommands(rootCmd, simapp.DefaultNodeHome, newApp, appExport, addModuleInitFlags)
Expand Down
2 changes: 2 additions & 0 deletions simapp/simd/cmd/root_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/pruning"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/snapshot"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/server"
Expand Down Expand Up @@ -207,6 +208,7 @@ func initRootCmd(
debug.Cmd(),
confixcmd.ConfigCommand(),
pruning.Cmd(newApp),
snapshot.Cmd(newApp),
)

server.AddCommands(rootCmd, simapp.DefaultNodeHome, newApp, appExport, addModuleInitFlags)
Expand Down
9 changes: 9 additions & 0 deletions store/snapshots/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,15 @@ func (m *Manager) RestoreChunk(chunk []byte) (bool, error) {
return false, nil
}

// RestoreLocalSnapshot restores app state from a local snapshot.
func (m *Manager) RestoreLocalSnapshot(height uint64, format uint32) error {
snapshot, ch, err := m.store.Load(height, format)
if err != nil {
Comment on lines 418 to +424
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change potentially affects state.

Call sequence:

(*cosmossdk.io/store/snapshots.Manager).sortedExtensionNames (store/snapshots/manager.go:422)
(*cosmossdk.io/store/snapshots.Manager).createSnapshot (store/snapshots/manager.go:192)
(*cosmossdk.io/store/snapshots.Manager).Create (store/snapshots/manager.go:161)
(*cosmossdk.io/store/snapshots.Manager).snapshot (store/snapshots/manager.go:460)
(*cosmossdk.io/store/snapshots.Manager).SnapshotIfApplicable (store/snapshots/manager.go:444)
(*github.com/cosmos/cosmos-sdk/baseapp.BaseApp).Commit (store/snapshots/manager.go:458)

return err
}
return m.restoreSnapshot(*snapshot, ch)
}

// sortedExtensionNames sort extension names for deterministic iteration.
func (m *Manager) sortedExtensionNames() []string {
names := make([]string, 0, len(m.extensions))
Expand Down