Skip to content

Commit f6d5974

Browse files
rjl493456442blakehhuynh
authored andcommitted
all: clean up the configs for pruner and snapshotter (ethereum#22396)
This PR cleans up the configurations for pruner and snapshotter by passing a config struct. And also, this PR disables the snapshot background generation if the chain is opened in "read-only" mode. The read-only mode is necessary in some cases. For example, we have a list of commands to open the etheruem node in "read-only" mode, like export-chain. In these cases, the snapshot background generation is non expected and should be banned explicitly.
1 parent 255c4ae commit f6d5974

File tree

7 files changed

+109
-44
lines changed

7 files changed

+109
-44
lines changed

cmd/geth/snapshot.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,14 @@ func pruneState(ctx *cli.Context) error {
170170
defer stack.Close()
171171

172172
chaindb := utils.MakeChainDatabase(ctx, stack, false)
173-
pruner, err := pruner.NewPruner(chaindb, stack.ResolvePath(""), stack.ResolvePath(config.Eth.TrieCleanCacheJournal), ctx.Uint64(utils.BloomFilterSizeFlag.Name))
173+
defer chaindb.Close()
174+
175+
prunerconfig := pruner.Config{
176+
Datadir: stack.ResolvePath(""),
177+
Cachedir: stack.ResolvePath(config.Eth.TrieCleanCacheJournal),
178+
BloomSize: ctx.Uint64(utils.BloomFilterSizeFlag.Name),
179+
}
180+
pruner, err := pruner.NewPruner(chaindb, prunerconfig)
174181
if err != nil {
175182
log.Error("Failed to open snapshot tree", "err", err)
176183
return err
@@ -199,12 +206,20 @@ func verifyState(ctx *cli.Context) error {
199206
defer stack.Close()
200207

201208
chaindb := utils.MakeChainDatabase(ctx, stack, true)
209+
defer chaindb.Close()
210+
202211
headBlock := rawdb.ReadHeadBlock(chaindb)
203212
if headBlock == nil {
204213
log.Error("Failed to load head block")
205214
return errors.New("no head block")
206215
}
207-
snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, headBlock.Root(), false, false, false)
216+
snapconfig := snapshot.Config{
217+
CacheSize: 256,
218+
Recovery: false,
219+
NoBuild: true,
220+
AsyncBuild: false,
221+
}
222+
snaptree, err := snapshot.New(snapconfig, chaindb, trie.NewDatabase(chaindb), headBlock.Root())
208223
if err != nil {
209224
log.Error("Failed to open snapshot tree", "err", err)
210225
return err
@@ -479,7 +494,13 @@ func dumpState(ctx *cli.Context) error {
479494
if err != nil {
480495
return err
481496
}
482-
snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, root, false, false, false)
497+
snapConfig := snapshot.Config{
498+
CacheSize: 256,
499+
Recovery: false,
500+
NoBuild: true,
501+
AsyncBuild: false,
502+
}
503+
snaptree, err := snapshot.New(snapConfig, db, trie.NewDatabase(db), root)
483504
if err != nil {
484505
return err
485506
}

cmd/utils/flags.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2198,6 +2198,9 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (*core.BlockChain, ethdb.Data
21982198
if !ctx.Bool(SnapshotFlag.Name) {
21992199
cache.SnapshotLimit = 0 // Disabled
22002200
}
2201+
// Disable snapshot generation/wiping by default
2202+
cache.SnapshotNoBuild = true
2203+
22012204
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheTrieFlag.Name) {
22022205
cache.TrieCleanLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheTrieFlag.Name) / 100
22032206
}
@@ -2206,7 +2209,6 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (*core.BlockChain, ethdb.Data
22062209
}
22072210
vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)}
22082211

2209-
// TODO(rjl493456442) disable snapshot generation/wiping if the chain is read only.
22102212
// Disable transaction indexing/unindexing by default.
22112213
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil, nil)
22122214
if err != nil {

core/blockchain.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ type CacheConfig struct {
135135
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
136136
Preimages bool // Whether to store preimage of trie key to the disk
137137

138-
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
138+
SnapshotNoBuild bool // Whether the background generation is allowed
139+
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
139140
}
140141

141142
// defaultCacheConfig are the default caching values if none are specified by the
@@ -399,7 +400,13 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
399400
log.Warn("Enabling snapshot recovery", "chainhead", head.NumberU64(), "diskbase", *layer)
400401
recover = true
401402
}
402-
bc.snaps, _ = snapshot.New(bc.db, bc.stateCache.TrieDB(), bc.cacheConfig.SnapshotLimit, head.Root(), !bc.cacheConfig.SnapshotWait, true, recover)
403+
snapconfig := snapshot.Config{
404+
CacheSize: bc.cacheConfig.SnapshotLimit,
405+
Recovery: recover,
406+
NoBuild: bc.cacheConfig.SnapshotNoBuild,
407+
AsyncBuild: !bc.cacheConfig.SnapshotWait,
408+
}
409+
bc.snaps, _ = snapshot.New(snapconfig, bc.db, bc.stateCache.TrieDB(), head.Root())
403410
}
404411

405412
// Start future block processor.

core/state/pruner/pruner.go

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ var (
6363
emptyCode = crypto.Keccak256(nil)
6464
)
6565

66+
// Config includes all the configurations for pruning.
67+
type Config struct {
68+
Datadir string // The directory of the state database
69+
Cachedir string // The directory of state clean cache
70+
BloomSize uint64 // The Megabytes of memory allocated to bloom-filter
71+
}
72+
6673
// Pruner is an offline tool to prune the stale state with the
6774
// help of the snapshot. The workflow of pruner is very simple:
6875
//
@@ -75,40 +82,44 @@ var (
7582
// periodically in order to release the disk usage and improve the
7683
// disk read performance to some extent.
7784
type Pruner struct {
78-
db ethdb.Database
79-
stateBloom *stateBloom
80-
datadir string
81-
trieCachePath string
82-
headHeader *types.Header
83-
snaptree *snapshot.Tree
85+
config Config
86+
chainHeader *types.Header
87+
db ethdb.Database
88+
stateBloom *stateBloom
89+
snaptree *snapshot.Tree
8490
}
8591

8692
// NewPruner creates the pruner instance.
87-
func NewPruner(db ethdb.Database, datadir, trieCachePath string, bloomSize uint64) (*Pruner, error) {
93+
func NewPruner(db ethdb.Database, config Config) (*Pruner, error) {
8894
headBlock := rawdb.ReadHeadBlock(db)
8995
if headBlock == nil {
9096
return nil, errors.New("Failed to load head block")
9197
}
92-
snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, headBlock.Root(), false, false, false)
98+
snapconfig := snapshot.Config{
99+
CacheSize: 256,
100+
Recovery: false,
101+
NoBuild: true,
102+
AsyncBuild: false,
103+
}
104+
snaptree, err := snapshot.New(snapconfig, db, trie.NewDatabase(db), headBlock.Root())
93105
if err != nil {
94106
return nil, err // The relevant snapshot(s) might not exist
95107
}
96108
// Sanitize the bloom filter size if it's too small.
97-
if bloomSize < 256 {
98-
log.Warn("Sanitizing bloomfilter size", "provided(MB)", bloomSize, "updated(MB)", 256)
99-
bloomSize = 256
109+
if config.BloomSize < 256 {
110+
log.Warn("Sanitizing bloomfilter size", "provided(MB)", config.BloomSize, "updated(MB)", 256)
111+
config.BloomSize = 256
100112
}
101-
stateBloom, err := newStateBloomWithSize(bloomSize)
113+
stateBloom, err := newStateBloomWithSize(config.BloomSize)
102114
if err != nil {
103115
return nil, err
104116
}
105117
return &Pruner{
106-
db: db,
107-
stateBloom: stateBloom,
108-
datadir: datadir,
109-
trieCachePath: trieCachePath,
110-
headHeader: headBlock.Header(),
111-
snaptree: snaptree,
118+
config: config,
119+
chainHeader: headBlock.Header(),
120+
db: db,
121+
stateBloom: stateBloom,
122+
snaptree: snaptree,
112123
}, nil
113124
}
114125

@@ -236,12 +247,12 @@ func (p *Pruner) Prune(root common.Hash) error {
236247
// reuse it for pruning instead of generating a new one. It's
237248
// mandatory because a part of state may already be deleted,
238249
// the recovery procedure is necessary.
239-
_, stateBloomRoot, err := findBloomFilter(p.datadir)
250+
_, stateBloomRoot, err := findBloomFilter(p.config.Datadir)
240251
if err != nil {
241252
return err
242253
}
243254
if stateBloomRoot != (common.Hash{}) {
244-
return RecoverPruning(p.datadir, p.db, p.trieCachePath)
255+
return RecoverPruning(p.config.Datadir, p.db, p.config.Cachedir)
245256
}
246257
// If the target state root is not specified, use the HEAD-127 as the
247258
// target. The reason for picking it is:
@@ -252,7 +263,7 @@ func (p *Pruner) Prune(root common.Hash) error {
252263
// Retrieve all snapshot layers from the current HEAD.
253264
// In theory there are 128 difflayers + 1 disk layer present,
254265
// so 128 diff layers are expected to be returned.
255-
layers = p.snaptree.Snapshots(p.headHeader.Root, 128, true)
266+
layers = p.snaptree.Snapshots(p.chainHeader.Root, 128, true)
256267
if len(layers) != 128 {
257268
// Reject if the accumulated diff layers are less than 128. It
258269
// means in most of normal cases, there is no associated state
@@ -294,7 +305,7 @@ func (p *Pruner) Prune(root common.Hash) error {
294305
}
295306
} else {
296307
if len(layers) > 0 {
297-
log.Info("Selecting bottom-most difflayer as the pruning target", "root", root, "height", p.headHeader.Number.Uint64()-127)
308+
log.Info("Selecting bottom-most difflayer as the pruning target", "root", root, "height", p.chainHeader.Number.Uint64()-127)
298309
} else {
299310
log.Info("Selecting user-specified state as the pruning target", "root", root)
300311
}
@@ -303,7 +314,7 @@ func (p *Pruner) Prune(root common.Hash) error {
303314
// It's necessary otherwise in the next restart we will hit the
304315
// deleted state root in the "clean cache" so that the incomplete
305316
// state is picked for usage.
306-
deleteCleanTrieCache(p.trieCachePath)
317+
deleteCleanTrieCache(p.config.Cachedir)
307318

308319
// All the state roots of the middle layer should be forcibly pruned,
309320
// otherwise the dangling state will be left.
@@ -325,7 +336,7 @@ func (p *Pruner) Prune(root common.Hash) error {
325336
if err := extractGenesis(p.db, p.stateBloom); err != nil {
326337
return err
327338
}
328-
filterName := bloomFilterName(p.datadir, root)
339+
filterName := bloomFilterName(p.config.Datadir, root)
329340

330341
log.Info("Writing state bloom to disk", "name", filterName)
331342
if err := p.stateBloom.Commit(filterName, filterName+stateBloomFileTempSuffix); err != nil {
@@ -362,7 +373,13 @@ func RecoverPruning(datadir string, db ethdb.Database, trieCachePath string) err
362373
// - The state HEAD is rewound already because of multiple incomplete `prune-state`
363374
// In this case, even the state HEAD is not exactly matched with snapshot, it
364375
// still feasible to recover the pruning correctly.
365-
snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, headBlock.Root(), false, false, true)
376+
snapconfig := snapshot.Config{
377+
CacheSize: 256,
378+
Recovery: true,
379+
NoBuild: true,
380+
AsyncBuild: false,
381+
}
382+
snaptree, err := snapshot.New(snapconfig, db, trie.NewDatabase(db), headBlock.Root())
366383
if err != nil {
367384
return err // The relevant snapshot(s) might not exist
368385
}

core/state/snapshot/journal.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func loadAndParseJournal(db ethdb.KeyValueStore, base *diskLayer) (snapshot, jou
120120
}
121121

122122
// loadSnapshot loads a pre-existing state snapshot backed by a key-value store.
123-
func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root common.Hash, recovery bool) (snapshot, bool, error) {
123+
func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, root common.Hash, cache int, recovery bool, noBuild bool) (snapshot, bool, error) {
124124
// If snapshotting is disabled (initial sync in progress), don't do anything,
125125
// wait for the chain to permit us to do something meaningful
126126
if rawdb.ReadSnapshotDisabled(diskdb) {
@@ -140,7 +140,7 @@ func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int,
140140
}
141141
snapshot, generator, err := loadAndParseJournal(diskdb, base)
142142
if err != nil {
143-
log.Warn("Failed to load new-format journal", "error", err)
143+
log.Warn("Failed to load journal", "error", err)
144144
return nil, false, err
145145
}
146146
// Entire snapshot journal loaded, sanity check the head. If the loaded
@@ -164,13 +164,16 @@ func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int,
164164
// disk layer.
165165
log.Warn("Snapshot is not continuous with chain", "snaproot", head, "chainroot", root)
166166
}
167-
// Everything loaded correctly, resume any suspended operations
167+
// Load the disk layer status from the generator if it's not complete
168168
if !generator.Done {
169-
// Whether or not wiping was in progress, load any generator progress too
170169
base.genMarker = generator.Marker
171170
if base.genMarker == nil {
172171
base.genMarker = []byte{}
173172
}
173+
}
174+
// Everything loaded correctly, resume any suspended operations
175+
// if the background generation is allowed
176+
if !generator.Done && !noBuild {
174177
base.genPending = make(chan struct{})
175178
base.genAbort = make(chan chan *generatorStats)
176179

core/state/snapshot/snapshot.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,14 @@ type snapshot interface {
148148
StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool)
149149
}
150150

151+
// Config includes the configurations for snapshots.
152+
type Config struct {
153+
CacheSize int // Megabytes permitted to use for read caches
154+
Recovery bool // Indicator that the snapshots is in the recovery mode
155+
NoBuild bool // Indicator that the snapshots generation is disallowed
156+
AsyncBuild bool // The snapshot generation is allowed to be constructed asynchronously
157+
}
158+
151159
// Tree is an Ethereum state snapshot tree. It consists of one persistent base
152160
// layer backed by a key-value store, on top of which arbitrarily many in-memory
153161
// diff layers are topped. The memory diffs can form a tree with branching, but
@@ -158,9 +166,9 @@ type snapshot interface {
158166
// storage data to avoid expensive multi-level trie lookups; and to allow sorted,
159167
// cheap iteration of the account/storage tries for sync aid.
160168
type Tree struct {
169+
config Config // Snapshots configurations
161170
diskdb ethdb.KeyValueStore // Persistent database to store the snapshot
162171
triedb *trie.Database // In-memory cache to access the trie through
163-
cache int // Megabytes permitted to use for read caches
164172
layers map[common.Hash]snapshot // Collection of all known layers
165173
lock sync.RWMutex
166174

@@ -183,26 +191,27 @@ type Tree struct {
183191
// This case happens when the snapshot is 'ahead' of the state trie.
184192
// - otherwise, the entire snapshot is considered invalid and will be recreated on
185193
// a background thread.
186-
func New(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root common.Hash, async bool, rebuild bool, recovery bool) (*Tree, error) {
194+
func New(config Config, diskdb ethdb.KeyValueStore, triedb *trie.Database, root common.Hash) (*Tree, error) {
187195
// Create a new, empty snapshot tree
188196
snap := &Tree{
197+
config: config,
189198
diskdb: diskdb,
190199
triedb: triedb,
191-
cache: cache,
192200
layers: make(map[common.Hash]snapshot),
193201
}
194-
if !async {
202+
// Create the building waiter iff the background generation is allowed
203+
if !config.NoBuild && !config.AsyncBuild {
195204
defer snap.waitBuild()
196205
}
197206
// Attempt to load a previously persisted snapshot and rebuild one if failed
198-
head, disabled, err := loadSnapshot(diskdb, triedb, cache, root, recovery)
207+
head, disabled, err := loadSnapshot(diskdb, triedb, root, config.CacheSize, config.Recovery, config.NoBuild)
199208
if disabled {
200209
log.Warn("Snapshot maintenance disabled (syncing)")
201210
return snap, nil
202211
}
203212
if err != nil {
204-
if rebuild {
205-
log.Warn("Failed to load snapshot, regenerating", "err", err)
213+
log.Warn("Failed to load snapshot", "err", err)
214+
if !config.NoBuild {
206215
snap.Rebuild(root)
207216
return snap, nil
208217
}
@@ -727,7 +736,7 @@ func (t *Tree) Rebuild(root common.Hash) {
727736
// generator will run a wiper first if there's not one running right now.
728737
log.Info("Rebuilding state snapshot")
729738
t.layers = map[common.Hash]snapshot{
730-
root: generateSnapshot(t.diskdb, t.triedb, t.cache, root),
739+
root: generateSnapshot(t.diskdb, t.triedb, t.config.CacheSize, root),
731740
}
732741
}
733742

tests/state_test_util.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,13 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo
267267

268268
var snaps *snapshot.Tree
269269
if snapshotter {
270-
snaps, _ = snapshot.New(db, sdb.TrieDB(), 1, root, false, true, false)
270+
snapconfig := snapshot.Config{
271+
CacheSize: 1,
272+
Recovery: false,
273+
NoBuild: false,
274+
AsyncBuild: false,
275+
}
276+
snaps, _ = snapshot.New(snapconfig, db, sdb.TrieDB(), root)
271277
}
272278
statedb, _ = state.New(root, sdb, snaps)
273279
return snaps, statedb

0 commit comments

Comments
 (0)