trie/triedb/pathdb: improve dirty node flushing trigger

This commit is contained in:
Gary Rong 2023-10-27 19:12:06 +08:00
parent 4cbca5178a
commit 665597c631
2 changed files with 51 additions and 33 deletions

View file

@ -172,37 +172,65 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) {
dl.lock.Lock() dl.lock.Lock()
defer dl.lock.Unlock() defer dl.lock.Unlock()
// Construct and store the state history first. If crash happens // Construct and store the state history first. If crash happens after storing
// after storing the state history but without flushing the // the state history but without flushing the corresponding states(journal),
// corresponding states(journal), the stored state history will // the stored state history will be truncated from head in the next restart.
// be truncated in the next restart. var (
overflow bool
oldest uint64
)
if dl.db.freezer != nil { if dl.db.freezer != nil {
err := writeHistory(dl.db.diskdb, dl.db.freezer, bottom, dl.db.config.StateHistory) err := writeHistory(dl.db.freezer, bottom)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Determine if the persisted history object has exceeded the configured
// limitation, set the overflow as true if so.
tail, err := dl.db.freezer.Tail()
if err != nil {
return nil, err
}
limit := dl.db.config.StateHistory
if limit != 0 && bottom.stateID()-tail > limit {
overflow = true
oldest = bottom.stateID() - limit + 1 // track the id of history **after truncation**
}
} }
// Mark the diskLayer as stale before applying any mutations on top. // Mark the diskLayer as stale before applying any mutations on top.
dl.stale = true dl.stale = true
// Store the root->id lookup afterwards. All stored lookups are // Store the root->id lookup afterwards. All stored lookups are identified
// identified by the **unique** state root. It's impossible that // by the **unique** state root. It's impossible that in the same chain
// in the same chain blocks are not adjacent but have the same // blocks are not adjacent but have the same root.
// root.
if dl.id == 0 { if dl.id == 0 {
rawdb.WriteStateID(dl.db.diskdb, dl.root, 0) rawdb.WriteStateID(dl.db.diskdb, dl.root, 0)
} }
rawdb.WriteStateID(dl.db.diskdb, bottom.rootHash(), bottom.stateID()) rawdb.WriteStateID(dl.db.diskdb, bottom.rootHash(), bottom.stateID())
// Construct a new disk layer by merging the nodes from the provided // Construct a new disk layer by merging the nodes from the provided diff
// diff layer, and flush the content in disk layer if there are too // layer, and flush the content in disk layer if there are too many nodes
// many nodes cached. The clean cache is inherited from the original // cached. The clean cache is inherited from the original disk layer.
// disk layer for reusing.
ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.cleans, dl.buffer.commit(bottom.nodes)) ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.cleans, dl.buffer.commit(bottom.nodes))
err := ndl.buffer.flush(ndl.db.diskdb, ndl.cleans, ndl.id, force)
// In a unique scenario where the ID of the oldest history object (after tail
// truncation) surpasses the persisted state ID, we take the necessary action
// of forcibly committing the cached dirty nodes to ensure that the persisted
// state ID remains higher.
if !force && rawdb.ReadPersistentStateID(dl.db.diskdb) < oldest {
force = true
}
if err := ndl.buffer.flush(ndl.db.diskdb, ndl.cleans, ndl.id, force); err != nil {
return nil, err
}
// To remove outdated history objects from the end, we set the 'tail' parameter
// to 'oldest-1' due to the offset between the freezer index and the history ID.
if overflow {
pruned, err := truncateFromTail(ndl.db.diskdb, ndl.db.freezer, oldest-1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Debug("Prune state history", "number", pruned)
}
return ndl, nil return ndl, nil
} }

View file

@ -512,38 +512,28 @@ func readHistory(freezer *rawdb.ResettableFreezer, id uint64) (*history, error)
return &dec, nil return &dec, nil
} }
// writeHistory writes the state history with provided state set. After // writeHistory persists the state history with the provided state set.
// storing the corresponding state history, it will also prune the stale func writeHistory(freezer *rawdb.ResettableFreezer, dl *diffLayer) error {
// histories from the disk with the given threshold.
func writeHistory(db ethdb.KeyValueStore, freezer *rawdb.ResettableFreezer, dl *diffLayer, limit uint64) error {
// Short circuit if state set is not available. // Short circuit if state set is not available.
if dl.states == nil { if dl.states == nil {
return errors.New("state change set is not available") return errors.New("state change set is not available")
} }
var ( var (
err error
n int
start = time.Now() start = time.Now()
h = newHistory(dl.rootHash(), dl.parentLayer().rootHash(), dl.block, dl.states) history = newHistory(dl.rootHash(), dl.parentLayer().rootHash(), dl.block, dl.states)
) )
accountData, storageData, accountIndex, storageIndex := h.encode() accountData, storageData, accountIndex, storageIndex := history.encode()
dataSize := common.StorageSize(len(accountData) + len(storageData)) dataSize := common.StorageSize(len(accountData) + len(storageData))
indexSize := common.StorageSize(len(accountIndex) + len(storageIndex)) indexSize := common.StorageSize(len(accountIndex) + len(storageIndex))
// Write history data into five freezer table respectively. // Write history data into five freezer table respectively.
rawdb.WriteStateHistory(freezer, dl.stateID(), h.meta.encode(), accountIndex, storageIndex, accountData, storageData) rawdb.WriteStateHistory(freezer, dl.stateID(), history.meta.encode(), accountIndex, storageIndex, accountData, storageData)
// Prune stale state histories based on the config.
if limit != 0 && dl.stateID() > limit {
n, err = truncateFromTail(db, freezer, dl.stateID()-limit)
if err != nil {
return err
}
}
historyDataBytesMeter.Mark(int64(dataSize)) historyDataBytesMeter.Mark(int64(dataSize))
historyIndexBytesMeter.Mark(int64(indexSize)) historyIndexBytesMeter.Mark(int64(indexSize))
historyBuildTimeMeter.UpdateSince(start) historyBuildTimeMeter.UpdateSince(start)
log.Debug("Stored state history", "id", dl.stateID(), "block", dl.block, "data", dataSize, "index", indexSize, "pruned", n, "elapsed", common.PrettyDuration(time.Since(start))) log.Debug("Stored state history", "id", dl.stateID(), "block", dl.block, "data", dataSize, "index", indexSize, "elapsed", common.PrettyDuration(time.Since(start)))
return nil return nil
} }