Skip to content

Commit ea9c962

Browse files
authored
Merge pull request ethereum#1962 from bnb-chain/cherry_pick_pbss_patches
cherry pick pbss patches from go-ethereum
2 parents 56424d3 + f5a854c commit ea9c962

File tree

7 files changed

+117
-56
lines changed

7 files changed

+117
-56
lines changed

eth/backend.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import (
6363
"github.com/ethereum/go-ethereum/params"
6464
"github.com/ethereum/go-ethereum/rlp"
6565
"github.com/ethereum/go-ethereum/rpc"
66+
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
6667
)
6768

6869
// Config contains the configuration options of the ETH protocol.
@@ -129,7 +130,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
129130
log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
130131
config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice)
131132
}
132-
if config.NoPruning && config.TrieDirtyCache > 0 {
133+
// Redistribute memory allocation from in-memory trie node garbage collection
134+
// to other caches when an archive node is requested.
135+
if config.StateScheme == rawdb.HashScheme && config.NoPruning && config.TrieDirtyCache > 0 {
133136
if config.SnapshotCache > 0 {
134137
config.TrieCleanCache += config.TrieDirtyCache * 3 / 5
135138
config.SnapshotCache += config.TrieDirtyCache * 2 / 5
@@ -138,6 +141,13 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
138141
}
139142
config.TrieDirtyCache = 0
140143
}
144+
// Optimize memory distribution by reallocating surplus allowance from the
145+
// dirty cache to the clean cache.
146+
if config.StateScheme == rawdb.PathScheme && config.TrieDirtyCache > pathdb.MaxDirtyBufferSize/1024/1024 {
147+
log.Info("Capped dirty cache size", "provided", common.StorageSize(config.TrieDirtyCache)*1024*1024, "adjusted", common.StorageSize(pathdb.MaxDirtyBufferSize))
148+
config.TrieCleanCache += config.TrieDirtyCache - pathdb.MaxDirtyBufferSize/1024/1024
149+
config.TrieDirtyCache = pathdb.MaxDirtyBufferSize / 1024 / 1024
150+
}
141151
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
142152

143153
// Assemble the Ethereum object

eth/handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,6 @@ func (h *handler) voteBroadcastLoop() {
980980
func (h *handler) enableSyncedFeatures() {
981981
h.acceptTxs.Store(true)
982982
if h.chain.TrieDB().Scheme() == rawdb.PathScheme {
983-
h.chain.TrieDB().SetBufferSize(pathdb.DefaultBufferSize)
983+
h.chain.TrieDB().SetBufferSize(pathdb.DefaultDirtyBufferSize)
984984
}
985985
}

trie/triedb/pathdb/database.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,18 @@ const (
4040
// defaultCleanSize is the default memory allowance of clean cache.
4141
defaultCleanSize = 16 * 1024 * 1024
4242

43-
// maxBufferSize is the maximum memory allowance of node buffer.
43+
// MaxDirtyBufferSize is the maximum memory allowance of node buffer.
4444
// Too large nodebuffer will cause the system to pause for a long
4545
// time when write happens. Also, the largest batch that pebble can
4646
// support is 4GB, node will panic if batch size exceeds this limit.
47-
maxBufferSize = 256 * 1024 * 1024
47+
MaxDirtyBufferSize = 256 * 1024 * 1024
4848

49-
// DefaultBufferSize is the default memory allowance of node buffer
49+
// DefaultDirtyBufferSize is the default memory allowance of node buffer
5050
// that aggregates the writes from above until it's flushed into the
5151
// disk. It's meant to be used once the initial sync is finished.
5252
// Do not increase the buffer size arbitrarily, otherwise the system
5353
// pause time will increase when the database writes happen.
54-
DefaultBufferSize = 64 * 1024 * 1024
54+
DefaultDirtyBufferSize = 64 * 1024 * 1024
5555
)
5656

5757
// layer is the interface implemented by all state layers which includes some
@@ -96,9 +96,9 @@ type Config struct {
9696
// unreasonable or unworkable.
9797
func (c *Config) sanitize() *Config {
9898
conf := *c
99-
if conf.DirtyCacheSize > maxBufferSize {
100-
log.Warn("Sanitizing invalid node buffer size", "provided", common.StorageSize(conf.DirtyCacheSize), "updated", common.StorageSize(maxBufferSize))
101-
conf.DirtyCacheSize = maxBufferSize
99+
if conf.DirtyCacheSize > MaxDirtyBufferSize {
100+
log.Warn("Sanitizing invalid node buffer size", "provided", common.StorageSize(conf.DirtyCacheSize), "updated", common.StorageSize(MaxDirtyBufferSize))
101+
conf.DirtyCacheSize = MaxDirtyBufferSize
102102
}
103103
return &conf
104104
}
@@ -107,7 +107,7 @@ func (c *Config) sanitize() *Config {
107107
var Defaults = &Config{
108108
StateHistory: params.FullImmutabilityThreshold,
109109
CleanCacheSize: defaultCleanSize,
110-
DirtyCacheSize: DefaultBufferSize,
110+
DirtyCacheSize: DefaultDirtyBufferSize,
111111
}
112112

113113
// ReadOnly is the config in order to open database in read only mode.
@@ -413,9 +413,9 @@ func (db *Database) SetBufferSize(size int) error {
413413
db.lock.Lock()
414414
defer db.lock.Unlock()
415415

416-
if size > maxBufferSize {
417-
log.Info("Capped node buffer size", "provided", common.StorageSize(size), "adjusted", common.StorageSize(maxBufferSize))
418-
size = maxBufferSize
416+
if size > MaxDirtyBufferSize {
417+
log.Info("Capped node buffer size", "provided", common.StorageSize(size), "adjusted", common.StorageSize(MaxDirtyBufferSize))
418+
size = MaxDirtyBufferSize
419419
}
420420
db.bufferSize = size
421421
return db.tree.bottom().setBufferSize(db.bufferSize)

trie/triedb/pathdb/database_test.go

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,15 @@ type tester struct {
9696
snapStorages map[common.Hash]map[common.Hash]map[common.Hash][]byte
9797
}
9898

99-
func newTester(t *testing.T) *tester {
99+
func newTester(t *testing.T, historyLimit uint64) *tester {
100100
var (
101101
disk, _ = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
102-
db = New(disk, &Config{CleanCacheSize: 256 * 1024, DirtyCacheSize: 256 * 1024})
103-
obj = &tester{
102+
db = New(disk, &Config{
103+
StateHistory: historyLimit,
104+
CleanCacheSize: 256 * 1024,
105+
DirtyCacheSize: 256 * 1024,
106+
})
107+
obj = &tester{
104108
db: db,
105109
preimages: make(map[common.Hash]common.Address),
106110
accounts: make(map[common.Hash][]byte),
@@ -376,7 +380,7 @@ func (t *tester) bottomIndex() int {
376380

377381
func TestDatabaseRollback(t *testing.T) {
378382
// Verify state histories
379-
tester := newTester(t)
383+
tester := newTester(t, 0)
380384
defer tester.release()
381385

382386
if err := tester.verifyHistory(); err != nil {
@@ -402,7 +406,7 @@ func TestDatabaseRollback(t *testing.T) {
402406

403407
func TestDatabaseRecoverable(t *testing.T) {
404408
var (
405-
tester = newTester(t)
409+
tester = newTester(t, 0)
406410
index = tester.bottomIndex()
407411
)
408412
defer tester.release()
@@ -441,7 +445,7 @@ func TestDatabaseRecoverable(t *testing.T) {
441445

442446
func TestReset(t *testing.T) {
443447
var (
444-
tester = newTester(t)
448+
tester = newTester(t, 0)
445449
index = tester.bottomIndex()
446450
)
447451
defer tester.release()
@@ -475,7 +479,7 @@ func TestReset(t *testing.T) {
475479
}
476480

477481
func TestCommit(t *testing.T) {
478-
tester := newTester(t)
482+
tester := newTester(t, 0)
479483
defer tester.release()
480484

481485
if err := tester.db.Commit(tester.lastHash(), false); err != nil {
@@ -499,7 +503,7 @@ func TestCommit(t *testing.T) {
499503
}
500504

501505
func TestJournal(t *testing.T) {
502-
tester := newTester(t)
506+
tester := newTester(t, 0)
503507
defer tester.release()
504508

505509
if err := tester.db.Journal(tester.lastHash()); err != nil {
@@ -523,7 +527,7 @@ func TestJournal(t *testing.T) {
523527
}
524528

525529
func TestCorruptedJournal(t *testing.T) {
526-
tester := newTester(t)
530+
tester := newTester(t, 0)
527531
defer tester.release()
528532

529533
if err := tester.db.Journal(tester.lastHash()); err != nil {
@@ -552,6 +556,35 @@ func TestCorruptedJournal(t *testing.T) {
552556
}
553557
}
554558

559+
// TestTailTruncateHistory function is designed to test a specific edge case where,
560+
// when history objects are removed from the end, it should trigger a state flush
561+
// if the ID of the new tail object is even higher than the persisted state ID.
562+
//
563+
// For example, let's say the ID of the persistent state is 10, and the current
564+
// history objects range from ID(5) to ID(15). As we accumulate six more objects,
565+
// the history will expand to cover ID(11) to ID(21). ID(11) then becomes the
566+
// oldest history object, and its ID is even higher than the stored state.
567+
//
568+
// In this scenario, it is mandatory to update the persistent state before
569+
// truncating the tail histories. This ensures that the ID of the persistent state
570+
// always falls within the range of [oldest-history-id, latest-history-id].
571+
func TestTailTruncateHistory(t *testing.T) {
572+
tester := newTester(t, 10)
573+
defer tester.release()
574+
575+
tester.db.Close()
576+
tester.db = New(tester.db.diskdb, &Config{StateHistory: 10})
577+
578+
head, err := tester.db.freezer.Ancients()
579+
if err != nil {
580+
t.Fatalf("Failed to obtain freezer head")
581+
}
582+
stored := rawdb.ReadPersistentStateID(tester.db.diskdb)
583+
if head != stored {
584+
t.Fatalf("Failed to truncate excess history object above, stored: %d, head: %d", stored, head)
585+
}
586+
}
587+
555588
// copyAccounts returns a deep-copied account set of the provided one.
556589
func copyAccounts(set map[common.Hash][]byte) map[common.Hash][]byte {
557590
copied := make(map[common.Hash][]byte, len(set))

trie/triedb/pathdb/difflayer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
func emptyLayer() *diskLayer {
3030
return &diskLayer{
3131
db: New(rawdb.NewMemoryDatabase(), nil),
32-
buffer: newNodeBuffer(DefaultBufferSize, nil, 0),
32+
buffer: newNodeBuffer(DefaultDirtyBufferSize, nil, 0),
3333
}
3434
}
3535

trie/triedb/pathdb/disklayer.go

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,37 +172,65 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) {
172172
dl.lock.Lock()
173173
defer dl.lock.Unlock()
174174

175-
// Construct and store the state history first. If crash happens
176-
// after storing the state history but without flushing the
177-
// corresponding states(journal), the stored state history will
178-
// be truncated in the next restart.
175+
// Construct and store the state history first. If crash happens after storing
176+
// the state history but without flushing the corresponding states(journal),
177+
// the stored state history will be truncated from head in the next restart.
178+
var (
179+
overflow bool
180+
oldest uint64
181+
)
179182
if dl.db.freezer != nil {
180-
err := writeHistory(dl.db.diskdb, dl.db.freezer, bottom, dl.db.config.StateHistory)
183+
err := writeHistory(dl.db.freezer, bottom)
181184
if err != nil {
182185
return nil, err
183186
}
187+
// Determine if the persisted history object has exceeded the configured
188+
// limitation, set the overflow as true if so.
189+
tail, err := dl.db.freezer.Tail()
190+
if err != nil {
191+
return nil, err
192+
}
193+
limit := dl.db.config.StateHistory
194+
if limit != 0 && bottom.stateID()-tail > limit {
195+
overflow = true
196+
oldest = bottom.stateID() - limit + 1 // track the id of history **after truncation**
197+
}
184198
}
185199
// Mark the diskLayer as stale before applying any mutations on top.
186200
dl.stale = true
187201

188-
// Store the root->id lookup afterwards. All stored lookups are
189-
// identified by the **unique** state root. It's impossible that
190-
// in the same chain blocks are not adjacent but have the same
191-
// root.
202+
// Store the root->id lookup afterwards. All stored lookups are identified
203+
// by the **unique** state root. It's impossible that in the same chain
204+
// blocks are not adjacent but have the same root.
192205
if dl.id == 0 {
193206
rawdb.WriteStateID(dl.db.diskdb, dl.root, 0)
194207
}
195208
rawdb.WriteStateID(dl.db.diskdb, bottom.rootHash(), bottom.stateID())
196209

197-
// Construct a new disk layer by merging the nodes from the provided
198-
// diff layer, and flush the content in disk layer if there are too
199-
// many nodes cached. The clean cache is inherited from the original
200-
// disk layer for reusing.
210+
// Construct a new disk layer by merging the nodes from the provided diff
211+
// layer, and flush the content in disk layer if there are too many nodes
212+
// cached. The clean cache is inherited from the original disk layer.
201213
ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.cleans, dl.buffer.commit(bottom.nodes))
202-
err := ndl.buffer.flush(ndl.db.diskdb, ndl.cleans, ndl.id, force)
203-
if err != nil {
214+
215+
// In a unique scenario where the ID of the oldest history object (after tail
216+
// truncation) surpasses the persisted state ID, we take the necessary action
217+
// of forcibly committing the cached dirty nodes to ensure that the persisted
218+
// state ID remains higher.
219+
if !force && rawdb.ReadPersistentStateID(dl.db.diskdb) < oldest {
220+
force = true
221+
}
222+
if err := ndl.buffer.flush(ndl.db.diskdb, ndl.cleans, ndl.id, force); err != nil {
204223
return nil, err
205224
}
225+
// To remove outdated history objects from the end, we set the 'tail' parameter
226+
// to 'oldest-1' due to the offset between the freezer index and the history ID.
227+
if overflow {
228+
pruned, err := truncateFromTail(ndl.db.diskdb, ndl.db.freezer, oldest-1)
229+
if err != nil {
230+
return nil, err
231+
}
232+
log.Debug("Pruned state history", "items", pruned, "tailid", oldest)
233+
}
206234
return ndl, nil
207235
}
208236

trie/triedb/pathdb/history.go

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -512,38 +512,28 @@ func readHistory(freezer *rawdb.ResettableFreezer, id uint64) (*history, error)
512512
return &dec, nil
513513
}
514514

515-
// writeHistory writes the state history with provided state set. After
516-
// storing the corresponding state history, it will also prune the stale
517-
// histories from the disk with the given threshold.
518-
func writeHistory(db ethdb.KeyValueStore, freezer *rawdb.ResettableFreezer, dl *diffLayer, limit uint64) error {
515+
// writeHistory persists the state history with the provided state set.
516+
func writeHistory(freezer *rawdb.ResettableFreezer, dl *diffLayer) error {
519517
// Short circuit if state set is not available.
520518
if dl.states == nil {
521519
return errors.New("state change set is not available")
522520
}
523521
var (
524-
err error
525-
n int
526-
start = time.Now()
527-
h = newHistory(dl.rootHash(), dl.parentLayer().rootHash(), dl.block, dl.states)
522+
start = time.Now()
523+
history = newHistory(dl.rootHash(), dl.parentLayer().rootHash(), dl.block, dl.states)
528524
)
529-
accountData, storageData, accountIndex, storageIndex := h.encode()
525+
accountData, storageData, accountIndex, storageIndex := history.encode()
530526
dataSize := common.StorageSize(len(accountData) + len(storageData))
531527
indexSize := common.StorageSize(len(accountIndex) + len(storageIndex))
532528

533529
// Write history data into five freezer table respectively.
534-
rawdb.WriteStateHistory(freezer, dl.stateID(), h.meta.encode(), accountIndex, storageIndex, accountData, storageData)
530+
rawdb.WriteStateHistory(freezer, dl.stateID(), history.meta.encode(), accountIndex, storageIndex, accountData, storageData)
535531

536-
// Prune stale state histories based on the config.
537-
if limit != 0 && dl.stateID() > limit {
538-
n, err = truncateFromTail(db, freezer, dl.stateID()-limit)
539-
if err != nil {
540-
return err
541-
}
542-
}
543532
historyDataBytesMeter.Mark(int64(dataSize))
544533
historyIndexBytesMeter.Mark(int64(indexSize))
545534
historyBuildTimeMeter.UpdateSince(start)
546-
log.Debug("Stored state history", "id", dl.stateID(), "block", dl.block, "data", dataSize, "index", indexSize, "pruned", n, "elapsed", common.PrettyDuration(time.Since(start)))
535+
log.Debug("Stored state history", "id", dl.stateID(), "block", dl.block, "data", dataSize, "index", indexSize, "elapsed", common.PrettyDuration(time.Since(start)))
536+
547537
return nil
548538
}
549539

0 commit comments

Comments
 (0)