Skip to content

Commit f5d89cd

Browse files
authored
Merge pull request #19244 from karalabe/freezer-2
cmd, core, eth, les, node: chain freezer on top of db rework
2 parents 60386b3 + 9eba3a9 commit f5d89cd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+3748
-458
lines changed

cmd/geth/chaincmd.go

Lines changed: 86 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"encoding/json"
2121
"fmt"
2222
"os"
23+
"path/filepath"
2324
"runtime"
2425
"strconv"
2526
"sync/atomic"
@@ -167,6 +168,22 @@ Remove blockchain and state databases`,
167168
The arguments are interpreted as block numbers or hashes.
168169
Use "ethereum dump 0" to dump the genesis block.`,
169170
}
171+
inspectCommand = cli.Command{
172+
Action: utils.MigrateFlags(inspect),
173+
Name: "inspect",
174+
Usage: "Inspect the storage size for each type of data in the database",
175+
ArgsUsage: " ",
176+
Flags: []cli.Flag{
177+
utils.DataDirFlag,
178+
utils.AncientFlag,
179+
utils.CacheFlag,
180+
utils.TestnetFlag,
181+
utils.RinkebyFlag,
182+
utils.GoerliFlag,
183+
utils.SyncModeFlag,
184+
},
185+
Category: "BLOCKCHAIN COMMANDS",
186+
}
170187
)
171188

172189
// initGenesis will initialise the given JSON format genesis file and writes it as
@@ -368,9 +385,12 @@ func exportPreimages(ctx *cli.Context) error {
368385

369386
func copyDb(ctx *cli.Context) error {
370387
// Ensure we have a source chain directory to copy
371-
if len(ctx.Args()) != 1 {
388+
if len(ctx.Args()) < 1 {
372389
utils.Fatalf("Source chaindata directory path argument missing")
373390
}
391+
if len(ctx.Args()) < 2 {
392+
utils.Fatalf("Source ancient chain directory path argument missing")
393+
}
374394
// Initialize a new chain for the running node to sync into
375395
stack := makeFullNode(ctx)
376396
defer stack.Close()
@@ -385,7 +405,7 @@ func copyDb(ctx *cli.Context) error {
385405
dl := downloader.New(0, chainDb, syncBloom, new(event.TypeMux), chain, nil, nil)
386406

387407
// Create a source peer to satisfy downloader requests from
388-
db, err := rawdb.NewLevelDBDatabase(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name)/2, 256, "")
408+
db, err := rawdb.NewLevelDBDatabaseWithFreezer(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name)/2, 256, ctx.Args().Get(1), "")
389409
if err != nil {
390410
return err
391411
}
@@ -420,34 +440,65 @@ func copyDb(ctx *cli.Context) error {
420440
}
421441

422442
func removeDB(ctx *cli.Context) error {
423-
stack, _ := makeConfigNode(ctx)
443+
stack, config := makeConfigNode(ctx)
424444

425-
for _, name := range []string{"chaindata", "lightchaindata"} {
426-
// Ensure the database exists in the first place
427-
logger := log.New("database", name)
428-
429-
dbdir := stack.ResolvePath(name)
430-
if !common.FileExist(dbdir) {
431-
logger.Info("Database doesn't exist, skipping", "path", dbdir)
432-
continue
433-
}
434-
// Confirm removal and execute
435-
fmt.Println(dbdir)
436-
confirm, err := console.Stdin.PromptConfirm("Remove this database?")
437-
switch {
438-
case err != nil:
439-
utils.Fatalf("%v", err)
440-
case !confirm:
441-
logger.Warn("Database deletion aborted")
442-
default:
443-
start := time.Now()
444-
os.RemoveAll(dbdir)
445-
logger.Info("Database successfully deleted", "elapsed", common.PrettyDuration(time.Since(start)))
446-
}
445+
// Remove the full node state database
446+
path := stack.ResolvePath("chaindata")
447+
if common.FileExist(path) {
448+
confirmAndRemoveDB(path, "full node state database")
449+
} else {
450+
log.Info("Full node state database missing", "path", path)
451+
}
452+
// Remove the full node ancient database
453+
path = config.Eth.DatabaseFreezer
454+
switch {
455+
case path == "":
456+
path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
457+
case !filepath.IsAbs(path):
458+
path = config.Node.ResolvePath(path)
459+
}
460+
if common.FileExist(path) {
461+
confirmAndRemoveDB(path, "full node ancient database")
462+
} else {
463+
log.Info("Full node ancient database missing", "path", path)
464+
}
465+
// Remove the light node database
466+
path = stack.ResolvePath("lightchaindata")
467+
if common.FileExist(path) {
468+
confirmAndRemoveDB(path, "light node database")
469+
} else {
470+
log.Info("Light node database missing", "path", path)
447471
}
448472
return nil
449473
}
450474

475+
// confirmAndRemoveDB prompts the user for a last confirmation and removes the
476+
// folder if accepted.
477+
func confirmAndRemoveDB(database string, kind string) {
478+
confirm, err := console.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
479+
switch {
480+
case err != nil:
481+
utils.Fatalf("%v", err)
482+
case !confirm:
483+
log.Info("Database deletion skipped", "path", database)
484+
default:
485+
start := time.Now()
486+
filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
487+
// If we're at the top level folder, recurse into
488+
if path == database {
489+
return nil
490+
}
491+
// Delete all the files, but not subfolders
492+
if !info.IsDir() {
493+
os.Remove(path)
494+
return nil
495+
}
496+
return filepath.SkipDir
497+
})
498+
log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
499+
}
500+
}
501+
451502
func dump(ctx *cli.Context) error {
452503
stack := makeFullNode(ctx)
453504
defer stack.Close()
@@ -476,6 +527,16 @@ func dump(ctx *cli.Context) error {
476527
return nil
477528
}
478529

530+
func inspect(ctx *cli.Context) error {
531+
node, _ := makeConfigNode(ctx)
532+
defer node.Close()
533+
534+
_, chainDb := utils.MakeChain(ctx, node)
535+
defer chainDb.Close()
536+
537+
return rawdb.InspectDatabase(chainDb)
538+
}
539+
479540
// hashish returns true for strings that look like hashes.
480541
func hashish(x string) bool {
481542
_, err := strconv.Atoi(x)

cmd/geth/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ var (
6262
utils.BootnodesV4Flag,
6363
utils.BootnodesV5Flag,
6464
utils.DataDirFlag,
65+
utils.AncientFlag,
6566
utils.KeyStoreDirFlag,
6667
utils.ExternalSignerFlag,
6768
utils.NoUSBFlag,
@@ -203,6 +204,7 @@ func init() {
203204
copydbCommand,
204205
removedbCommand,
205206
dumpCommand,
207+
inspectCommand,
206208
// See accountcmd.go:
207209
accountCommand,
208210
walletCommand,

cmd/geth/usage.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ var AppHelpFlagGroups = []flagGroup{
6969
Flags: []cli.Flag{
7070
configFileFlag,
7171
utils.DataDirFlag,
72+
utils.AncientFlag,
7273
utils.KeyStoreDirFlag,
7374
utils.NoUSBFlag,
7475
utils.NetworkIdFlag,

cmd/utils/cmd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@ func ExportPreimages(db ethdb.Database, fn string) error {
302302
}
303303
// Iterate over the preimages and export them
304304
it := db.NewIteratorWithPrefix([]byte("secure-key-"))
305+
defer it.Release()
306+
305307
for it.Next() {
306308
if err := rlp.Encode(writer, it.Value()); err != nil {
307309
return err

cmd/utils/flags.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ var (
117117
Usage: "Data directory for the databases and keystore",
118118
Value: DirectoryString{node.DefaultDataDir()},
119119
}
120+
AncientFlag = DirectoryFlag{
121+
Name: "datadir.ancient",
122+
Usage: "Data directory for ancient chain segments (default = inside chaindata)",
123+
}
120124
KeyStoreDirFlag = DirectoryFlag{
121125
Name: "keystore",
122126
Usage: "Directory for the keystore (default = inside the datadir)",
@@ -1378,6 +1382,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
13781382
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
13791383
}
13801384
cfg.DatabaseHandles = makeDatabaseHandles()
1385+
if ctx.GlobalIsSet(AncientFlag.Name) {
1386+
cfg.DatabaseFreezer = ctx.GlobalString(AncientFlag.Name)
1387+
}
13811388

13821389
if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
13831390
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
@@ -1566,7 +1573,7 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
15661573
if ctx.GlobalString(SyncModeFlag.Name) == "light" {
15671574
name = "lightchaindata"
15681575
}
1569-
chainDb, err := stack.OpenDatabase(name, cache, handles, "")
1576+
chainDb, err := stack.OpenDatabaseWithFreezer(name, cache, handles, ctx.GlobalString(AncientFlag.Name), "")
15701577
if err != nil {
15711578
Fatalf("Could not open database: %v", err)
15721579
}

common/size.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ type StorageSize float64
2626

2727
// String implements the stringer interface.
2828
func (s StorageSize) String() string {
29-
if s > 1048576 {
29+
if s > 1099511627776 {
30+
return fmt.Sprintf("%.2f TiB", s/1099511627776)
31+
} else if s > 1073741824 {
32+
return fmt.Sprintf("%.2f GiB", s/1073741824)
33+
} else if s > 1048576 {
3034
return fmt.Sprintf("%.2f MiB", s/1048576)
3135
} else if s > 1024 {
3236
return fmt.Sprintf("%.2f KiB", s/1024)
@@ -38,7 +42,11 @@ func (s StorageSize) String() string {
3842
// TerminalString implements log.TerminalStringer, formatting a string for console
3943
// output during logging.
4044
func (s StorageSize) TerminalString() string {
41-
if s > 1048576 {
45+
if s > 1099511627776 {
46+
return fmt.Sprintf("%.2fTiB", s/1099511627776)
47+
} else if s > 1073741824 {
48+
return fmt.Sprintf("%.2fGiB", s/1073741824)
49+
} else if s > 1048576 {
4250
return fmt.Sprintf("%.2fMiB", s/1048576)
4351
} else if s > 1024 {
4452
return fmt.Sprintf("%.2fKiB", s/1024)

consensus/clique/clique.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,11 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo
365365
break
366366
}
367367
}
368-
// If we're at an checkpoint block, make a snapshot if it's known
369-
if number == 0 || (number%c.config.Epoch == 0 && chain.GetHeaderByNumber(number-1) == nil) {
368+
// If we're at the genesis, snapshot the initial state. Alternatively if we're
369+
// at a checkpoint block without a parent (light client CHT), or we have piled
370+
// up more headers than allowed to be reorged (chain reinit from a freezer),
371+
// consider the checkpoint trusted and snapshot it.
372+
if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > params.ImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) {
370373
checkpoint := chain.GetHeaderByNumber(number)
371374
if checkpoint != nil {
372375
hash := checkpoint.Hash()

consensus/clique/snapshot.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ import (
2020
"bytes"
2121
"encoding/json"
2222
"sort"
23+
"time"
2324

2425
"github.com/ethereum/go-ethereum/common"
2526
"github.com/ethereum/go-ethereum/core/types"
2627
"github.com/ethereum/go-ethereum/ethdb"
28+
"github.com/ethereum/go-ethereum/log"
2729
"github.com/ethereum/go-ethereum/params"
2830
lru "github.com/hashicorp/golang-lru"
2931
)
@@ -197,7 +199,11 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
197199
// Iterate through the headers and create a new snapshot
198200
snap := s.copy()
199201

200-
for _, header := range headers {
202+
var (
203+
start = time.Now()
204+
logged = time.Now()
205+
)
206+
for i, header := range headers {
201207
// Remove any votes on checkpoint blocks
202208
number := header.Number.Uint64()
203209
if number%s.config.Epoch == 0 {
@@ -285,6 +291,14 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
285291
}
286292
delete(snap.Tally, header.Coinbase)
287293
}
294+
// If we're taking too much time (ecrecover), notify the user once a while
295+
if time.Since(logged) > 8*time.Second {
296+
log.Info("Reconstructing voting history", "processed", i, "total", len(headers), "elapsed", common.PrettyDuration(time.Since(start)))
297+
logged = time.Now()
298+
}
299+
}
300+
if time.Since(start) > 8*time.Second {
301+
log.Info("Reconstructed voting history", "processed", len(headers), "elapsed", common.PrettyDuration(time.Since(start)))
288302
}
289303
snap.Number += uint64(len(headers))
290304
snap.Hash = headers[len(headers)-1].Hash()

consensus/ethash/sealer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ func (ethash *Ethash) remote(notify []string, noverify bool) {
270270
start := time.Now()
271271
if !noverify {
272272
if err := ethash.verifySeal(nil, header, true); err != nil {
273-
log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", time.Since(start), "err", err)
273+
log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)), "err", err)
274274
return false
275275
}
276276
}
@@ -279,7 +279,7 @@ func (ethash *Ethash) remote(notify []string, noverify bool) {
279279
log.Warn("Ethash result channel is empty, submitted mining result is rejected")
280280
return false
281281
}
282-
log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", time.Since(start))
282+
log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)))
283283

284284
// Solutions seems to be valid, return to the miner and notify acceptance.
285285
solution := block.WithSeal(header)

console/prompter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ func (p *terminalPrompter) PromptPassword(prompt string) (passwd string, err err
142142
// PromptConfirm displays the given prompt to the user and requests a boolean
143143
// choice to be made, returning that choice.
144144
func (p *terminalPrompter) PromptConfirm(prompt string) (bool, error) {
145-
input, err := p.Prompt(prompt + " [y/N] ")
145+
input, err := p.Prompt(prompt + " [y/n] ")
146146
if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" {
147147
return true, nil
148148
}

0 commit comments

Comments
 (0)