mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
triedb/pathdb: introduce metadata and version tracking
This commit is contained in:
parent
27fbfdcce6
commit
69fa0683ad
6 changed files with 96 additions and 60 deletions
|
|
@ -17,35 +17,29 @@
|
||||||
package rawdb
|
package rawdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadLastStateHistoryIndex retrieves the number of latest indexed state history.
|
// ReadStateHistoryIndexMetadata retrieves the metadata of state history index.
|
||||||
func ReadLastStateHistoryIndex(db ethdb.KeyValueReader) *uint64 {
|
func ReadStateHistoryIndexMetadata(db ethdb.KeyValueReader) []byte {
|
||||||
data, _ := db.Get(headStateHistoryIndexKey)
|
data, _ := db.Get(headStateHistoryIndexKey)
|
||||||
if len(data) != 8 {
|
return data
|
||||||
return nil
|
|
||||||
}
|
|
||||||
number := binary.BigEndian.Uint64(data)
|
|
||||||
return &number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteLastStateHistoryIndex stores the number of latest indexed state history
|
// WriteStateHistoryIndexMetadata stores the metadata of state history index
|
||||||
// into database.
|
// into database.
|
||||||
func WriteLastStateHistoryIndex(db ethdb.KeyValueWriter, number uint64) {
|
func WriteStateHistoryIndexMetadata(db ethdb.KeyValueWriter, blob []byte) {
|
||||||
if err := db.Put(headStateHistoryIndexKey, encodeBlockNumber(number)); err != nil {
|
if err := db.Put(headStateHistoryIndexKey, blob); err != nil {
|
||||||
log.Crit("Failed to store the state index tail", "err", err)
|
log.Crit("Failed to store the metadata of state history index", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteLastStateHistoryIndex removes the number of latest indexed state history.
|
// DeleteStateHistoryIndexMetadata removes the metadata of state history index.
|
||||||
func DeleteLastStateHistoryIndex(db ethdb.KeyValueWriter) {
|
func DeleteStateHistoryIndexMetadata(db ethdb.KeyValueWriter) {
|
||||||
if err := db.Delete(headStateHistoryIndexKey); err != nil {
|
if err := db.Delete(headStateHistoryIndexKey); err != nil {
|
||||||
log.Crit("Failed to delete the state index tail", "err", err)
|
log.Crit("Failed to delete the metadata of state history index", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,12 +152,12 @@ func increaseKey(key []byte) []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHistoryIndex completely removes all history indexing data, including indexes
|
// DeleteStateHistoryIndex completely removes all history indexing data, including
|
||||||
// for accounts and storages.
|
// indexes for accounts and storages.
|
||||||
//
|
//
|
||||||
// Note, this method assumes the storage space with prefix `StateHistoryIndexPrefix`
|
// Note, this method assumes the storage space with prefix `StateHistoryIndexPrefix`
|
||||||
// is exclusively occupied by the history indexing data!
|
// is exclusively occupied by the history indexing data!
|
||||||
func DeleteHistoryIndex(db ethdb.KeyValueRangeDeleter) {
|
func DeleteStateHistoryIndex(db ethdb.KeyValueRangeDeleter) {
|
||||||
if err := db.DeleteRange(StateHistoryIndexPrefix, increaseKey(StateHistoryIndexPrefix)); err != nil {
|
if err := db.DeleteRange(StateHistoryIndexPrefix, increaseKey(StateHistoryIndexPrefix)); err != nil {
|
||||||
log.Crit("Failed to delete history index range", "err", err)
|
log.Crit("Failed to delete history index range", "err", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -314,8 +314,8 @@ func (db *Database) repairHistory() error {
|
||||||
// TODO(rjl493456442) would be better to group them into a batch.
|
// TODO(rjl493456442) would be better to group them into a batch.
|
||||||
//
|
//
|
||||||
// Purge all state history indexing data first
|
// Purge all state history indexing data first
|
||||||
rawdb.DeleteLastStateHistoryIndex(db.diskdb)
|
rawdb.DeleteStateHistoryIndexMetadata(db.diskdb)
|
||||||
rawdb.DeleteHistoryIndex(db.diskdb)
|
rawdb.DeleteStateHistoryIndex(db.diskdb)
|
||||||
err := db.freezer.Reset()
|
err := db.freezer.Reset()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("Failed to reset state histories", "err", err)
|
log.Crit("Failed to reset state histories", "err", err)
|
||||||
|
|
@ -507,8 +507,8 @@ func (db *Database) Enable(root common.Hash) error {
|
||||||
// TODO(rjl493456442) would be better to group them into a batch.
|
// TODO(rjl493456442) would be better to group them into a batch.
|
||||||
//
|
//
|
||||||
// Purge all state history indexing data first
|
// Purge all state history indexing data first
|
||||||
rawdb.DeleteLastStateHistoryIndex(db.diskdb)
|
rawdb.DeleteStateHistoryIndexMetadata(db.diskdb)
|
||||||
rawdb.DeleteHistoryIndex(db.diskdb)
|
rawdb.DeleteStateHistoryIndex(db.diskdb)
|
||||||
if err := db.freezer.Reset(); err != nil {
|
if err := db.freezer.Reset(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -189,8 +189,8 @@ func TestBatchIndexerWrite(t *testing.T) {
|
||||||
if err := batch.finish(true); err != nil {
|
if err := batch.finish(true); err != nil {
|
||||||
t.Fatalf("Failed to finish batch indexer, %v", err)
|
t.Fatalf("Failed to finish batch indexer, %v", err)
|
||||||
}
|
}
|
||||||
indexed := rawdb.ReadLastStateHistoryIndex(db)
|
metadata := loadIndexMetadata(db)
|
||||||
if indexed == nil || *indexed != uint64(10) {
|
if metadata == nil || metadata.Last != uint64(10) {
|
||||||
t.Fatal("Unexpected index position")
|
t.Fatal("Unexpected index position")
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
|
|
@ -278,8 +278,8 @@ func TestBatchIndexerDelete(t *testing.T) {
|
||||||
t.Fatalf("Failed to finish batch indexer, %v", err)
|
t.Fatalf("Failed to finish batch indexer, %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed := rawdb.ReadLastStateHistoryIndex(db)
|
metadata := loadIndexMetadata(db)
|
||||||
if indexed != nil {
|
if metadata != nil {
|
||||||
t.Fatal("Unexpected index position")
|
t.Fatal("Unexpected index position")
|
||||||
}
|
}
|
||||||
it := db.NewIterator(rawdb.StateHistoryIndexPrefix, nil)
|
it := db.NewIterator(rawdb.StateHistoryIndexPrefix, nil)
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,44 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The batch size for reading state histories
|
const (
|
||||||
const historyReadBatch = 1000
|
// The batch size for reading state histories
|
||||||
|
historyReadBatch = 1000
|
||||||
|
|
||||||
|
stateIndexV0 = uint8(0) // initial version of state index structure
|
||||||
|
stateIndexVersion = stateIndexV0 // the current state index version
|
||||||
|
)
|
||||||
|
|
||||||
|
type indexMetadata struct {
|
||||||
|
Version uint8
|
||||||
|
Last uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadIndexMetadata(db ethdb.KeyValueReader) *indexMetadata {
|
||||||
|
blob := rawdb.ReadStateHistoryIndexMetadata(db)
|
||||||
|
if len(blob) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var m indexMetadata
|
||||||
|
if err := rlp.DecodeBytes(blob, &m); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeIndexMetadata(db ethdb.KeyValueWriter, last uint64) {
|
||||||
|
var m indexMetadata
|
||||||
|
m.Version = stateIndexVersion
|
||||||
|
m.Last = last
|
||||||
|
blob, err := rlp.EncodeToBytes(m)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Failed to encode index metadata", "err", err)
|
||||||
|
}
|
||||||
|
rawdb.WriteStateHistoryIndexMetadata(db, blob)
|
||||||
|
}
|
||||||
|
|
||||||
// batchIndexer is a structure designed to perform batch indexing or unindexing
|
// batchIndexer is a structure designed to perform batch indexing or unindexing
|
||||||
// of state histories atomically.
|
// of state histories atomically.
|
||||||
|
|
@ -144,12 +178,12 @@ func (b *batchIndexer) finish(force bool) error {
|
||||||
}
|
}
|
||||||
// Update the position of last indexed state history
|
// Update the position of last indexed state history
|
||||||
if !b.delete {
|
if !b.delete {
|
||||||
rawdb.WriteLastStateHistoryIndex(batch, b.lastID)
|
storeIndexMetadata(batch, b.lastID)
|
||||||
} else {
|
} else {
|
||||||
if b.lastID == 1 {
|
if b.lastID == 1 {
|
||||||
rawdb.DeleteLastStateHistoryIndex(batch)
|
rawdb.DeleteStateHistoryIndexMetadata(batch)
|
||||||
} else {
|
} else {
|
||||||
rawdb.WriteLastStateHistoryIndex(batch, b.lastID-1)
|
storeIndexMetadata(batch, b.lastID-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := batch.Write(); err != nil {
|
if err := batch.Write(); err != nil {
|
||||||
|
|
@ -167,11 +201,11 @@ func indexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.Ancient
|
||||||
indexHistoryTimer.UpdateSince(start)
|
indexHistoryTimer.UpdateSince(start)
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
indexed := rawdb.ReadLastStateHistoryIndex(db)
|
metadata := loadIndexMetadata(db)
|
||||||
if indexed == nil || *indexed+1 != historyID {
|
if metadata == nil || metadata.Last+1 != historyID {
|
||||||
last := "null"
|
last := "null"
|
||||||
if indexed != nil {
|
if metadata != nil {
|
||||||
last = fmt.Sprintf("%v", *indexed)
|
last = fmt.Sprintf("%v", metadata.Last)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("history indexing is out of order, last: %s, requested: %d", last, historyID)
|
return fmt.Errorf("history indexing is out of order, last: %s, requested: %d", last, historyID)
|
||||||
}
|
}
|
||||||
|
|
@ -196,11 +230,11 @@ func unindexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.Ancie
|
||||||
unindexHistoryTimer.UpdateSince(start)
|
unindexHistoryTimer.UpdateSince(start)
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
indexed := rawdb.ReadLastStateHistoryIndex(db)
|
metadata := loadIndexMetadata(db)
|
||||||
if indexed == nil || *indexed != historyID {
|
if metadata == nil || metadata.Last != historyID {
|
||||||
last := "null"
|
last := "null"
|
||||||
if indexed != nil {
|
if metadata != nil {
|
||||||
last = fmt.Sprintf("%v", *indexed)
|
last = fmt.Sprintf("%v", metadata.Last)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("history unindexing is out of order, last: %s, requested: %d", last, historyID)
|
return fmt.Errorf("history unindexing is out of order, last: %s, requested: %d", last, historyID)
|
||||||
}
|
}
|
||||||
|
|
@ -286,8 +320,8 @@ func (i *indexIniter) run(lastID uint64) {
|
||||||
// checkDone indicates whether all requested state histories
|
// checkDone indicates whether all requested state histories
|
||||||
// have been fully indexed.
|
// have been fully indexed.
|
||||||
checkDone = func() bool {
|
checkDone = func() bool {
|
||||||
indexed := rawdb.ReadLastStateHistoryIndex(i.disk)
|
metadata := loadIndexMetadata(i.disk)
|
||||||
return indexed != nil && *indexed == lastID
|
return metadata != nil && metadata.Last == lastID
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
go i.index(done, interrupt, lastID)
|
go i.index(done, interrupt, lastID)
|
||||||
|
|
@ -364,22 +398,22 @@ func (i *indexIniter) next() (uint64, error) {
|
||||||
tailID := tail + 1 // compute the id of the oldest history
|
tailID := tail + 1 // compute the id of the oldest history
|
||||||
|
|
||||||
// Start indexing from scratch if nothing has been indexed
|
// Start indexing from scratch if nothing has been indexed
|
||||||
lastIndexed := rawdb.ReadLastStateHistoryIndex(i.disk)
|
metadata := loadIndexMetadata(i.disk)
|
||||||
if lastIndexed == nil {
|
if metadata == nil {
|
||||||
log.Debug("Initialize state history indexing from scratch", "id", tailID)
|
log.Debug("Initialize state history indexing from scratch", "id", tailID)
|
||||||
return tailID, nil
|
return tailID, nil
|
||||||
}
|
}
|
||||||
// Resume indexing from the last interrupted position
|
// Resume indexing from the last interrupted position
|
||||||
if *lastIndexed+1 >= tailID {
|
if metadata.Last+1 >= tailID {
|
||||||
log.Debug("Resume state history indexing", "id", *lastIndexed+1, "tail", tailID)
|
log.Debug("Resume state history indexing", "id", metadata.Last+1, "tail", tailID)
|
||||||
return *lastIndexed + 1, nil
|
return metadata.Last + 1, nil
|
||||||
}
|
}
|
||||||
// History has been shortened without indexing. Discard the gapped segment
|
// History has been shortened without indexing. Discard the gapped segment
|
||||||
// in the history and shift to the first available element.
|
// in the history and shift to the first available element.
|
||||||
//
|
//
|
||||||
// The missing indexes corresponding to the gapped histories won't be visible.
|
// The missing indexes corresponding to the gapped histories won't be visible.
|
||||||
// It's fine to leave them unindexed.
|
// It's fine to leave them unindexed.
|
||||||
log.Info("History gap detected, discard old segment", "oldHead", *lastIndexed, "newHead", tailID)
|
log.Info("History gap detected, discard old segment", "oldHead", metadata.Last, "newHead", tailID)
|
||||||
return tailID, nil
|
return tailID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -476,6 +510,14 @@ type historyIndexer struct {
|
||||||
// newHistoryIndexer constructs the history indexer and launches the background
|
// newHistoryIndexer constructs the history indexer and launches the background
|
||||||
// initer to complete the indexing of any remaining state histories.
|
// initer to complete the indexing of any remaining state histories.
|
||||||
func newHistoryIndexer(disk ethdb.KeyValueStore, freezer ethdb.AncientStore, lastHistoryID uint64) *historyIndexer {
|
func newHistoryIndexer(disk ethdb.KeyValueStore, freezer ethdb.AncientStore, lastHistoryID uint64) *historyIndexer {
|
||||||
|
// Purge the obsolete index data from the database.
|
||||||
|
metadata := loadIndexMetadata(disk)
|
||||||
|
if metadata != nil && metadata.Version != stateIndexVersion {
|
||||||
|
// TODO(rjl493456442) would be better to group them into a batch.
|
||||||
|
rawdb.DeleteStateHistoryIndexMetadata(disk)
|
||||||
|
rawdb.DeleteStateHistoryIndex(disk)
|
||||||
|
log.Info("Cleaned up obsolete state history index", "version", metadata.Version, "want", stateIndexVersion)
|
||||||
|
}
|
||||||
return &historyIndexer{
|
return &historyIndexer{
|
||||||
initer: newIndexIniter(disk, freezer, lastHistoryID),
|
initer: newIndexIniter(disk, freezer, lastHistoryID),
|
||||||
disk: disk,
|
disk: disk,
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,8 @@ type indexReaderWithLimitTag struct {
|
||||||
// newIndexReaderWithLimitTag constructs a index reader with indexing position.
|
// newIndexReaderWithLimitTag constructs a index reader with indexing position.
|
||||||
func newIndexReaderWithLimitTag(db ethdb.KeyValueReader, state stateIdent) (*indexReaderWithLimitTag, error) {
|
func newIndexReaderWithLimitTag(db ethdb.KeyValueReader, state stateIdent) (*indexReaderWithLimitTag, error) {
|
||||||
// Read the last indexed ID before the index reader construction
|
// Read the last indexed ID before the index reader construction
|
||||||
indexed := rawdb.ReadLastStateHistoryIndex(db)
|
metadata := loadIndexMetadata(db)
|
||||||
if indexed == nil {
|
if metadata == nil {
|
||||||
return nil, errors.New("state history hasn't been indexed yet")
|
return nil, errors.New("state history hasn't been indexed yet")
|
||||||
}
|
}
|
||||||
r, err := newIndexReader(db, state)
|
r, err := newIndexReader(db, state)
|
||||||
|
|
@ -123,7 +123,7 @@ func newIndexReaderWithLimitTag(db ethdb.KeyValueReader, state stateIdent) (*ind
|
||||||
}
|
}
|
||||||
return &indexReaderWithLimitTag{
|
return &indexReaderWithLimitTag{
|
||||||
reader: r,
|
reader: r,
|
||||||
limit: *indexed,
|
limit: metadata.Last,
|
||||||
db: db,
|
db: db,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -157,14 +157,14 @@ func (r *indexReaderWithLimitTag) readGreaterThan(id uint64, lastID uint64) (uin
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
// Refresh the index reader and give another attempt
|
// Refresh the index reader and give another attempt
|
||||||
indexed := rawdb.ReadLastStateHistoryIndex(r.db)
|
metadata := loadIndexMetadata(r.db)
|
||||||
if indexed == nil || *indexed < lastID {
|
if metadata == nil || metadata.Last < lastID {
|
||||||
return 0, errors.New("state history hasn't been indexed yet")
|
return 0, errors.New("state history hasn't been indexed yet")
|
||||||
}
|
}
|
||||||
if err := r.reader.refresh(); err != nil {
|
if err := r.reader.refresh(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
r.limit = *indexed
|
r.limit = metadata.Last
|
||||||
|
|
||||||
return r.reader.readGreaterThan(id)
|
return r.reader.readGreaterThan(id)
|
||||||
}
|
}
|
||||||
|
|
@ -308,15 +308,15 @@ func (r *historyReader) read(state stateIdentQuery, stateID uint64, lastID uint6
|
||||||
if stateID < tail {
|
if stateID < tail {
|
||||||
return nil, errors.New("historical state has been pruned")
|
return nil, errors.New("historical state has been pruned")
|
||||||
}
|
}
|
||||||
lastIndexedID := rawdb.ReadLastStateHistoryIndex(r.disk)
|
|
||||||
|
|
||||||
// To serve the request, all state histories from stateID+1 to lastID
|
// To serve the request, all state histories from stateID+1 to lastID
|
||||||
// must be indexed. It's not supposed to happen unless system is very
|
// must be indexed. It's not supposed to happen unless system is very
|
||||||
// wrong.
|
// wrong.
|
||||||
if lastIndexedID == nil || *lastIndexedID < lastID {
|
metadata := loadIndexMetadata(r.disk)
|
||||||
|
if metadata == nil || metadata.Last < lastID {
|
||||||
indexed := "null"
|
indexed := "null"
|
||||||
if lastIndexedID != nil {
|
if metadata != nil {
|
||||||
indexed = fmt.Sprintf("%d", *lastIndexedID)
|
indexed = fmt.Sprintf("%d", metadata.Last)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("state history is not fully indexed, requested: %d, indexed: %s", stateID, indexed)
|
return nil, fmt.Errorf("state history is not fully indexed, requested: %d, indexed: %s", stateID, indexed)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ import (
|
||||||
|
|
||||||
func waitIndexing(db *Database) {
|
func waitIndexing(db *Database) {
|
||||||
for {
|
for {
|
||||||
id := rawdb.ReadLastStateHistoryIndex(db.diskdb)
|
metadata := loadIndexMetadata(db.diskdb)
|
||||||
if id != nil && *id >= db.tree.bottom().stateID() {
|
if metadata != nil && metadata.Last >= db.tree.bottom().stateID() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue