Skip to content

Commit

Permalink
Change ancient chain segments from root ancient to sub folders (#572)
Browse files Browse the repository at this point in the history
* cmd, core, ethdb, node: rework ancient store folder reference by ethereum/go-ethereum@e44d655
  • Loading branch information
huyngopt1994 authored and Francesco4203 committed Sep 16, 2024
1 parent e223ca7 commit fcc9ccf
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 100 deletions.
51 changes: 24 additions & 27 deletions cmd/ronin/dbcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"os"
"os/signal"
"path/filepath"
"sort"
"strconv"
"strings"
"syscall"
Expand Down Expand Up @@ -197,8 +196,8 @@ WARNING: This is a low-level operation which may cause database corruption!`,
dbDumpFreezerIndex = &cli.Command{
Action: freezerInspect,
Name: "freezer-index",
Usage: "Dump out the index of a given freezer type",
ArgsUsage: "<type> <start (int)> <end (int)>",
Usage: "Dump out the index of a specific freezer table",
ArgsUsage: "<freezer-type> <table-type> <start (int)> <end (int)>",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.DBEngineFlag,
Expand Down Expand Up @@ -311,7 +310,7 @@ func inspect(ctx *cli.Context) error {
start []byte
)
if ctx.NArg() > 2 {
return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage)
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 {
Expand Down Expand Up @@ -519,42 +518,40 @@ func dbDumpTrie(ctx *cli.Context) error {

func freezerInspect(ctx *cli.Context) error {
var (
start, end int64
disableSnappy bool
err error
start, end int64
err error
)
if ctx.NArg() < 3 {
if ctx.NArg() < 4 {
return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
}
kind := ctx.Args().Get(0)
if noSnap, ok := rawdb.FreezerNoSnappy[kind]; !ok {
var options []string
for opt := range rawdb.FreezerNoSnappy {
options = append(options, opt)
}
sort.Strings(options)
return fmt.Errorf("Could read freezer-type '%v'. Available options: %v", kind, options)
} else {
disableSnappy = noSnap
}
if start, err = strconv.ParseInt(ctx.Args().Get(1), 10, 64); err != nil {

var (
freezerType = ctx.Args().Get(0)
tableType = ctx.Args().Get(1)
)

if start, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil {
log.Info("Could read start-param", "error", err)
return err
}
if end, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil {
log.Info("Could read count param", "error", err)
return err
}

stack, _ := makeConfigNode(ctx)
defer stack.Close()
path := filepath.Join(stack.ResolvePath("chaindata"), "ancient")
log.Info("Opening freezer", "location", path, "name", kind)
if f, err := rawdb.NewFreezerTable(path, kind, disableSnappy); err != nil {
return err
} else {
f.DumpIndex(start, end)
// Open the Freezer Database with mode read-only
db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close()

ancient, err := db.AncientDatadir()
if err != nil {
log.Info("Failed to retrive ancient root", "err", err)
}
return nil

return rawdb.InspectFreezerTable(ancient, freezerType, tableType, start, end)

}

// ParseHexOrString tries to hexdecode b, but if the prefix is missing, it instead just returns the raw bytes
Expand Down
2 changes: 1 addition & 1 deletion cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ var (
}
AncientFlag = &flags.DirectoryFlag{
Name: "datadir.ancient",
Usage: "Data directory for ancient chain segments (default = inside chaindata)",
Usage: "Root directory for ancient data (default = inside chaindata)",
Category: flags.EthCategory,
}
MinFreeDiskSpaceFlag = &flags.DirectoryFlag{
Expand Down
24 changes: 12 additions & 12 deletions core/rawdb/accessors_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import (
func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash {
var data []byte
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
data, _ = reader.Ancient(freezerHashTable, number)
data, _ = reader.Ancient(chainFreezerHashTable, number)
if len(data) == 0 {
// Get it by hash from leveldb
data, _ = db.Get(headerHashKey(number))
Expand Down Expand Up @@ -304,7 +304,7 @@ func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValu
// First try to look up the data in ancient database. Extra hash
// comparison is necessary since ancient database only maintains
// the canonical data.
data, _ = reader.Ancient(freezerHeaderTable, number)
data, _ = reader.Ancient(chainFreezerHeaderTable, number)
if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
return nil
}
Expand Down Expand Up @@ -380,7 +380,7 @@ func deleteHeaderWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
// isCanon is an internal utility method, to check whether the given number/hash
// is part of the ancient (canon) set.
func isCanon(reader ethdb.AncientReaderOp, number uint64, hash common.Hash) bool {
h, err := reader.Ancient(freezerHashTable, number)
h, err := reader.Ancient(chainFreezerHashTable, number)
if err != nil {
return false
}
Expand All @@ -396,7 +396,7 @@ func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
// Check if the data is in ancients
if isCanon(reader, number, hash) {
data, _ = reader.Ancient(freezerBodiesTable, number)
data, _ = reader.Ancient(chainFreezerBodiesTable, number)
return nil
}
// If not, try reading from leveldb
Expand All @@ -411,7 +411,7 @@ func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue
func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
var data []byte
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
data, _ = reader.Ancient(freezerBodiesTable, number)
data, _ = reader.Ancient(chainFreezerBodiesTable, number)
if len(data) > 0 {
return nil
}
Expand Down Expand Up @@ -501,7 +501,7 @@ func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
// Check if the data is in ancients
if isCanon(reader, number, hash) {
data, _ = reader.Ancient(freezerDifficultyTable, number)
data, _ = reader.Ancient(chainFreezerDifficultyTable, number)
return nil
}
// If not, try reading from leveldb
Expand Down Expand Up @@ -561,7 +561,7 @@ func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawVa
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
// Check if the data is in ancients
if isCanon(reader, number, hash) {
data, _ = reader.Ancient(freezerReceiptTable, number)
data, _ = reader.Ancient(chainFreezerReceiptTable, number)
return nil
}
// If not, try reading from leveldb
Expand Down Expand Up @@ -793,19 +793,19 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts

func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage, td *big.Int) error {
num := block.NumberU64()
if err := op.AppendRaw(freezerHashTable, num, block.Hash().Bytes()); err != nil {
if err := op.AppendRaw(chainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
return fmt.Errorf("can't add block %d hash: %v", num, err)
}
if err := op.Append(freezerHeaderTable, num, header); err != nil {
if err := op.Append(chainFreezerHeaderTable, num, header); err != nil {
return fmt.Errorf("can't append block header %d: %v", num, err)
}
if err := op.Append(freezerBodiesTable, num, block.Body()); err != nil {
if err := op.Append(chainFreezerBodiesTable, num, block.Body()); err != nil {
return fmt.Errorf("can't append block body %d: %v", num, err)
}
if err := op.Append(freezerReceiptTable, num, receipts); err != nil {
if err := op.Append(chainFreezerReceiptTable, num, receipts); err != nil {
return fmt.Errorf("can't append block %d receipts: %v", num, err)
}
if err := op.Append(freezerDifficultyTable, num, td); err != nil {
if err := op.Append(chainFreezerDifficultyTable, num, td); err != nil {
return fmt.Errorf("can't append block %d total difficulty: %v", num, err)
}
return nil
Expand Down
89 changes: 89 additions & 0 deletions core/rawdb/ancient_scheme.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package rawdb

import "fmt"

// The list of table names of chain freezer. (headers, hashes, bodies, difficulties)

const (
// chainFreezerHeaderTable indicates the name of the freezer header table.
chainFreezerHeaderTable = "headers"

// chainFreezerHashTable indicates the name of the freezer canonical hash table.
chainFreezerHashTable = "hashes"

// chainFreezerBodiesTable indicates the name of the freezer block body table.
chainFreezerBodiesTable = "bodies"

// chainFreezerReceiptTable indicates the name of the freezer receipts table.
chainFreezerReceiptTable = "receipts"

// chainFreezerDifficultyTable indicates the name of the freezer total difficulty table.
chainFreezerDifficultyTable = "diffs"
)

// chainFreezerNoSnappy configures whether compression is disabled for the ancient-tables.
// Hashes and difficulties don't compress well.
var chainFreezerNoSnappy = map[string]bool{
chainFreezerHeaderTable: false,
chainFreezerHashTable: true,
chainFreezerBodiesTable: false,
chainFreezerReceiptTable: false,
chainFreezerDifficultyTable: true,
}

// The list of identifiers of ancient stores. It can split more in the futures.
var (
chainFreezerName = "chain" // the folder name of chain segment ancient store.
)

// freezers the collections of all builtin freezers.
var freezers = []string{chainFreezerName}

// InspectFreezerTable dumps out the index of a specific freezer table. The passed
// ancient indicates the path of root ancient directory where the chain freezer can
// be opened. Start and end specifiy the range for dumping out indexes.
// Note this function can only used for debugging purpose.
func InspectFreezerTable(ancient string, freezerName string, tableName string, start, end int64) error {
var (
path string
tables map[string]bool
)

switch freezerName {
case chainFreezerName:
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
default:
return fmt.Errorf("unknown freezer, supported ones: %v", freezers)
}
noSnappy, exit := tables[tableName]
if !exit {
// If the tableName is not exit in the tables, return an error.
var names []string
for name := range tables {
names = append(names, name)
}
return fmt.Errorf("unknown table name, supported ones: %v", names)
}
table, err := newFreezerTable(path, tableName, noSnappy)
if err != nil {
return err
}
table.dumpIndexStdout(start, end)
return nil
}
10 changes: 5 additions & 5 deletions core/rawdb/chain_freezer.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,19 +270,19 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
}

// Write to the batch.
if err := op.AppendRaw(freezerHashTable, number, hash[:]); err != nil {
if err := op.AppendRaw(chainFreezerHashTable, number, hash[:]); err != nil {
return fmt.Errorf("can't write hash to freezer: %v", err)
}
if err := op.AppendRaw(freezerHeaderTable, number, header); err != nil {
if err := op.AppendRaw(chainFreezerHeaderTable, number, header); err != nil {
return fmt.Errorf("can't write header to freezer: %v", err)
}
if err := op.AppendRaw(freezerBodiesTable, number, body); err != nil {
if err := op.AppendRaw(chainFreezerBodiesTable, number, body); err != nil {
return fmt.Errorf("can't write body to freezer: %v", err)
}
if err := op.AppendRaw(freezerReceiptTable, number, receipts); err != nil {
if err := op.AppendRaw(chainFreezerReceiptTable, number, receipts); err != nil {
return fmt.Errorf("can't write receipts to freezer: %v", err)
}
if err := op.AppendRaw(freezerDifficultyTable, number, td); err != nil {
if err := op.AppendRaw(chainFreezerDifficultyTable, number, td); err != nil {
return fmt.Errorf("can't write td to freezer: %v", err)
}

Expand Down
2 changes: 1 addition & 1 deletion core/rawdb/chain_iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func InitDatabaseFromFreezer(db ethdb.Database) {
if i+count > frozen {
count = frozen - i
}
data, err := db.AncientRange(freezerHashTable, i, count, 32*count)
data, err := db.AncientRange(chainFreezerHashTable, i, count, 32*count)
if err != nil {
log.Crit("Failed to init database from freezer", "err", err)
}
Expand Down
35 changes: 30 additions & 5 deletions core/rawdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"time"

Expand Down Expand Up @@ -153,12 +154,36 @@ func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
return &nofreezedb{KeyValueStore: db}
}

// resolveChainFreezerDir is a helper function which resolves the absolute path
// of chain freezer by considering backward compatibility.
func resolveChainFreezerDir(ancient string) string {
// Check if the chain freezer is already present in the specified
// sub folder, if not then two possiblities
// - chain freezer is not initialized
// - it's legacy location, chain freezer is present in the 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)
}
}
return freezer
}

// NewDatabaseWithFreezer creates a high level database on top of a given key-
// value data store with a freezer moving immutable chain segments into cold
// storage.
func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace string, readonly bool) (ethdb.Database, error) {
func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly bool) (ethdb.Database, error) {
// Create the idle freezer instance
frdb, err := newChainFreezer(freezer, namespace, readonly, freezerTableSize, FreezerNoSnappy)
frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly, freezerTableSize, chainFreezerNoSnappy)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -189,7 +214,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace st
// If the freezer already contains something, ensure that the genesis blocks
// match, otherwise we might mix up freezers across chains and destroy both
// the freezer and the key-value store.
frgenesis, err := frdb.Ancient(freezerHashTable, 0)
frgenesis, err := frdb.Ancient(chainFreezerHashTable, 0)
if err != nil {
return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
} else if !bytes.Equal(kvgenesis, frgenesis) {
Expand Down Expand Up @@ -235,7 +260,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace st
}()
}
return &freezerdb{
ancientRoot: freezer, // o.AncientsDirectory
ancientRoot: ancient, // o.AncientsDirectory
KeyValueStore: db,
AncientStore: frdb,
}, nil
Expand Down Expand Up @@ -517,7 +542,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
}
// Inspect append-only file store then.
ancientSizes := []*common.StorageSize{&ancientHeadersSize, &ancientBodiesSize, &ancientReceiptsSize, &ancientHashesSize, &ancientTdsSize}
for i, category := range []string{freezerHeaderTable, freezerBodiesTable, freezerReceiptTable, freezerHashTable, freezerDifficultyTable} {
for i, category := range []string{chainFreezerHeaderTable, chainFreezerBodiesTable, chainFreezerReceiptTable, chainFreezerHashTable, chainFreezerDifficultyTable} {
if size, err := db.AncientSize(category); err == nil {
*ancientSizes[i] += common.StorageSize(size)
total += common.StorageSize(size)
Expand Down
Loading

0 comments on commit fcc9ccf

Please sign in to comment.