cmd/XDC: add db commands: stats, compact, put, get, delete (#22014)

This commit is contained in:
Daniel Liu 2025-01-24 10:29:17 +08:00
parent 742103c19c
commit 4224367396
3 changed files with 279 additions and 73 deletions

View file

@ -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()

259
cmd/XDC/dbcmd.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
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: "<hex-encoded key>",
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: "<hex-encoded key>",
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: "<hex-encoded key> <hex-encoded value>",
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)
}

View file

@ -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))