Skip to content

Commit

Permalink
Fix intermediate node caching (#2585)
Browse files Browse the repository at this point in the history
Co-authored-by: Dan Laine <[email protected]>
  • Loading branch information
dboehm-avalabs and Dan Laine authored Jan 4, 2024
1 parent 72dc442 commit 7c82a5b
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 36 deletions.
29 changes: 14 additions & 15 deletions x/merkledb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,23 +259,22 @@ func newDatabase(
return make([]byte, 0, defaultBufferLength)
},
}
iNodeDB, err := newIntermediateNodeDB(
db,
bufferPool,
metrics,
int(config.IntermediateNodeCacheSize),
int(config.IntermediateWriteBufferSize),
int(config.IntermediateWriteBatchSize),
BranchFactorToTokenSize[config.BranchFactor])
if err != nil {
return nil, err
}

trieDB := &merkleDB{
metrics: metrics,
baseDB: db,
intermediateNodeDB: iNodeDB,
valueNodeDB: newValueNodeDB(db, bufferPool, metrics, int(config.ValueNodeCacheSize)),
metrics: metrics,
baseDB: db,
intermediateNodeDB: newIntermediateNodeDB(
db,
bufferPool,
metrics,
int(config.IntermediateNodeCacheSize),
int(config.IntermediateWriteBufferSize),
int(config.IntermediateWriteBatchSize),
BranchFactorToTokenSize[config.BranchFactor]),
valueNodeDB: newValueNodeDB(db,
bufferPool,
metrics,
int(config.ValueNodeCacheSize)),
history: newTrieHistory(int(config.HistoryLength)),
debugTracer: getTracerIfEnabled(config.TraceLevel, DebugTrace, config.Tracer),
infoTracer: getTracerIfEnabled(config.TraceLevel, InfoTrace, config.Tracer),
Expand Down
19 changes: 11 additions & 8 deletions x/merkledb/intermediate_node_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package merkledb

import (
"errors"
"sync"

"github.com/ava-labs/avalanchego/cache"
Expand All @@ -14,8 +13,6 @@ import (

const defaultBufferLength = 256

var errCacheSizeTooSmall = errors.New("cache size must be larger than or equal to write buffer size")

// Holds intermediate nodes. That is, those without values.
// Changes to this database aren't written to [baseDB] until
// they're evicted from the [nodeCache] or Flush is called.
Expand Down Expand Up @@ -51,10 +48,7 @@ func newIntermediateNodeDB(
writeBufferSize int,
evictionBatchSize int,
tokenSize int,
) (*intermediateNodeDB, error) {
if cacheSize < writeBufferSize {
return nil, errCacheSizeTooSmall
}
) *intermediateNodeDB {
result := &intermediateNodeDB{
metrics: metrics,
baseDB: db,
Expand All @@ -69,7 +63,7 @@ func newIntermediateNodeDB(
result.onEviction,
)

return result, nil
return result
}

// A non-nil error is considered fatal and closes [db.baseDB].
Expand Down Expand Up @@ -125,6 +119,15 @@ func (db *intermediateNodeDB) Get(key Key) (*node, error) {
}
db.metrics.IntermediateNodeCacheMiss()

if cachedValue, isCached := db.writeBuffer.Get(key); isCached {
db.metrics.IntermediateNodeCacheHit()
if cachedValue == nil {
return nil, database.ErrNotFound
}
return cachedValue, nil
}
db.metrics.IntermediateNodeCacheMiss()

dbKey := db.constructDBKey(key)
db.metrics.DatabaseNodeRead()
nodeBytes, err := db.baseDB.Get(dbKey)
Expand Down
18 changes: 5 additions & 13 deletions x/merkledb/intermediate_node_db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func Test_IntermediateNodeDB(t *testing.T) {

evictionBatchSize := bufferSize
baseDB := memdb.New()
db, err := newIntermediateNodeDB(
db := newIntermediateNodeDB(
baseDB,
&sync.Pool{
New: func() interface{} { return make([]byte, 0) },
Expand All @@ -44,7 +44,6 @@ func Test_IntermediateNodeDB(t *testing.T) {
evictionBatchSize,
4,
)
require.NoError(err)

// Put a key-node pair
node1Key := ToKey([]byte{0x01})
Expand Down Expand Up @@ -148,7 +147,7 @@ func FuzzIntermediateNodeDBConstructDBKey(f *testing.F) {
) {
require := require.New(t)
for _, tokenSize := range validTokenSizes {
db, err := newIntermediateNodeDB(
db := newIntermediateNodeDB(
baseDB,
&sync.Pool{
New: func() interface{} { return make([]byte, 0) },
Expand All @@ -159,7 +158,6 @@ func FuzzIntermediateNodeDBConstructDBKey(f *testing.F) {
evictionBatchSize,
tokenSize,
)
require.NoError(err)

p := ToKey(key)
uBitLength := tokenLength * uint(tokenSize)
Expand Down Expand Up @@ -192,7 +190,7 @@ func Test_IntermediateNodeDB_ConstructDBKey_DirtyBuffer(t *testing.T) {
bufferSize := 200
evictionBatchSize := bufferSize
baseDB := memdb.New()
db, err := newIntermediateNodeDB(
db := newIntermediateNodeDB(
baseDB,
&sync.Pool{
New: func() interface{} { return make([]byte, 0) },
Expand All @@ -204,8 +202,6 @@ func Test_IntermediateNodeDB_ConstructDBKey_DirtyBuffer(t *testing.T) {
4,
)

require.NoError(err)

db.bufferPool.Put([]byte{0xFF, 0xFF, 0xFF})
constructedKey := db.constructDBKey(ToKey([]byte{}))
require.Len(constructedKey, 2)
Expand All @@ -231,7 +227,7 @@ func TestIntermediateNodeDBClear(t *testing.T) {
bufferSize := 200
evictionBatchSize := bufferSize
baseDB := memdb.New()
db, err := newIntermediateNodeDB(
db := newIntermediateNodeDB(
baseDB,
&sync.Pool{
New: func() interface{} { return make([]byte, 0) },
Expand All @@ -243,8 +239,6 @@ func TestIntermediateNodeDBClear(t *testing.T) {
4,
)

require.NoError(err)

for _, b := range [][]byte{{1}, {2}, {3}} {
require.NoError(db.Put(ToKey(b), newNode(ToKey(b))))
}
Expand All @@ -269,7 +263,7 @@ func TestIntermediateNodeDBDeleteEmptyKey(t *testing.T) {
bufferSize := 200
evictionBatchSize := bufferSize
baseDB := memdb.New()
db, err := newIntermediateNodeDB(
db := newIntermediateNodeDB(
baseDB,
&sync.Pool{
New: func() interface{} { return make([]byte, 0) },
Expand All @@ -281,8 +275,6 @@ func TestIntermediateNodeDBDeleteEmptyKey(t *testing.T) {
4,
)

require.NoError(err)

emptyKey := ToKey([]byte{})
require.NoError(db.Put(emptyKey, newNode(emptyKey)))
require.NoError(db.Flush())
Expand Down

0 comments on commit 7c82a5b

Please sign in to comment.