cmd/geth: add db commands stats, compact, put, get, delete (#22014)
This PR introduces: - db.put to put a value into the database - db.get to read a value from the database - db.delete to delete a value from the database - db.stats to check compaction info from the database - db.compact to trigger a db compaction It also moves inspectdb to db.inspect.
This commit is contained in:
parent
3ecfdccd9a
commit
c4a2b682ff
@ -20,7 +20,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
@ -28,7 +27,6 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/console/prompt"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
@ -170,18 +168,6 @@ The export-preimages command export hash preimages to an RLP encoded stream`,
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
The first argument must be the directory containing the blockchain to download from`,
|
||||
}
|
||||
removedbCommand = cli.Command{
|
||||
Action: utils.MigrateFlags(removeDB),
|
||||
Name: "removedb",
|
||||
Usage: "Remove blockchain and state databases",
|
||||
ArgsUsage: " ",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
},
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
Remove blockchain and state databases`,
|
||||
}
|
||||
dumpCommand = cli.Command{
|
||||
Action: utils.MigrateFlags(dump),
|
||||
@ -202,25 +188,6 @@ Remove blockchain and state databases`,
|
||||
The arguments are interpreted as block numbers or hashes.
|
||||
Use "ethereum dump 0" to dump the genesis block.`,
|
||||
}
|
||||
inspectCommand = cli.Command{
|
||||
Action: utils.MigrateFlags(inspect),
|
||||
Name: "inspect",
|
||||
Usage: "Inspect the storage size for each type of data in the database",
|
||||
ArgsUsage: " ",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.AncientFlag,
|
||||
utils.CacheFlag,
|
||||
utils.MainnetFlag,
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.YoloV3Flag,
|
||||
utils.LegacyTestnetFlag,
|
||||
utils.SyncModeFlag,
|
||||
},
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
}
|
||||
)
|
||||
|
||||
// initGenesis will initialise the given JSON format genesis file and writes it as
|
||||
@ -323,17 +290,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
|
||||
stats, err := db.Stat("leveldb.stats")
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to read database stats: %v", err)
|
||||
}
|
||||
fmt.Println(stats)
|
||||
|
||||
ioStats, err := db.Stat("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)
|
||||
@ -351,22 +308,12 @@ func importChain(ctx *cli.Context) error {
|
||||
// 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.Stat("leveldb.stats")
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to read database stats: %v", err)
|
||||
}
|
||||
fmt.Println(stats)
|
||||
|
||||
ioStats, err = db.Stat("leveldb.iostats")
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to read database iostats: %v", err)
|
||||
}
|
||||
fmt.Println(ioStats)
|
||||
showLeveldbStats(db)
|
||||
return importErr
|
||||
}
|
||||
|
||||
@ -499,66 +446,6 @@ func copyDb(ctx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeDB(ctx *cli.Context) error {
|
||||
stack, config := makeConfigNode(ctx)
|
||||
|
||||
// Remove the full node state database
|
||||
path := stack.ResolvePath("chaindata")
|
||||
if common.FileExist(path) {
|
||||
confirmAndRemoveDB(path, "full node state database")
|
||||
} else {
|
||||
log.Info("Full node state database missing", "path", path)
|
||||
}
|
||||
// Remove the full node ancient database
|
||||
path = config.Eth.DatabaseFreezer
|
||||
switch {
|
||||
case path == "":
|
||||
path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
|
||||
case !filepath.IsAbs(path):
|
||||
path = config.Node.ResolvePath(path)
|
||||
}
|
||||
if common.FileExist(path) {
|
||||
confirmAndRemoveDB(path, "full node ancient database")
|
||||
} else {
|
||||
log.Info("Full node ancient database missing", "path", path)
|
||||
}
|
||||
// Remove the light node database
|
||||
path = stack.ResolvePath("lightchaindata")
|
||||
if common.FileExist(path) {
|
||||
confirmAndRemoveDB(path, "light node database")
|
||||
} else {
|
||||
log.Info("Light node database missing", "path", path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// confirmAndRemoveDB prompts the user for a last confirmation and removes the
|
||||
// folder if accepted.
|
||||
func confirmAndRemoveDB(database string, kind string) {
|
||||
confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
|
||||
switch {
|
||||
case err != nil:
|
||||
utils.Fatalf("%v", err)
|
||||
case !confirm:
|
||||
log.Info("Database deletion skipped", "path", database)
|
||||
default:
|
||||
start := time.Now()
|
||||
filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
|
||||
// If we're at the top level folder, recurse into
|
||||
if path == database {
|
||||
return nil
|
||||
}
|
||||
// Delete all the files, but not subfolders
|
||||
if !info.IsDir() {
|
||||
os.Remove(path)
|
||||
return nil
|
||||
}
|
||||
return filepath.SkipDir
|
||||
})
|
||||
log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
}
|
||||
|
||||
func dump(ctx *cli.Context) error {
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
@ -598,16 +485,6 @@ func dump(ctx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func inspect(ctx *cli.Context) error {
|
||||
node, _ := makeConfigNode(ctx)
|
||||
defer node.Close()
|
||||
|
||||
_, chainDb := utils.MakeChain(ctx, node, true)
|
||||
defer chainDb.Close()
|
||||
|
||||
return rawdb.InspectDatabase(chainDb)
|
||||
}
|
||||
|
||||
// hashish returns true for strings that look like hashes.
|
||||
func hashish(x string) bool {
|
||||
_, err := strconv.Atoi(x)
|
||||
|
341
cmd/geth/dbcmd.go
Normal file
341
cmd/geth/dbcmd.go
Normal file
@ -0,0 +1,341 @@
|
||||
// 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"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/console/prompt"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/leveldb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
removedbCommand = cli.Command{
|
||||
Action: utils.MigrateFlags(removeDB),
|
||||
Name: "removedb",
|
||||
Usage: "Remove blockchain and state databases",
|
||||
ArgsUsage: "",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
},
|
||||
Category: "DATABASE COMMANDS",
|
||||
Description: `
|
||||
Remove blockchain and state databases`,
|
||||
}
|
||||
dbCommand = cli.Command{
|
||||
Name: "db",
|
||||
Usage: "Low level database operations",
|
||||
ArgsUsage: "",
|
||||
Category: "DATABASE COMMANDS",
|
||||
Subcommands: []cli.Command{
|
||||
dbInspectCmd,
|
||||
dbStatCmd,
|
||||
dbCompactCmd,
|
||||
dbGetCmd,
|
||||
dbDeleteCmd,
|
||||
dbPutCmd,
|
||||
},
|
||||
}
|
||||
dbInspectCmd = cli.Command{
|
||||
Action: utils.MigrateFlags(inspect),
|
||||
Name: "inspect",
|
||||
ArgsUsage: "<prefix> <start>",
|
||||
|
||||
Usage: "Inspect the storage size for each type of data in the database",
|
||||
Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`,
|
||||
}
|
||||
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, config := makeConfigNode(ctx)
|
||||
|
||||
// Remove the full node state database
|
||||
path := stack.ResolvePath("chaindata")
|
||||
if common.FileExist(path) {
|
||||
confirmAndRemoveDB(path, "full node state database")
|
||||
} else {
|
||||
log.Info("Full node state database missing", "path", path)
|
||||
}
|
||||
// Remove the full node ancient database
|
||||
path = config.Eth.DatabaseFreezer
|
||||
switch {
|
||||
case path == "":
|
||||
path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
|
||||
case !filepath.IsAbs(path):
|
||||
path = config.Node.ResolvePath(path)
|
||||
}
|
||||
if common.FileExist(path) {
|
||||
confirmAndRemoveDB(path, "full node ancient database")
|
||||
} else {
|
||||
log.Info("Full node ancient database missing", "path", path)
|
||||
}
|
||||
// Remove the light node database
|
||||
path = stack.ResolvePath("lightchaindata")
|
||||
if common.FileExist(path) {
|
||||
confirmAndRemoveDB(path, "light node database")
|
||||
} else {
|
||||
log.Info("Light node database missing", "path", path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// confirmAndRemoveDB prompts the user for a last confirmation and removes the
|
||||
// folder if accepted.
|
||||
func confirmAndRemoveDB(database string, kind string) {
|
||||
confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
|
||||
switch {
|
||||
case err != nil:
|
||||
utils.Fatalf("%v", err)
|
||||
case !confirm:
|
||||
log.Info("Database deletion skipped", "path", database)
|
||||
default:
|
||||
start := time.Now()
|
||||
filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
|
||||
// If we're at the top level folder, recurse into
|
||||
if path == database {
|
||||
return nil
|
||||
}
|
||||
// Delete all the files, but not subfolders
|
||||
if !info.IsDir() {
|
||||
os.Remove(path)
|
||||
return nil
|
||||
}
|
||||
return filepath.SkipDir
|
||||
})
|
||||
log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
}
|
||||
|
||||
func inspect(ctx *cli.Context) error {
|
||||
var (
|
||||
prefix []byte
|
||||
start []byte
|
||||
)
|
||||
if ctx.NArg() > 2 {
|
||||
return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage)
|
||||
}
|
||||
if ctx.NArg() >= 1 {
|
||||
if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil {
|
||||
return fmt.Errorf("failed to hex-decode 'prefix': %v", err)
|
||||
} else {
|
||||
prefix = d
|
||||
}
|
||||
}
|
||||
if ctx.NArg() >= 2 {
|
||||
if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil {
|
||||
return fmt.Errorf("failed to hex-decode 'start': %v", err)
|
||||
} else {
|
||||
start = d
|
||||
}
|
||||
}
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
_, chainDb := utils.MakeChain(ctx, stack, true)
|
||||
defer chainDb.Close()
|
||||
|
||||
return rawdb.InspectDatabase(chainDb, prefix, 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()
|
||||
path := stack.ResolvePath("chaindata")
|
||||
db, err := leveldb.NewCustom(path, "", func(options *opt.Options) {
|
||||
options.ReadOnly = true
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
showLeveldbStats(db)
|
||||
err = db.Close()
|
||||
if err != nil {
|
||||
log.Info("Close err", "error", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dbCompact(ctx *cli.Context) error {
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
path := stack.ResolvePath("chaindata")
|
||||
cache := ctx.GlobalInt(utils.CacheFlag.Name) * ctx.GlobalInt(utils.CacheDatabaseFlag.Name) / 100
|
||||
db, err := leveldb.NewCustom(path, "", func(options *opt.Options) {
|
||||
options.OpenFilesCacheCapacity = utils.MakeDatabaseHandles()
|
||||
options.BlockCacheCapacity = cache / 2 * opt.MiB
|
||||
options.WriteBuffer = cache / 4 * opt.MiB // Two of these are used internally
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
showLeveldbStats(db)
|
||||
log.Info("Triggering compaction")
|
||||
err = db.Compact(nil, nil)
|
||||
if err != nil {
|
||||
log.Info("Compact err", "error", err)
|
||||
}
|
||||
showLeveldbStats(db)
|
||||
log.Info("Closing db")
|
||||
err = db.Close()
|
||||
if err != nil {
|
||||
log.Info("Close err", "error", err)
|
||||
}
|
||||
log.Info("Exiting")
|
||||
return err
|
||||
}
|
||||
|
||||
// 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()
|
||||
path := stack.ResolvePath("chaindata")
|
||||
db, err := leveldb.NewCustom(path, "", func(options *opt.Options) {
|
||||
options.ReadOnly = true
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
key, err := hexutil.Decode(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", "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)
|
||||
defer db.Close()
|
||||
key, err := hexutil.Decode(ctx.Args().Get(0))
|
||||
if err != nil {
|
||||
log.Info("Could not decode the key", "error", err)
|
||||
return err
|
||||
}
|
||||
if err = db.Delete(key); err != nil {
|
||||
log.Info("Delete operation returned an error", "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)
|
||||
defer db.Close()
|
||||
var (
|
||||
key []byte
|
||||
value []byte
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
key, err = hexutil.Decode(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)
|
||||
}
|
@ -238,7 +238,6 @@ func init() {
|
||||
removedbCommand,
|
||||
dumpCommand,
|
||||
dumpGenesisCommand,
|
||||
inspectCommand,
|
||||
// See accountcmd.go:
|
||||
accountCommand,
|
||||
walletCommand,
|
||||
@ -254,6 +253,8 @@ func init() {
|
||||
licenseCommand,
|
||||
// See config.go
|
||||
dumpConfigCommand,
|
||||
// see dbcmd.go
|
||||
dbCommand,
|
||||
// See cmd/utils/flags_legacy.go
|
||||
utils.ShowDeprecated,
|
||||
// See snapshot.go
|
||||
|
@ -1073,9 +1073,9 @@ func setLes(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||
}
|
||||
}
|
||||
|
||||
// makeDatabaseHandles raises out the number of allowed file handles per process
|
||||
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
||||
// for Geth and returns half of the allowance to assign to the database.
|
||||
func makeDatabaseHandles() int {
|
||||
func MakeDatabaseHandles() int {
|
||||
limit, err := fdlimit.Maximum()
|
||||
if err != nil {
|
||||
Fatalf("Failed to retrieve file descriptor allowance: %v", err)
|
||||
@ -1546,7 +1546,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
|
||||
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
|
||||
}
|
||||
cfg.DatabaseHandles = makeDatabaseHandles()
|
||||
cfg.DatabaseHandles = MakeDatabaseHandles()
|
||||
if ctx.GlobalIsSet(AncientFlag.Name) {
|
||||
cfg.DatabaseFreezer = ctx.GlobalString(AncientFlag.Name)
|
||||
}
|
||||
@ -1821,7 +1821,7 @@ func SplitTagsFlag(tagsFlag string) map[string]string {
|
||||
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||
var (
|
||||
cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
|
||||
handles = makeDatabaseHandles()
|
||||
handles = MakeDatabaseHandles()
|
||||
|
||||
err error
|
||||
chainDb ethdb.Database
|
||||
|
@ -270,8 +270,8 @@ func (s *stat) Count() string {
|
||||
|
||||
// InspectDatabase traverses the entire database and checks the size
|
||||
// of all different categories of data.
|
||||
func InspectDatabase(db ethdb.Database) error {
|
||||
it := db.NewIterator(nil, nil)
|
||||
func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
it := db.NewIterator(keyPrefix, keyStart)
|
||||
defer it.Release()
|
||||
|
||||
var (
|
||||
@ -309,6 +309,7 @@ func InspectDatabase(db ethdb.Database) error {
|
||||
// Meta- and unaccounted data
|
||||
metadata stat
|
||||
unaccounted stat
|
||||
shutdownInfo stat
|
||||
|
||||
// Totals
|
||||
total common.StorageSize
|
||||
@ -359,6 +360,8 @@ func InspectDatabase(db ethdb.Database) error {
|
||||
bytes.HasPrefix(key, []byte("bltIndex-")) ||
|
||||
bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub
|
||||
bloomTrieNodes.Add(size)
|
||||
case bytes.Equal(key, uncleanShutdownKey):
|
||||
shutdownInfo.Add(size)
|
||||
default:
|
||||
var accounted bool
|
||||
for _, meta := range [][]byte{
|
||||
@ -413,6 +416,7 @@ func InspectDatabase(db ethdb.Database) error {
|
||||
{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
|
||||
{"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
|
||||
{"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
|
||||
{"Key-Value store", "Shutdown metadata", shutdownInfo.Size(), shutdownInfo.Count()},
|
||||
{"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()},
|
||||
{"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()},
|
||||
{"Ancient store", "Receipt lists", ancientReceiptsSize.String(), ancients.String()},
|
||||
|
@ -84,6 +84,7 @@ type Database struct {
|
||||
// New returns a wrapped LevelDB object. The namespace is the prefix that the
|
||||
// metrics reporting should use for surfacing internal stats.
|
||||
func New(file string, cache int, handles int, namespace string) (*Database, error) {
|
||||
return NewCustom(file, namespace, func(options *opt.Options) {
|
||||
// Ensure we have some minimal caching and file guarantees
|
||||
if cache < minCache {
|
||||
cache = minCache
|
||||
@ -91,17 +92,28 @@ func New(file string, cache int, handles int, namespace string) (*Database, erro
|
||||
if handles < minHandles {
|
||||
handles = minHandles
|
||||
}
|
||||
// Set default options
|
||||
options.OpenFilesCacheCapacity = handles
|
||||
options.BlockCacheCapacity = cache / 2 * opt.MiB
|
||||
options.WriteBuffer = cache / 4 * opt.MiB // Two of these are used internally
|
||||
})
|
||||
}
|
||||
|
||||
// NewCustom returns a wrapped LevelDB object. The namespace is the prefix that the
|
||||
// metrics reporting should use for surfacing internal stats.
|
||||
// The customize function allows the caller to modify the leveldb options.
|
||||
func NewCustom(file string, namespace string, customize func(options *opt.Options)) (*Database, error) {
|
||||
options := configureOptions(customize)
|
||||
logger := log.New("database", file)
|
||||
logger.Info("Allocated cache and file handles", "cache", common.StorageSize(cache*1024*1024), "handles", handles)
|
||||
usedCache := options.GetBlockCacheCapacity() + options.GetWriteBuffer()*2
|
||||
logCtx := []interface{}{"cache", common.StorageSize(usedCache), "handles", options.GetOpenFilesCacheCapacity()}
|
||||
if options.ReadOnly {
|
||||
logCtx = append(logCtx, "readonly", "true")
|
||||
}
|
||||
logger.Info("Allocated cache and file handles", logCtx...)
|
||||
|
||||
// Open the db and recover any potential corruptions
|
||||
db, err := leveldb.OpenFile(file, &opt.Options{
|
||||
OpenFilesCacheCapacity: handles,
|
||||
BlockCacheCapacity: cache / 2 * opt.MiB,
|
||||
WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally
|
||||
Filter: filter.NewBloomFilter(10),
|
||||
DisableSeeksCompaction: true,
|
||||
})
|
||||
db, err := leveldb.OpenFile(file, options)
|
||||
if _, corrupted := err.(*errors.ErrCorrupted); corrupted {
|
||||
db, err = leveldb.RecoverFile(file, nil)
|
||||
}
|
||||
@ -133,6 +145,20 @@ func New(file string, cache int, handles int, namespace string) (*Database, erro
|
||||
return ldb, nil
|
||||
}
|
||||
|
||||
// configureOptions sets some default options, then runs the provided setter.
|
||||
func configureOptions(customizeFn func(*opt.Options)) *opt.Options {
|
||||
// Set default options
|
||||
options := &opt.Options{
|
||||
Filter: filter.NewBloomFilter(10),
|
||||
DisableSeeksCompaction: true,
|
||||
}
|
||||
// Allow caller to make custom modifications to the options
|
||||
if customizeFn != nil {
|
||||
customizeFn(options)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// Close stops the metrics collection, flushes any pending data to disk and closes
|
||||
// all io accesses to the underlying key-value store.
|
||||
func (db *Database) Close() error {
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
CommandHelpTemplate = `{{.cmd.Name}}{{if .cmd.Subcommands}} command{{end}}{{if .cmd.Flags}} [command options]{{end}} [arguments...]
|
||||
CommandHelpTemplate = `{{.cmd.Name}}{{if .cmd.Subcommands}} command{{end}}{{if .cmd.Flags}} [command options]{{end}} {{.cmd.ArgsUsage}}
|
||||
{{if .cmd.Description}}{{.cmd.Description}}
|
||||
{{end}}{{if .cmd.Subcommands}}
|
||||
SUBCOMMANDS:
|
||||
@ -36,7 +36,7 @@ SUBCOMMANDS:
|
||||
{{end}}
|
||||
{{end}}{{end}}`
|
||||
|
||||
OriginCommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
|
||||
OriginCommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} {{.ArgsUsage}}
|
||||
{{if .Description}}{{.Description}}
|
||||
{{end}}{{if .Subcommands}}
|
||||
SUBCOMMANDS:
|
||||
|
Loading…
Reference in New Issue
Block a user