Skip to content

Commit

Permalink
fix: optimize resolveChainFreezerDir func
Browse files Browse the repository at this point in the history
  • Loading branch information
VM committed Jan 12, 2024
1 parent ccb4d55 commit fee8a25
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 37 deletions.
4 changes: 2 additions & 2 deletions cmd/geth/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,12 @@ func TestCustomBackend(t *testing.T) {
{ // Can't start pebble on top of leveldb
initArgs: []string{"--db.engine", "leveldb"},
execArgs: []string{"--db.engine", "pebble"},
execExpect: `Fatal: Could not open database: db.engine choice was pebble but found pre-existing leveldb database in specified data directory`,
execExpect: `Fatal: Failed to register the Ethereum service: db.engine choice was pebble but found pre-existing leveldb database in specified data directory`,
},
{ // Can't start leveldb on top of pebble
initArgs: []string{"--db.engine", "pebble"},
execArgs: []string{"--db.engine", "leveldb"},
execExpect: `Fatal: Could not open database: db.engine choice was leveldb but found pre-existing pebble database in specified data directory`,
execExpect: `Fatal: Failed to register the Ethereum service: db.engine choice was leveldb but found pre-existing pebble database in specified data directory`,
},
{ // Reject invalid backend choice
initArgs: []string{"--db.engine", "mssql"},
Expand Down
24 changes: 18 additions & 6 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -1848,7 +1848,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if cfg.SyncMode == downloader.FullSync {
cfg.PruneAncientData = ctx.Bool(PruneAncientDataFlag.Name)
} else {
log.Crit("pruneancient parameter didn't take effect for current syncmode")
log.Crit("pruneancient parameter can only be used with syncmode=full")
}
}
if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
Expand Down Expand Up @@ -1884,9 +1884,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.IsSet(StateHistoryFlag.Name) {
cfg.StateHistory = ctx.Uint64(StateHistoryFlag.Name)
}
if ctx.IsSet(StateSchemeFlag.Name) {
cfg.StateScheme = ctx.String(StateSchemeFlag.Name)
scheme, err := compareCLIWithConfig(ctx)
if err != nil {
Fatalf("%v", err)
}
cfg.StateScheme = scheme
// Parse transaction history flag, if user is still using legacy config
// file with 'TxLookupLimit' configured, copy the value to 'TransactionHistory'.
if cfg.TransactionHistory == ethconfig.Defaults.TransactionHistory && cfg.TxLookupLimit != ethconfig.Defaults.TxLookupLimit {
Expand Down Expand Up @@ -2040,7 +2042,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
readonly = false
}
// Check if we have an already initialized chain and fall back to
// that if so. Otherwise we need to generate a new genesis spec.
// that if so. Otherwise, we need to generate a new genesis spec.
chaindb := MakeChainDatabase(ctx, stack, readonly, false)
if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) {
cfg.Genesis = nil // fallback to db content
Expand Down Expand Up @@ -2275,6 +2277,8 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly, disableFree

// tryMakeReadOnlyDatabase try to open the chain database in read-only mode,
// or fallback to write mode if the database is not initialized.
//
//nolint:unused
func tryMakeReadOnlyDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
// If datadir doesn't exist we need to open db in write-mode
// so database engine can create files.
Expand Down Expand Up @@ -2349,7 +2353,11 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
}
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), chainDb)
provided, err := compareCLIWithConfig(ctx)
if err != nil {
Fatalf("%v", err)
}
scheme, err := rawdb.ParseStateScheme(provided, chainDb)
if err != nil {
Fatalf("%v", err)
}
Expand Down Expand Up @@ -2417,7 +2425,11 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read
config := &trie.Config{
Preimages: preimage,
}
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
provided, err := compareCLIWithConfig(ctx)
if err != nil {
Fatalf("%v", err)
}
scheme, err := rawdb.ParseStateScheme(provided, disk)
if err != nil {
Fatalf("%v", err)
}
Expand Down
36 changes: 21 additions & 15 deletions core/rawdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,20 +224,19 @@ func resolveChainFreezerDir(ancient string) string {
// sub folder, if not then two possibilities:
// - chain freezer is not initialized
// - chain freezer exists in legacy location (root ancient folder)
freezer := path.Join(ancient, chainFreezerName)
if !common.FileExist(freezer) {
if !common.FileExist(ancient) {
// The entire ancient store is not initialized, still use the sub
// folder for initialization.
} else {
// Ancient root is already initialized, then we hold the assumption
// that chain freezer is also initialized and located in root folder.
// In this case fallback to legacy location.
freezer = ancient
log.Info("Found legacy ancient chain path", "location", ancient)
}
chain := path.Join(ancient, chainFreezerName)
state := path.Join(ancient, stateFreezerName)
if common.FileExist(chain) {
return chain
}
if common.FileExist(state) {
return chain
}
if common.FileExist(ancient) {
log.Info("Found legacy ancient chain path", "location", ancient)
chain = ancient
}
return freezer
return chain
}

// NewDatabaseWithFreezer creates a high level database on top of a given key-
Expand All @@ -264,6 +263,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
WriteAncientType(db, PruneFreezerType)
}
return &freezerdb{
ancientRoot: ancient,
KeyValueStore: db,
AncientStore: frdb,
}, nil
Expand Down Expand Up @@ -336,7 +336,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
break
}
}
// We are about to exit on error. Print database metdata beore exiting
// We are about to exit on error. Print database metdata before exiting
printChainMetadata(db)
return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ",
frozen-1, number, head)
Expand Down Expand Up @@ -365,7 +365,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
// freezer.
}
}
// no prune ancinet start success
// no prune ancient start success
if !readonly {
WriteAncientType(db, EntireFreezerType)
}
Expand Down Expand Up @@ -516,6 +516,11 @@ func Open(o OpenOptions) (ethdb.Database, error) {
if err != nil {
return nil, err
}
if ReadAncientType(kvdb) == PruneFreezerType {
if !o.PruneAncientData {
log.Warn("Disk db is pruned")
}
}
if len(o.AncientsDirectory) == 0 {
return kvdb, nil
}
Expand Down Expand Up @@ -556,6 +561,7 @@ func (s *stat) Size() string {
func (s *stat) Count() string {
return s.count.String()
}

func AncientInspect(db ethdb.Database) error {
offset := counter(ReadOffSetOfCurrentAncientFreezer(db))
// Get number of ancient rows inside the freezer.
Expand Down
111 changes: 111 additions & 0 deletions core/rawdb/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,114 @@
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package rawdb

import (
"fmt"
"os"
"testing"
)

const (
mockChainFreezerPath = "/geth/chaindata/ancient/chain"
mockStateFreezerPath = "/geth/chaindata/ancient/state"
mockAncientFreezerPath = "/geth/chaindata/ancient"
)

func Test_resolveChainFreezerDir(t *testing.T) {
tests := []struct {
name string
fn func(dir string) string
ancient string
wantedResult string
}{
{
name: "run geth in pruned mode and chain dir is existent",
fn: func(dir string) string {
path := fmt.Sprintf("%s%s", dir, mockChainFreezerPath)
if err := os.MkdirAll(path, 0700); err != nil {
t.Fatalf("Failed to mkdir all dirs, error: %v", err)
}
return fmt.Sprintf("%s%s", dir, mockAncientFreezerPath)
},
wantedResult: mockChainFreezerPath,
},
{
name: "run geth in path and pruned mode; chain is nonexistent and state is existent",
fn: func(dir string) string {
path := fmt.Sprintf("%s%s", dir, mockStateFreezerPath)
if err := os.MkdirAll(path, 0700); err != nil {
t.Fatalf("Failed to mkdir all dirs, error: %v", err)
}
return fmt.Sprintf("%s%s", dir, mockAncientFreezerPath)
},
wantedResult: mockChainFreezerPath,
},
{
name: "run geth in hash and pruned mode; ancient block data locates in ancient dir",
fn: func(dir string) string {
path := fmt.Sprintf("%s%s", dir, mockAncientFreezerPath)
if err := os.MkdirAll(path, 0700); err != nil {
t.Fatalf("Failed to mkdir all dirs, error: %v", err)
}
return path
},
wantedResult: mockAncientFreezerPath,
},
{
name: "run geth in pruned mode and there is no ancient dir",
fn: func(dir string) string {
return fmt.Sprintf("%s%s", dir, mockAncientFreezerPath)
},
wantedResult: mockChainFreezerPath,
},
{
name: "run geth in non-pruned mode; ancient is existent and state dir is nonexistent",
fn: func(dir string) string {
path := fmt.Sprintf("%s%s", dir, mockAncientFreezerPath)
if err := os.MkdirAll(path, 0700); err != nil {
t.Fatalf("Failed to mkdir all dirs, error: %v", err)
}
return fmt.Sprintf("%s%s", dir, mockAncientFreezerPath)
},
wantedResult: mockAncientFreezerPath,
},
// {
// name: "run geth in non-pruned mode and chain dir is existent",
// fn: func(dir string) string {
// path := fmt.Sprintf("%s%s", dir, mockChainFreezerPath)
// if err := os.MkdirAll(path, 0700); err != nil {
// t.Fatalf("Failed to mkdir all dirs, error: %v", err)
// }
// return fmt.Sprintf("%s%s", dir, mockAncientFreezerPath)
// },
// wantedResult: mockChainFreezerPath,
// },
// {
// name: "run geth in non-pruned mode, ancient and chain dir is nonexistent",
// fn: func(dir string) string {
// return fmt.Sprintf("%s%s", dir, mockAncientFreezerPath)
// },
// wantedResult: mockChainFreezerPath,
// },
// {
// name: "run geth in non-pruned mode; ancient dir is existent, chain and state dir is nonexistent",
// fn: func(dir string) string {
// path := fmt.Sprintf("%s%s", dir, mockStateFreezerPath)
// if err := os.MkdirAll(path, 0700); err != nil {
// t.Fatalf("Failed to mkdir all dirs, error: %v", err)
// }
// return fmt.Sprintf("%s%s", dir, mockAncientFreezerPath)
// },
// wantedResult: mockChainFreezerPath,
// },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
got := resolveChainFreezerDir(tt.fn(tempDir))
if got != fmt.Sprintf("%s%s", tempDir, tt.wantedResult) {
t.Fatalf("resolveChainFreezerDir() = %s, wanted = %s", got, tt.wantedResult)
}
})
}
}
2 changes: 1 addition & 1 deletion core/rawdb/freezer.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func NewFreezer(datadir string, namespace string, readonly bool, offset uint64,
}

// Some blocks in ancientDB may have already been frozen and been pruned, so adding the offset to
// reprensent the absolute number of blocks already frozen.
// represent the absolute number of blocks already frozen.
freezer.frozen.Add(offset)
freezer.tail.Add(offset)

Expand Down
2 changes: 1 addition & 1 deletion core/rawdb/prunedfreezer.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func newPrunedFreezer(datadir string, db ethdb.KeyValueStore, offset uint64) (*p

// delete ancient dir
if err := os.RemoveAll(datadir); err != nil && !os.IsNotExist(err) {
log.Warn("remove the ancient dir failed.", "path", datadir, "error", err)
log.Warn("Failed to remove the ancient dir", "path", datadir, "error", err)
return nil, err
}
log.Info("Opened ancientdb with nodata mode", "database", datadir, "frozen", freezer.frozen)
Expand Down
25 changes: 13 additions & 12 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice)
}

// Assemble the Ethereum object
chainDb, err := stack.OpenAndMergeDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles,
config.DatabaseFreezer, config.DatabaseDiff, "eth/db/chaindata/", false, config.PersistDiff, config.PruneAncientData)
if err != nil {
return nil, err
}
config.StateScheme, err = rawdb.ParseStateScheme(config.StateScheme, chainDb)
if err != nil {
return nil, err
}
// Redistribute memory allocation from in-memory trie node garbage collection
// to other caches when an archive node is requested.
if config.StateScheme == rawdb.HashScheme && config.NoPruning && config.TrieDirtyCache > 0 {
Expand All @@ -150,18 +161,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
}
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)

// Assemble the Ethereum object
chainDb, err := stack.OpenAndMergeDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles,
config.DatabaseFreezer, config.DatabaseDiff, "eth/db/chaindata/", false, config.PersistDiff, config.PruneAncientData)
if err != nil {
return nil, err
}
scheme, err := rawdb.ParseStateScheme(config.StateScheme, chainDb)
if err != nil {
return nil, err
}
// Try to recover offline state pruning only in hash-based.
if scheme == rawdb.HashScheme {
if config.StateScheme == rawdb.HashScheme {
if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, config.TriesInMemory); err != nil {
log.Error("Failed to recover state", "error", err)
}
Expand Down Expand Up @@ -247,8 +248,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
TriesInMemory: config.TriesInMemory,
Preimages: config.Preimages,
StateHistory: config.StateHistory,
StateScheme: config.StateScheme,
PathSyncFlush: config.PathSyncFlush,
StateScheme: scheme,
}
)
bcOps := make([]core.BlockChainOption, 0)
Expand Down

0 comments on commit fee8a25

Please sign in to comment.