From 42243673964615306f0aeb9e9d1baaa1f28d598b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 24 Jan 2025 10:29:17 +0800 Subject: [PATCH] cmd/XDC: add db commands: stats, compact, put, get, delete (#22014) --- cmd/XDC/chaincmd.go | 84 ++------------ cmd/XDC/dbcmd.go | 259 ++++++++++++++++++++++++++++++++++++++++++++ cmd/XDC/main.go | 9 ++ 3 files changed, 279 insertions(+), 73 deletions(-) create mode 100644 cmd/XDC/dbcmd.go diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go index d6fb97b913..4a5d115572 100644 --- a/cmd/XDC/chaincmd.go +++ b/cmd/XDC/chaincmd.go @@ -27,7 +27,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/console" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" @@ -132,19 +131,6 @@ if already existing.`, }, Description: ` The export-preimages command export hash preimages to an RLP encoded stream`, - } - removedbCommand = &cli.Command{ - Action: removeDB, - Name: "removedb", - Usage: "Remove blockchain and state databases", - ArgsUsage: " ", - Flags: []cli.Flag{ - utils.DataDirFlag, - utils.XDCXDataDirFlag, - utils.LightModeFlag, - }, - Description: ` -Remove blockchain and state databases`, } dumpCommand = &cli.Command{ Action: dump, @@ -224,8 +210,8 @@ func importChain(ctx *cli.Context) error { // Start metrics export if enabled utils.SetupMetrics(&cfg.Metrics) - chain, chainDb := utils.MakeChain(ctx, stack, false) - defer chainDb.Close() + chain, db := utils.MakeChain(ctx, stack, false) + defer db.Close() // Start periodically gathering memory profiles var peakMemAlloc, peakMemSys uint64 @@ -245,13 +231,17 @@ func importChain(ctx *cli.Context) error { // Import the chain start := time.Now() + var importErr error + if ctx.Args().Len() == 1 { if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { + importErr = err log.Error("Import error", "err", err) } } else { for _, arg := range ctx.Args().Slice() { if err := utils.ImportChain(chain, arg); err != nil { + importErr = err log.Error("Import error", "file", arg, "err", err) } } @@ -260,19 +250,7 @@ func importChain(ctx *cli.Context) error { fmt.Printf("Import done in %v.\n\n", time.Since(start)) // Output pre-compaction stats mostly to see the import trashing - db := chainDb - - stats, err := db.Get([]byte("leveldb.stats")) - if err != nil { - utils.Fatalf("Failed to read database stats: %v", err) - } - fmt.Println(stats) - - ioStats, err := db.Get([]byte("leveldb.iostats")) - if err != nil { - utils.Fatalf("Failed to read database iostats: %v", err) - } - fmt.Println(ioStats) + showLeveldbStats(db) // Print the memory statistics used by the importing mem := new(runtime.MemStats) @@ -283,31 +261,20 @@ func importChain(ctx *cli.Context) error { fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) - if ctx.IsSet(utils.NoCompactionFlag.Name) { + if ctx.Bool(utils.NoCompactionFlag.Name) { return nil } // Compact the entire database to more accurately measure disk io and print the stats start = time.Now() fmt.Println("Compacting entire database...") - if err = db.Compact(nil, nil); err != nil { + if err := db.Compact(nil, nil); err != nil { utils.Fatalf("Compaction failed: %v", err) } fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) - stats, err = db.Get([]byte("leveldb.stats")) - if err != nil { - utils.Fatalf("Failed to read database stats: %v", err) - } - fmt.Println(stats) - - ioStats, err = db.Get([]byte("leveldb.iostats")) - if err != nil { - utils.Fatalf("Failed to read database iostats: %v", err) - } - fmt.Println(ioStats) - - return nil + showLeveldbStats(db) + return importErr } func exportChain(ctx *cli.Context) error { @@ -385,35 +352,6 @@ func exportPreimages(ctx *cli.Context) error { return nil } -func removeDB(ctx *cli.Context) error { - stack, _ := makeConfigNode(ctx) - - for _, name := range []string{"chaindata", "lightchaindata"} { - // Ensure the database exists in the first place - logger := log.New("database", name) - - dbdir := stack.ResolvePath(name) - if !common.FileExist(dbdir) { - logger.Info("Database doesn't exist, skipping", "path", dbdir) - continue - } - // Confirm removal and execute - fmt.Println(dbdir) - confirm, err := console.Stdin.PromptConfirm("Remove this database?") - switch { - case err != nil: - utils.Fatalf("%v", err) - case !confirm: - logger.Warn("Database deletion aborted") - default: - start := time.Now() - os.RemoveAll(dbdir) - logger.Info("Database successfully deleted", "elapsed", common.PrettyDuration(time.Since(start))) - } - } - return nil -} - func dump(ctx *cli.Context) error { stack, _ := makeFullNode(ctx) defer stack.Close() diff --git a/cmd/XDC/dbcmd.go b/cmd/XDC/dbcmd.go new file mode 100644 index 0000000000..dddd91e7ea --- /dev/null +++ b/cmd/XDC/dbcmd.go @@ -0,0 +1,259 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "fmt" + "os" + "time" + + "github.com/XinFinOrg/XDPoSChain/cmd/utils" + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/common/hexutil" + "github.com/XinFinOrg/XDPoSChain/console" + "github.com/XinFinOrg/XDPoSChain/ethdb" + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/urfave/cli/v2" +) + +var ( + removedbCommand = &cli.Command{ + Action: removeDB, + Name: "removedb", + Usage: "Remove blockchain and state databases", + ArgsUsage: " ", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.XDCXDataDirFlag, + utils.LightModeFlag, + }, + Description: ` +Remove blockchain and state databases`, + } + dbCommand = &cli.Command{ + Name: "db", + Usage: "Low level database operations", + ArgsUsage: "", + Subcommands: []*cli.Command{ + dbStatCmd, + dbCompactCmd, + dbGetCmd, + dbDeleteCmd, + dbPutCmd, + }, + } + dbStatCmd = &cli.Command{ + Action: dbStats, + Name: "stats", + Usage: "Print leveldb statistics", + } + dbCompactCmd = &cli.Command{ + Action: dbCompact, + Name: "compact", + Usage: "Compact leveldb database. WARNING: May take a very long time", + Description: `This command performs a database compaction. +WARNING: This operation may take a very long time to finish, and may cause database +corruption if it is aborted during execution'!`, + } + dbGetCmd = &cli.Command{ + Action: dbGet, + Name: "get", + Usage: "Show the value of a database key", + ArgsUsage: "", + Description: "This command looks up the specified database key from the database.", + } + dbDeleteCmd = &cli.Command{ + Action: dbDelete, + Name: "delete", + Usage: "Delete a database key (WARNING: may corrupt your database)", + ArgsUsage: "", + Description: `This command deletes the specified database key from the database. +WARNING: This is a low-level operation which may cause database corruption!`, + } + dbPutCmd = &cli.Command{ + Action: dbPut, + Name: "put", + Usage: "Set the value of a database key (WARNING: may corrupt your database)", + ArgsUsage: " ", + Description: `This command sets a given database key to the given value. +WARNING: This is a low-level operation which may cause database corruption!`, + } +) + +func removeDB(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + for _, name := range []string{"chaindata", "lightchaindata"} { + // Ensure the database exists in the first place + logger := log.New("database", name) + + dbdir := stack.ResolvePath(name) + if !common.FileExist(dbdir) { + logger.Info("Database doesn't exist, skipping", "path", dbdir) + continue + } + confirmAndRemoveDB(dbdir, name) + } + return nil +} + +// confirmAndRemoveDB prompts the user for a last confirmation and removes the +// folder if accepted. +func confirmAndRemoveDB(database string, kind string) { + confirm, err := console.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database)) + switch { + case err != nil: + utils.Fatalf("%v", err) + case !confirm: + log.Warn("Database deletion aborted", "path", database) + default: + start := time.Now() + os.RemoveAll(database) + log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start))) + } +} + +func showLeveldbStats(db ethdb.Stater) { + if stats, err := db.Stat("leveldb.stats"); err != nil { + log.Warn("Failed to read database stats", "error", err) + } else { + fmt.Println(stats) + } + if ioStats, err := db.Stat("leveldb.iostats"); err != nil { + log.Warn("Failed to read database iostats", "error", err) + } else { + fmt.Println(ioStats) + } +} + +func dbStats(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, true) + defer db.Close() + + showLeveldbStats(db) + return nil +} + +func dbCompact(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, false) + defer db.Close() + + log.Info("Stats before compaction") + showLeveldbStats(db) + + log.Info("Triggering compaction") + if err := db.Compact(nil, nil); err != nil { + log.Info("Compact err", "error", err) + return err + } + + log.Info("Stats after compaction") + showLeveldbStats(db) + return nil +} + +// dbGet shows the value of a given database key +func dbGet(ctx *cli.Context) error { + if ctx.NArg() != 1 { + return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) + } + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, true) + defer db.Close() + + key, err := common.ParseHexOrString(ctx.Args().Get(0)) + if err != nil { + log.Info("Could not decode the key", "error", err) + return err + } + + data, err := db.Get(key) + if err != nil { + log.Info("Get operation failed", "key", fmt.Sprintf("%#x", key), "error", err) + return err + } + fmt.Printf("key %#x:\n\t%#x\n", key, data) + return nil +} + +// dbDelete deletes a key from the database +func dbDelete(ctx *cli.Context) error { + if ctx.NArg() != 1 { + return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) + } + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, false) + defer db.Close() + + key, err := common.ParseHexOrString(ctx.Args().Get(0)) + if err != nil { + log.Info("Could not decode the key", "error", err) + return err + } + data, err := db.Get(key) + if err == nil { + fmt.Printf("Previous value: %#x\n", data) + } + if err = db.Delete(key); err != nil { + log.Info("Delete operation returned an error", "key", fmt.Sprintf("%#x", key), "error", err) + return err + } + return nil +} + +// dbPut overwrite a value in the database +func dbPut(ctx *cli.Context) error { + if ctx.NArg() != 2 { + return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) + } + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, false) + defer db.Close() + + var ( + key []byte + value []byte + data []byte + err error + ) + key, err = common.ParseHexOrString(ctx.Args().Get(0)) + if err != nil { + log.Info("Could not decode the key", "error", err) + return err + } + value, err = hexutil.Decode(ctx.Args().Get(1)) + if err != nil { + log.Info("Could not decode the value", "error", err) + return err + } + data, err = db.Get(key) + if err == nil { + fmt.Printf("Previous value:\n%#x\n", data) + } + return db.Put(key, value) +} diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go index f3b9bdba13..21d8ee777a 100644 --- a/cmd/XDC/main.go +++ b/cmd/XDC/main.go @@ -188,6 +188,8 @@ func init() { initCommand, importCommand, exportCommand, + importPreimagesCommand, + exportPreimagesCommand, removedbCommand, dumpCommand, // See accountcmd.go: @@ -198,9 +200,16 @@ func init() { attachCommand, javascriptCommand, // See misccmd.go: + makecacheCommand, + makedagCommand, versionCommand, + licenseCommand, // See config.go dumpConfigCommand, + // see dbcmd.go + dbCommand, + // See cmd/utils/flags_legacy.go + utils.ShowDeprecated, } sort.Sort(cli.CommandsByName(app.Commands))