From 2b64c5c93875f1c68811365d9aa21f28bec0d319 Mon Sep 17 00:00:00 2001 From: Delweng Date: Thu, 24 Jul 2025 14:21:49 +0800 Subject: [PATCH] no global cache Signed-off-by: Delweng --- triedb/pathdb/database.go | 9 +++- triedb/pathdb/history_index.go | 80 ++++++++++++++++++---------- triedb/pathdb/history_index_test.go | 28 +++++----- triedb/pathdb/history_indexer.go | 33 +++++++----- triedb/pathdb/history_reader.go | 31 +++++++---- triedb/pathdb/history_reader_test.go | 2 +- triedb/pathdb/reader.go | 2 +- 7 files changed, 114 insertions(+), 71 deletions(-) diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index e323a7449e..6e405cbe9a 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -60,6 +60,9 @@ const ( var ( // maxDiffLayers is the maximum diff layers allowed in the layer tree. maxDiffLayers = 128 + + // historyCacheSize is the maximum size of history cache. + historyCacheSize = 4096 ) // layer is the interface implemented by all state layers which includes some @@ -225,6 +228,7 @@ type Database struct { freezer ethdb.ResettableAncientStore // Freezer for storing trie histories, nil possible in tests lock sync.RWMutex // Lock to prevent mutations from happening at the same time indexer *historyIndexer // History indexer + cacher *historyCacher // History cacher } // New attempts to load an already existing layer from a persistent key-value @@ -242,6 +246,7 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { config: config, diskdb: diskdb, hasher: merkleNodeHasher, + cacher: newHistoryCacher(historyCacheSize), } // Establish a dedicated database namespace tailored for verkle-specific // data, ensuring the isolation of both verkle and merkle tree data. It's @@ -276,7 +281,7 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { } // TODO (rjl493456442) disable the background indexing in read-only mode if db.freezer != nil && db.config.EnableStateIndexing { - db.indexer = newHistoryIndexer(db.diskdb, db.freezer, db.tree.bottom().stateID()) + db.indexer = newHistoryIndexer(db.diskdb, db.freezer, db.tree.bottom().stateID(), db.cacher) log.Info("Enabled state history indexing") } fields := config.fields() @@ -531,7 +536,7 @@ func (db *Database) Enable(root common.Hash) error { // 2. Re-initialize the indexer so it starts indexing from the new state root. if db.indexer != nil && db.freezer != nil && db.config.EnableStateIndexing { db.indexer.close() - db.indexer = newHistoryIndexer(db.diskdb, db.freezer, db.tree.bottom().stateID()) + db.indexer = newHistoryIndexer(db.diskdb, db.freezer, db.tree.bottom().stateID(), db.cacher) log.Info("Re-enabled state history indexing") } log.Info("Rebuilt trie database", "root", root) diff --git a/triedb/pathdb/history_index.go b/triedb/pathdb/history_index.go index 20895019c5..1096a1a0a5 100644 --- a/triedb/pathdb/history_index.go +++ b/triedb/pathdb/history_index.go @@ -76,15 +76,16 @@ type indexReader struct { readers map[uint32]*blockReader state stateIdent timings *readTimings + cacher *historyCacher } // loadIndexData loads the index data associated with the specified state. -func loadIndexData(db ethdb.KeyValueReader, state stateIdent, timings *readTimings, cacheRead bool) ([]*indexBlockDesc, error) { +func loadIndexData(db ethdb.KeyValueReader, state stateIdent, timings *readTimings, cacheRead bool, cacher *historyCacher) ([]*indexBlockDesc, error) { start := time.Now() key := state.String() var blob []byte - if cacheRead && historyIndexCache.Contains(key) { - blob, _ = historyIndexCache.Get(key) + if cacheRead && cacher != nil && cacher.index.Contains(key) { + blob, _ = cacher.index.Get(key) } else { if state.account { blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash) @@ -98,19 +99,21 @@ func loadIndexData(db ethdb.KeyValueReader, state stateIdent, timings *readTimin if len(blob) == 0 { return nil, nil } - historyIndexCache.Add(key, blob) + if cacher != nil { + cacher.index.Add(key, blob) + } return parseIndex(blob) } // newIndexReader constructs a index reader for the specified state. Reader with // empty data is allowed. -func newIndexReader(db ethdb.KeyValueReader, state stateIdent) (*indexReader, error) { - return newIndexReaderWithTimings(db, state, nil) +func newIndexReader(db ethdb.KeyValueReader, state stateIdent, cacher *historyCacher) (*indexReader, error) { + return newIndexReaderWithTimings(db, state, nil, cacher) } // Helper to allow passing timings -func newIndexReaderWithTimings(db ethdb.KeyValueReader, state stateIdent, timings *readTimings) (*indexReader, error) { - descList, err := loadIndexData(db, state, timings, true) +func newIndexReaderWithTimings(db ethdb.KeyValueReader, state stateIdent, timings *readTimings, cacher *historyCacher) (*indexReader, error) { + descList, err := loadIndexData(db, state, timings, true, cacher) if err != nil { return nil, err } @@ -120,6 +123,7 @@ func newIndexReaderWithTimings(db ethdb.KeyValueReader, state stateIdent, timing db: db, state: state, timings: timings, + cacher: cacher, }, nil } @@ -134,7 +138,7 @@ func (r *indexReader) refresh() error { delete(r.readers, last.id) } } - descList, err := loadIndexData(r.db, r.state, r.timings, false) + descList, err := loadIndexData(r.db, r.state, r.timings, false, r.cacher) if err != nil { return err } @@ -161,16 +165,17 @@ func (r *indexReader) readGreaterThan(id uint64) (uint64, error) { ) start := time.Now() key := fmt.Sprintf("%s:%d", r.state.String(), desc.id) - if val, ok := historyBlockCache.Get(key); ok { - blob = val + if r.cacher != nil && r.cacher.block.Contains(key) { + blob, _ = r.cacher.block.Get(key) } else { if r.state.account { blob = rawdb.ReadAccountHistoryIndexBlock(r.db, r.state.addressHash, desc.id) } else { blob = rawdb.ReadStorageHistoryIndexBlock(r.db, r.state.addressHash, r.state.storageHash, desc.id) } - if len(blob) > 0 { - historyBlockCache.Add(key, blob) + + if r.cacher != nil && len(blob) > 0 { + r.cacher.block.Add(key, blob) } } if r.timings != nil { @@ -200,10 +205,11 @@ type indexWriter struct { lastID uint64 // The ID of the latest tracked history state stateIdent db ethdb.KeyValueReader + cacher *historyCacher } // newIndexWriter constructs the index writer for the specified state. -func newIndexWriter(db ethdb.KeyValueReader, state stateIdent) (*indexWriter, error) { +func newIndexWriter(db ethdb.KeyValueReader, state stateIdent, cacher *historyCacher) (*indexWriter, error) { var blob []byte if state.account { blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash) @@ -218,6 +224,7 @@ func newIndexWriter(db ethdb.KeyValueReader, state stateIdent) (*indexWriter, er bw: bw, state: state, db: db, + cacher: cacher, }, nil } descList, err := parseIndex(blob) @@ -243,6 +250,7 @@ func newIndexWriter(db ethdb.KeyValueReader, state stateIdent) (*indexWriter, er bw: bw, state: state, db: db, + cacher: cacher, }, nil } @@ -301,8 +309,10 @@ func (w *indexWriter) finish(batch ethdb.Batch) { } for _, bw := range writers { buf := bw.finish() - if key := fmt.Sprintf("%s:%d", w.state.String(), bw.desc.id); historyBlockCache.Contains(key) { - historyBlockCache.Add(key, buf) + if w.cacher != nil { + if key := fmt.Sprintf("%s:%d", w.state.String(), bw.desc.id); w.cacher.block.Contains(key) { + w.cacher.block.Add(key, buf) + } } if w.state.account { rawdb.WriteAccountHistoryIndexBlock(batch, w.state.addressHash, bw.desc.id, buf) @@ -316,8 +326,10 @@ func (w *indexWriter) finish(batch ethdb.Batch) { for _, desc := range descList { buf = append(buf, desc.encode()...) } - if key := w.state.String(); historyIndexCache.Contains(key) { - historyIndexCache.Add(key, buf) + if w.cacher != nil { + if key := w.state.String(); w.cacher.index.Contains(key) { + w.cacher.index.Add(key, buf) + } } if w.state.account { rawdb.WriteAccountHistoryIndex(batch, w.state.addressHash, buf) @@ -334,10 +346,11 @@ type indexDeleter struct { lastID uint64 // The ID of the latest tracked history state stateIdent db ethdb.KeyValueReader + cacher *historyCacher } // newIndexDeleter constructs the index deleter for the specified state. -func newIndexDeleter(db ethdb.KeyValueReader, state stateIdent) (*indexDeleter, error) { +func newIndexDeleter(db ethdb.KeyValueReader, state stateIdent, cacher *historyCacher) (*indexDeleter, error) { var blob []byte if state.account { blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash) @@ -354,6 +367,7 @@ func newIndexDeleter(db ethdb.KeyValueReader, state stateIdent) (*indexDeleter, bw: bw, state: state, db: db, + cacher: cacher, }, nil } descList, err := parseIndex(blob) @@ -379,6 +393,7 @@ func newIndexDeleter(db ethdb.KeyValueReader, state stateIdent) (*indexDeleter, bw: bw, state: state, db: db, + cacher: cacher, }, nil } @@ -436,9 +451,10 @@ func (d *indexDeleter) pop(id uint64) error { // This function is safe to be called multiple times. func (d *indexDeleter) finish(batch ethdb.Batch) { for _, id := range d.dropped { - key := fmt.Sprintf("%s:%d", d.state.String(), id) - if historyBlockCache.Contains(key) { - historyBlockCache.Remove(key) + if d.cacher != nil { + if key := fmt.Sprintf("%s:%d", d.state.String(), id); d.cacher.block.Contains(key) { + d.cacher.block.Remove(key) + } } if d.state.account { rawdb.DeleteAccountHistoryIndexBlock(batch, d.state.addressHash, id) @@ -451,9 +467,10 @@ func (d *indexDeleter) finish(batch ethdb.Batch) { // Flush the content of last block writer, regardless it's dirty or not if !d.bw.empty() { buf := d.bw.finish() - key := fmt.Sprintf("%s:%d", d.state.String(), d.bw.desc.id) - if historyBlockCache.Contains(key) { - historyBlockCache.Add(key, buf) + if d.cacher != nil { + if key := fmt.Sprintf("%s:%d", d.state.String(), d.bw.desc.id); d.cacher.block.Contains(key) { + d.cacher.block.Add(key, buf) + } } if d.state.account { @@ -464,8 +481,10 @@ func (d *indexDeleter) finish(batch ethdb.Batch) { } // Flush the index metadata into the supplied batch if d.empty() { - if key := d.state.String(); historyIndexCache.Contains(key) { - historyIndexCache.Remove(key) + if d.cacher != nil { + if key := d.state.String(); d.cacher.index.Contains(key) { + d.cacher.index.Remove(key) + } } if d.state.account { rawdb.DeleteAccountHistoryIndex(batch, d.state.addressHash) @@ -477,8 +496,11 @@ func (d *indexDeleter) finish(batch ethdb.Batch) { for _, desc := range d.descList { buf = append(buf, desc.encode()...) } - if key := d.state.String(); historyIndexCache.Contains(key) { - historyIndexCache.Add(key, buf) + + if d.cacher != nil { + if key := d.state.String(); d.cacher.index.Contains(key) { + d.cacher.index.Add(key, buf) + } } if d.state.account { diff --git a/triedb/pathdb/history_index_test.go b/triedb/pathdb/history_index_test.go index 7b24b86fd6..e9ba0b8b6c 100644 --- a/triedb/pathdb/history_index_test.go +++ b/triedb/pathdb/history_index_test.go @@ -33,7 +33,7 @@ func TestIndexReaderBasic(t *testing.T) { 1, 5, 10, 11, 20, } db := rawdb.NewMemoryDatabase() - bw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa})) + bw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa}), nil) for i := 0; i < len(elements); i++ { bw.append(elements[i]) } @@ -41,7 +41,7 @@ func TestIndexReaderBasic(t *testing.T) { bw.finish(batch) batch.Write() - br, err := newIndexReader(db, newAccountIdent(common.Hash{0xa})) + br, err := newIndexReader(db, newAccountIdent(common.Hash{0xa}), nil) if err != nil { t.Fatalf("Failed to construct the index reader, %v", err) } @@ -75,7 +75,7 @@ func TestIndexReaderLarge(t *testing.T) { slices.Sort(elements) db := rawdb.NewMemoryDatabase() - bw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa})) + bw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa}), nil) for i := 0; i < len(elements); i++ { bw.append(elements[i]) } @@ -83,7 +83,7 @@ func TestIndexReaderLarge(t *testing.T) { bw.finish(batch) batch.Write() - br, err := newIndexReader(db, newAccountIdent(common.Hash{0xa})) + br, err := newIndexReader(db, newAccountIdent(common.Hash{0xa}), nil) if err != nil { t.Fatalf("Failed to construct the index reader, %v", err) } @@ -107,7 +107,7 @@ func TestIndexReaderLarge(t *testing.T) { } func TestEmptyIndexReader(t *testing.T) { - br, err := newIndexReader(rawdb.NewMemoryDatabase(), newAccountIdent(common.Hash{0xa})) + br, err := newIndexReader(rawdb.NewMemoryDatabase(), newAccountIdent(common.Hash{0xa}), nil) if err != nil { t.Fatalf("Failed to construct the index reader, %v", err) } @@ -122,7 +122,7 @@ func TestEmptyIndexReader(t *testing.T) { func TestIndexWriterBasic(t *testing.T) { db := rawdb.NewMemoryDatabase() - iw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa})) + iw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa}), nil) iw.append(2) if err := iw.append(1); err == nil { t.Fatal("out-of-order insertion is not expected") @@ -134,7 +134,7 @@ func TestIndexWriterBasic(t *testing.T) { iw.finish(batch) batch.Write() - iw, err := newIndexWriter(db, newAccountIdent(common.Hash{0xa})) + iw, err := newIndexWriter(db, newAccountIdent(common.Hash{0xa}), nil) if err != nil { t.Fatalf("Failed to construct the block writer, %v", err) } @@ -148,7 +148,7 @@ func TestIndexWriterBasic(t *testing.T) { func TestIndexWriterDelete(t *testing.T) { db := rawdb.NewMemoryDatabase() - iw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa})) + iw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa}), nil) for i := 0; i < indexBlockEntriesCap*4; i++ { iw.append(uint64(i + 1)) } @@ -157,7 +157,7 @@ func TestIndexWriterDelete(t *testing.T) { batch.Write() // Delete unknown id, the request should be rejected - id, _ := newIndexDeleter(db, newAccountIdent(common.Hash{0xa})) + id, _ := newIndexDeleter(db, newAccountIdent(common.Hash{0xa}), nil) if err := id.pop(indexBlockEntriesCap * 5); err == nil { t.Fatal("Expect error to occur for unknown id") } @@ -179,7 +179,7 @@ func TestIndexWriterDelete(t *testing.T) { func TestBatchIndexerWrite(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - batch = newBatchIndexer(db, false) + batch = newBatchIndexer(db, nil, false) histories = makeHistories(10) ) for i, h := range histories { @@ -212,7 +212,7 @@ func TestBatchIndexerWrite(t *testing.T) { } } for addrHash, indexes := range accounts { - ir, _ := newIndexReader(db, newAccountIdent(addrHash)) + ir, _ := newIndexReader(db, newAccountIdent(addrHash), nil) for i := 0; i < len(indexes)-1; i++ { n, err := ir.readGreaterThan(indexes[i]) if err != nil { @@ -232,7 +232,7 @@ func TestBatchIndexerWrite(t *testing.T) { } for addrHash, slots := range storages { for slotHash, indexes := range slots { - ir, _ := newIndexReader(db, newStorageIdent(addrHash, slotHash)) + ir, _ := newIndexReader(db, newStorageIdent(addrHash, slotHash), nil) for i := 0; i < len(indexes)-1; i++ { n, err := ir.readGreaterThan(indexes[i]) if err != nil { @@ -256,7 +256,7 @@ func TestBatchIndexerWrite(t *testing.T) { func TestBatchIndexerDelete(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - bw = newBatchIndexer(db, false) + bw = newBatchIndexer(db, nil, false) histories = makeHistories(10) ) // Index histories @@ -270,7 +270,7 @@ func TestBatchIndexerDelete(t *testing.T) { } // Unindex histories - bd := newBatchIndexer(db, true) + bd := newBatchIndexer(db, nil, true) for i := len(histories) - 1; i >= 0; i-- { if err := bd.process(histories[i], uint64(i+1)); err != nil { t.Fatalf("Failed to process history, %v", err) diff --git a/triedb/pathdb/history_indexer.go b/triedb/pathdb/history_indexer.go index 054d43e946..08f6190dcb 100644 --- a/triedb/pathdb/history_indexer.go +++ b/triedb/pathdb/history_indexer.go @@ -79,15 +79,17 @@ type batchIndexer struct { delete bool // Index or unindex mode lastID uint64 // The ID of latest processed history db ethdb.KeyValueStore + cacher *historyCacher } // newBatchIndexer constructs the batch indexer with the supplied mode. -func newBatchIndexer(db ethdb.KeyValueStore, delete bool) *batchIndexer { +func newBatchIndexer(db ethdb.KeyValueStore, cacher *historyCacher, delete bool) *batchIndexer { return &batchIndexer{ accounts: make(map[common.Hash][]uint64), storages: make(map[common.Hash]map[common.Hash][]uint64), delete: delete, db: db, + cacher: cacher, } } @@ -139,7 +141,7 @@ func (b *batchIndexer) finish(force bool) error { for addrHash, idList := range b.accounts { eg.Go(func() error { if !b.delete { - iw, err := newIndexWriter(b.db, newAccountIdent(addrHash)) + iw, err := newIndexWriter(b.db, newAccountIdent(addrHash), b.cacher) if err != nil { return err } @@ -152,7 +154,7 @@ func (b *batchIndexer) finish(force bool) error { iw.finish(batch) batchMu.Unlock() } else { - id, err := newIndexDeleter(b.db, newAccountIdent(addrHash)) + id, err := newIndexDeleter(b.db, newAccountIdent(addrHash), b.cacher) if err != nil { return err } @@ -173,7 +175,7 @@ func (b *batchIndexer) finish(force bool) error { for storageHash, idList := range slots { eg.Go(func() error { if !b.delete { - iw, err := newIndexWriter(b.db, newStorageIdent(addrHash, storageHash)) + iw, err := newIndexWriter(b.db, newStorageIdent(addrHash, storageHash), b.cacher) if err != nil { return err } @@ -186,7 +188,7 @@ func (b *batchIndexer) finish(force bool) error { iw.finish(batch) batchMu.Unlock() } else { - id, err := newIndexDeleter(b.db, newStorageIdent(addrHash, storageHash)) + id, err := newIndexDeleter(b.db, newStorageIdent(addrHash, storageHash), b.cacher) if err != nil { return err } @@ -227,7 +229,7 @@ func (b *batchIndexer) finish(force bool) error { } // indexSingle processes the state history with the specified ID for indexing. -func indexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.AncientReader) error { +func indexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.AncientReader, cacher *historyCacher) error { start := time.Now() defer func() { indexHistoryTimer.UpdateSince(start) @@ -245,7 +247,7 @@ func indexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.Ancient if err != nil { return err } - b := newBatchIndexer(db, false) + b := newBatchIndexer(db, cacher, false) if err := b.process(h, historyID); err != nil { return err } @@ -257,7 +259,7 @@ func indexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.Ancient } // unindexSingle processes the state history with the specified ID for unindexing. -func unindexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.AncientReader) error { +func unindexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.AncientReader, cacher *historyCacher) error { start := time.Now() defer func() { unindexHistoryTimer.UpdateSince(start) @@ -275,7 +277,7 @@ func unindexSingle(historyID uint64, db ethdb.KeyValueStore, freezer ethdb.Ancie if err != nil { return err } - b := newBatchIndexer(db, true) + b := newBatchIndexer(db, cacher, true) if err := b.process(h, historyID); err != nil { return err } @@ -302,6 +304,7 @@ type interruptSignal struct { type indexIniter struct { disk ethdb.KeyValueStore freezer ethdb.AncientStore + cacher *historyCacher interrupt chan *interruptSignal done chan struct{} closed chan struct{} @@ -416,7 +419,7 @@ func (i *indexIniter) run(lastID uint64) { // been fully indexed, unindex it here and shut down the initializer. if checkDone() { log.Info("Truncate the extra history", "id", lastID) - if err := unindexSingle(lastID, i.disk, i.freezer); err != nil { + if err := unindexSingle(lastID, i.disk, i.freezer, i.cacher); err != nil { signal.result <- err return } @@ -517,7 +520,7 @@ func (i *indexIniter) index(done chan struct{}, interrupt *atomic.Int32, lastID current = beginID start = time.Now() logged = time.Now() - batch = newBatchIndexer(i.disk, false) + batch = newBatchIndexer(i.disk, i.cacher, false) ) for current <= lastID { count := lastID - current + 1 @@ -585,6 +588,7 @@ type historyIndexer struct { initer *indexIniter disk ethdb.KeyValueStore freezer ethdb.AncientStore + cacher *historyCacher } // checkVersion checks whether the index data in the database matches the version. @@ -611,12 +615,13 @@ func checkVersion(disk ethdb.KeyValueStore) { // newHistoryIndexer constructs the history indexer and launches the background // 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, cacher *historyCacher) *historyIndexer { checkVersion(disk) return &historyIndexer{ initer: newIndexIniter(disk, freezer, lastHistoryID), disk: disk, freezer: freezer, + cacher: cacher, } } @@ -642,7 +647,7 @@ func (i *historyIndexer) extend(historyID uint64) error { case <-i.initer.closed: return errors.New("indexer is closed") case <-i.initer.done: - return indexSingle(historyID, i.disk, i.freezer) + return indexSingle(historyID, i.disk, i.freezer, i.cacher) case i.initer.interrupt <- signal: return <-signal.result } @@ -659,7 +664,7 @@ func (i *historyIndexer) shorten(historyID uint64) error { case <-i.initer.closed: return errors.New("indexer is closed") case <-i.initer.done: - return unindexSingle(historyID, i.disk, i.freezer) + return unindexSingle(historyID, i.disk, i.freezer, i.cacher) case i.initer.interrupt <- signal: return <-signal.result } diff --git a/triedb/pathdb/history_reader.go b/triedb/pathdb/history_reader.go index 1c0f82e8b2..daa98dee1b 100644 --- a/triedb/pathdb/history_reader.go +++ b/triedb/pathdb/history_reader.go @@ -32,11 +32,6 @@ import ( "github.com/ethereum/go-ethereum/log" ) -var ( - historyIndexCache = lru.NewCache[string, []byte](10000) - historyBlockCache = lru.NewCache[string, []byte](10000) -) - // stateIdent represents the identifier of a state element, which can be // either an account or a storage slot. type stateIdent struct { @@ -131,8 +126,8 @@ type indexReaderWithLimitTag struct { // newIndexReaderWithLimitTag constructs a index reader with indexing position. // Add timings parameter to pass through -func newIndexReaderWithLimitTag(db ethdb.KeyValueReader, state stateIdent, limit uint64, timings *readTimings) (*indexReaderWithLimitTag, error) { - r, err := newIndexReaderWithTimings(db, state, timings) +func newIndexReaderWithLimitTag(db ethdb.KeyValueReader, state stateIdent, limit uint64, timings *readTimings, cacher *historyCacher) (*indexReaderWithLimitTag, error) { + r, err := newIndexReaderWithTimings(db, state, timings, cacher) if err != nil { return nil, err } @@ -190,19 +185,35 @@ func (r *indexReaderWithLimitTag) readGreaterThan(id uint64, lastID uint64) (uin return r.reader.readGreaterThan(id) } +// historyCacher is responsible for caching history objects in memory to speed up access. +type historyCacher struct { + index *lru.Cache[string, []byte] + block *lru.Cache[string, []byte] +} + +// newHistoryCacher creates a new historyCacher instance. +func newHistoryCacher(size int) *historyCacher { + return &historyCacher{ + index: lru.NewCache[string, []byte](size), + block: lru.NewCache[string, []byte](size), + } +} + // historyReader is the structure to access historic state data. type historyReader struct { disk ethdb.KeyValueReader freezer ethdb.AncientReader readers map[string]*indexReaderWithLimitTag + cacher *historyCacher } -// newHistoryReader constructs the history reader with the supplied db. -func newHistoryReader(disk ethdb.KeyValueReader, freezer ethdb.AncientReader) *historyReader { +// newHistoryReader constructs the history reader with the supplied db and cacher. +func newHistoryReader(disk ethdb.KeyValueReader, freezer ethdb.AncientReader, cacher *historyCacher) *historyReader { return &historyReader{ disk: disk, freezer: freezer, readers: make(map[string]*indexReaderWithLimitTag), + cacher: cacher, } } @@ -386,7 +397,7 @@ func (r *historyReader) read(state stateIdentQuery, stateID uint64, lastID uint6 // state retrieval ir, ok := r.readers[state.String()] if !ok { - ir, err = newIndexReaderWithLimitTag(r.disk, state.stateIdent, metadata.Last, timings) + ir, err = newIndexReaderWithLimitTag(r.disk, state.stateIdent, metadata.Last, timings, r.cacher) if err != nil { return nil, err } diff --git a/triedb/pathdb/history_reader_test.go b/triedb/pathdb/history_reader_test.go index 4eb93fb9c9..fc1d82d791 100644 --- a/triedb/pathdb/history_reader_test.go +++ b/triedb/pathdb/history_reader_test.go @@ -133,7 +133,7 @@ func testHistoryReader(t *testing.T, historyLimit uint64) { var ( roots = env.roots dRoot = env.db.tree.bottom().rootHash() - hr = newHistoryReader(env.db.diskdb, env.db.freezer) + hr = newHistoryReader(env.db.diskdb, env.db.freezer, env.db.cacher) ) for _, root := range roots { if root == dRoot { diff --git a/triedb/pathdb/reader.go b/triedb/pathdb/reader.go index b7b18f13f9..09a6ad4b1d 100644 --- a/triedb/pathdb/reader.go +++ b/triedb/pathdb/reader.go @@ -230,7 +230,7 @@ func (db *Database) HistoricReader(root common.Hash) (*HistoricalStateReader, er return &HistoricalStateReader{ id: *id, db: db, - reader: newHistoryReader(db.diskdb, db.freezer), + reader: newHistoryReader(db.diskdb, db.freezer, db.cacher), }, nil }