Skip to content

Commit

Permalink
Merge pull request #473 from ahrtr/revert_meta_20230426
Browse files Browse the repository at this point in the history
Migrate `surgery revert-meta-page` to cobra style command
  • Loading branch information
ahrtr authored May 3, 2023
2 parents 44af899 + 8164464 commit 97d2cc3
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 93 deletions.
57 changes: 57 additions & 0 deletions cmd/bbolt/command_surgery_cobra.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,54 @@ func newSurgeryCobraCommand() *cobra.Command {
Short: "surgery related commands",
}

surgeryCmd.AddCommand(newSurgeryRevertMetaPageCommand())
surgeryCmd.AddCommand(newSurgeryClearPageElementsCommand())
surgeryCmd.AddCommand(newSurgeryFreelistCommand())

return surgeryCmd
}

func newSurgeryRevertMetaPageCommand() *cobra.Command {
revertMetaPageCmd := &cobra.Command{
Use: "revert-meta-page <bbolt-file> [options]",
Short: "Revert the meta page to revert the changes performed by the latest transaction",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("db file path not provided")
}
if len(args) > 1 {
return errors.New("too many arguments")
}
return nil
},
RunE: surgeryRevertMetaPageFunc,
}

revertMetaPageCmd.Flags().StringVar(&surgeryTargetDBFilePath, "output", "", "path to the target db file")

return revertMetaPageCmd
}

func surgeryRevertMetaPageFunc(cmd *cobra.Command, args []string) error {
srcDBPath := args[0]

if err := checkDBPaths(srcDBPath, surgeryTargetDBFilePath); err != nil {
return err
}

if err := common.CopyFile(srcDBPath, surgeryTargetDBFilePath); err != nil {
return fmt.Errorf("[revert-meta-page] copy file failed: %w", err)
}

if err := surgeon.RevertMetaPage(surgeryTargetDBFilePath); err != nil {
return fmt.Errorf("revert-meta-page command failed: %w", err)
}

fmt.Fprintln(os.Stdout, "The meta page is reverted.")

return nil
}

func newSurgeryClearPageElementsCommand() *cobra.Command {
clearElementCmd := &cobra.Command{
Use: "clear-page-elements <bbolt-file> [options]",
Expand Down Expand Up @@ -210,3 +252,18 @@ func readMetaPage(path string) (*common.Meta, error) {
}
return common.LoadPageMeta(buf), nil
}

func checkDBPaths(srcPath, dstPath string) error {
_, err := os.Stat(srcPath)
if os.IsNotExist(err) {
return fmt.Errorf("source database file %q doesn't exist", srcPath)
} else if err != nil {
return fmt.Errorf("failed to open source database file %q: %v", srcPath, err)
}

if dstPath == "" {
return fmt.Errorf("output database path wasn't given, specify output database file path with --output option")
}

return nil
}
46 changes: 46 additions & 0 deletions cmd/bbolt/command_surgery_cobra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main_test

import (
"fmt"
"os"
"path/filepath"
"testing"

Expand All @@ -15,6 +16,51 @@ import (
"go.etcd.io/bbolt/internal/guts_cli"
)

func TestSurgery_RevertMetaPage(t *testing.T) {
pageSize := 4096
db := btesting.MustCreateDBWithOption(t, &bolt.Options{PageSize: pageSize})
srcPath := db.Path()

defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())

srcFile, err := os.Open(srcPath)
require.NoError(t, err)
defer srcFile.Close()

// Read both meta0 and meta1 from srcFile
srcBuf0 := readPage(t, srcPath, 0, pageSize)
srcBuf1 := readPage(t, srcPath, 1, pageSize)
meta0Page := common.LoadPageMeta(srcBuf0)
meta1Page := common.LoadPageMeta(srcBuf1)

// Get the non-active meta page
nonActiveSrcBuf := srcBuf0
nonActiveMetaPageId := 0
if meta0Page.Txid() > meta1Page.Txid() {
nonActiveSrcBuf = srcBuf1
nonActiveMetaPageId = 1
}
t.Logf("non active meta page id: %d", nonActiveMetaPageId)

// revert the meta page
rootCmd := main.NewRootCommand()
output := filepath.Join(t.TempDir(), "db")
rootCmd.SetArgs([]string{
"surgery", "revert-meta-page", srcPath,
"--output", output,
})
err = rootCmd.Execute()
require.NoError(t, err)

// read both meta0 and meta1 from dst file
dstBuf0 := readPage(t, output, 0, pageSize)
dstBuf1 := readPage(t, output, 1, pageSize)

// check result. Note we should skip the page ID
assert.Equal(t, pageDataWithoutPageId(nonActiveSrcBuf), pageDataWithoutPageId(dstBuf0))
assert.Equal(t, pageDataWithoutPageId(nonActiveSrcBuf), pageDataWithoutPageId(dstBuf1))
}

func TestSurgery_ClearPageElements_Without_Overflow(t *testing.T) {
testCases := []struct {
name string
Expand Down
52 changes: 0 additions & 52 deletions cmd/bbolt/surgery_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ func (cmd *surgeryCommand) Run(args ...string) error {
case "help":
fmt.Fprintln(cmd.Stderr, cmd.Usage())
return ErrUsage
case "revert-meta-page":
return newRevertMetaPageCommand(cmd).Run(args[1:]...)
case "copy-page":
return newCopyPageCommand(cmd).Run(args[1:]...)
case "clear-page":
Expand Down Expand Up @@ -90,56 +88,6 @@ Use "bbolt surgery [command] -h" for more information about a command.
`, "\n")
}

// revertMetaPageCommand represents the "surgery revert-meta-page" command execution.
type revertMetaPageCommand struct {
*surgeryCommand
}

// newRevertMetaPageCommand returns a revertMetaPageCommand.
func newRevertMetaPageCommand(m *surgeryCommand) *revertMetaPageCommand {
c := &revertMetaPageCommand{}
c.surgeryCommand = m
return c
}

// Run executes the command.
func (cmd *revertMetaPageCommand) Run(args ...string) error {
// Parse flags.
fs := flag.NewFlagSet("", flag.ContinueOnError)
help := fs.Bool("h", false, "")
if err := fs.Parse(args); err != nil {
return err
} else if *help {
fmt.Fprintln(cmd.Stderr, cmd.Usage())
return ErrUsage
}

if err := cmd.parsePathsAndCopyFile(fs); err != nil {
return fmt.Errorf("revertMetaPageCommand failed to parse paths and copy file: %w", err)
}

// revert the meta page
if err := surgeon.RevertMetaPage(cmd.dstPath); err != nil {
return fmt.Errorf("revertMetaPageCommand failed: %w", err)
}

fmt.Fprintln(cmd.Stdout, "The meta page is reverted.")
return nil
}

// Usage returns the help message.
func (cmd *revertMetaPageCommand) Usage() string {
return strings.TrimLeft(`
usage: bolt surgery revert-meta-page SRC DST
RevertMetaPage copies the database file at SRC to a newly created database
file at DST. Afterwards, it reverts the meta page on the newly created
database at DST.
The original database is left untouched.
`, "\n")
}

// copyPageCommand represents the "surgery copy-page" command execution.
type copyPageCommand struct {
*surgeryCommand
Expand Down
41 changes: 0 additions & 41 deletions cmd/bbolt/surgery_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,47 +14,6 @@ import (
"go.etcd.io/bbolt/internal/common"
)

func TestSurgery_RevertMetaPage(t *testing.T) {
pageSize := 4096
db := btesting.MustCreateDBWithOption(t, &bolt.Options{PageSize: pageSize})
srcPath := db.Path()

defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())

srcFile, err := os.Open(srcPath)
require.NoError(t, err)
defer srcFile.Close()

// Read both meta0 and meta1 from srcFile
srcBuf0 := readPage(t, srcPath, 0, pageSize)
srcBuf1 := readPage(t, srcPath, 1, pageSize)
meta0Page := common.LoadPageMeta(srcBuf0)
meta1Page := common.LoadPageMeta(srcBuf1)

// Get the non-active meta page
nonActiveSrcBuf := srcBuf0
nonActiveMetaPageId := 0
if meta0Page.Txid() > meta1Page.Txid() {
nonActiveSrcBuf = srcBuf1
nonActiveMetaPageId = 1
}
t.Logf("non active meta page id: %d", nonActiveMetaPageId)

// revert the meta page
dstPath := filepath.Join(t.TempDir(), "dstdb")
m := NewMain()
err = m.Run("surgery", "revert-meta-page", srcPath, dstPath)
require.NoError(t, err)

// read both meta0 and meta1 from dst file
dstBuf0 := readPage(t, dstPath, 0, pageSize)
dstBuf1 := readPage(t, dstPath, 1, pageSize)

// check result. Note we should skip the page ID
assert.Equal(t, pageDataWithoutPageId(nonActiveSrcBuf), pageDataWithoutPageId(dstBuf0))
assert.Equal(t, pageDataWithoutPageId(nonActiveSrcBuf), pageDataWithoutPageId(dstBuf1))
}

func TestSurgery_CopyPage(t *testing.T) {
pageSize := 4096
db := btesting.MustCreateDBWithOption(t, &bolt.Options{PageSize: pageSize})
Expand Down

0 comments on commit 97d2cc3

Please sign in to comment.