Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions blockchain/aggregated_bloom_filter_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"fmt"

"github.com/NethermindEth/juno/core"
"github.com/NethermindEth/juno/utils"
"github.com/bits-and-blooms/bitset"
"github.com/ethereum/go-ethereum/common/lru"
)

// NOTE(Ege): consider making it configurable
Expand All @@ -27,15 +27,17 @@ type EventFiltersCacheKey struct {
// for block ranges, supporting fallback loading and bulk insertion.
// It is safe for concurrent use.
type AggregatedBloomFilterCache struct {
cache lru.Cache[EventFiltersCacheKey, *core.AggregatedBloomFilter]
cache *utils.LRU[EventFiltersCacheKey, *core.AggregatedBloomFilter]
fallbackFunc func(EventFiltersCacheKey) (core.AggregatedBloomFilter, error)
}

// NewAggregatedBloomCache creates a new LRU cache for aggregated bloom filters
// with the specified maximum size (number of ranges to cache).
func NewAggregatedBloomCache(size int) AggregatedBloomFilterCache {
func NewAggregatedBloomCache() AggregatedBloomFilterCache {
return AggregatedBloomFilterCache{
cache: *lru.NewCache[EventFiltersCacheKey, *core.AggregatedBloomFilter](size),
cache: utils.NewLRU[
EventFiltersCacheKey,
*core.AggregatedBloomFilter,
](AggregatedBloomFilterCacheSize),
}
Comment thread
brbrr marked this conversation as resolved.
}

Expand Down
8 changes: 4 additions & 4 deletions blockchain/aggregated_bloom_filter_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func populateAggregatedBloomDeterministic(

func TestMatchBlockIterator_InsertAndQueryRandomEvents(t *testing.T) {
numEvents := 64
numAggregatedBloomFilters := uint64(16)
var numAggregatedBloomFilters uint64 = blockchain.AggregatedBloomFilterCacheSize
blocksPerFilter := core.NumBlocksPerFilter
chainHeight := numAggregatedBloomFilters*blocksPerFilter - 1

Expand All @@ -159,7 +159,7 @@ func TestMatchBlockIterator_InsertAndQueryRandomEvents(t *testing.T) {

testDB := memory.New()
// Create cache and insert filters
cache := blockchain.NewAggregatedBloomCache(int(numAggregatedBloomFilters))
cache := blockchain.NewAggregatedBloomCache()
cache.SetMany(filters)
runningFilterStart := numAggregatedBloomFilters * blocksPerFilter
innerFilter := core.NewAggregatedFilter(runningFilterStart)
Expand Down Expand Up @@ -188,15 +188,15 @@ func TestMatchBlockIterator_InsertAndQueryRandomEvents(t *testing.T) {
}

func TestMatchedBlockIterator_BasicCases(t *testing.T) {
var numAggregatedBloomFilters uint64 = 16
var numAggregatedBloomFilters uint64 = blockchain.AggregatedBloomFilterCacheSize
chainHeight := numAggregatedBloomFilters*core.NumBlocksPerFilter - 1

events := generateRandomEvents(t, 1, 3, 1)
test := events[0]
emmitedEvery := 4
filters := populateAggregatedBloomDeterministic(t, numAggregatedBloomFilters, test, core.NumBlocksPerFilter, uint64(emmitedEvery))

cache := blockchain.NewAggregatedBloomCache(int(numAggregatedBloomFilters))
cache := blockchain.NewAggregatedBloomCache()
cache.SetMany(filters)

testDB := memory.New()
Expand Down
2 changes: 1 addition & 1 deletion blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func New(database db.KeyValueStore, network *networks.Network, opts ...Option) *
opt(&o)
}

cachedFilters := NewAggregatedBloomCache(AggregatedBloomFilterCacheSize)
cachedFilters := NewAggregatedBloomCache()
fallback := func(key EventFiltersCacheKey) (core.AggregatedBloomFilter, error) {
return core.GetAggregatedBloomFilter(database, key.fromBlock, key.toBlock)
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/ethereum/go-ethereum v1.17.3
github.com/fxamacker/cbor/v2 v2.9.2
github.com/go-playground/validator/v10 v10.30.2
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/libp2p/go-libp2p v0.48.0
github.com/libp2p/go-libp2p-kad-dht v0.39.1
github.com/libp2p/go-libp2p-pubsub v0.16.0
Expand Down Expand Up @@ -84,7 +85,6 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand Down
7 changes: 4 additions & 3 deletions rpc/v10/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import (
"github.com/NethermindEth/juno/rpc/rpccore"
"github.com/NethermindEth/juno/starknet/compiler"
"github.com/NethermindEth/juno/sync"
"github.com/NethermindEth/juno/utils"
"github.com/NethermindEth/juno/utils/log"
"github.com/NethermindEth/juno/vm"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/sourcegraph/conc"
)

Expand All @@ -48,7 +48,7 @@ type Handler struct {

// todo(rdr): why do we have the `TraceCacheKey` type and why it feels uncomfortable
// to use. It makes no sense, why not use `Felt` or `Hash` directly?
blockTraceCache *lru.Cache[rpccore.TraceCacheKey, TraceBlockTransactionsResponse]
blockTraceCache *utils.LRU[rpccore.TraceCacheKey, TraceBlockTransactionsResponse]
// todo(rdr): Can this cache be genericified and can it be applied to the `blockTraceCache`
submittedTransactionsCache *rpccore.TransactionCache

Expand Down Expand Up @@ -78,6 +78,7 @@ func New(
if err != nil {
logger.Fatalf("Failed to parse ABI: %v", err)
}

return &Handler{
bcReader: bcReader,
syncReader: syncReader,
Expand All @@ -95,7 +96,7 @@ func New(
l1Heads: feed.New[*core.L1Head](),
preLatestFeed: feed.New[*pending.PreLatest](),

blockTraceCache: lru.NewCache[
blockTraceCache: utils.NewLRU[
rpccore.TraceCacheKey,
TraceBlockTransactionsResponse,
](rpccore.TraceCacheSize),
Expand Down
9 changes: 6 additions & 3 deletions rpc/v8/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import (
"github.com/NethermindEth/juno/rpc/rpccore"
"github.com/NethermindEth/juno/starknet/compiler"
"github.com/NethermindEth/juno/sync"
"github.com/NethermindEth/juno/utils"
"github.com/NethermindEth/juno/utils/log"
"github.com/NethermindEth/juno/vm"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/sourcegraph/conc"
)

Expand All @@ -46,7 +46,7 @@ type Handler struct {
idgen func() string
subscriptions stdsync.Map // map[string]*subscription

blockTraceCache *lru.Cache[rpccore.TraceCacheKey, []TracedBlockTransaction]
blockTraceCache *utils.LRU[rpccore.TraceCacheKey, []TracedBlockTransaction]
submittedTransactionsCache *rpccore.TransactionCache

filterLimit uint
Expand Down Expand Up @@ -89,7 +89,10 @@ func New(
preConfirmedFeed: feed.New[*pendingpkg.PreConfirmed](),
l1Heads: feed.New[*core.L1Head](),

blockTraceCache: lru.NewCache[rpccore.TraceCacheKey, []TracedBlockTransaction](rpccore.TraceCacheSize),
blockTraceCache: utils.NewLRU[
rpccore.TraceCacheKey,
[]TracedBlockTransaction,
](rpccore.TraceCacheSize),
filterLimit: math.MaxUint,
coreContractABI: contractABI,
}
Expand Down
6 changes: 3 additions & 3 deletions rpc/v9/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import (
"github.com/NethermindEth/juno/rpc/rpccore"
"github.com/NethermindEth/juno/starknet/compiler"
"github.com/NethermindEth/juno/sync"
"github.com/NethermindEth/juno/utils"
"github.com/NethermindEth/juno/utils/log"
"github.com/NethermindEth/juno/vm"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/sourcegraph/conc"
)

Expand All @@ -49,7 +49,7 @@ type Handler struct {

// todo(rdr): why do we have the `TraceCacheKey` type and why it feels uncomfortable
// to use. It makes no sense, why not use `Felt` or `Hash` directly?
blockTraceCache *lru.Cache[rpccore.TraceCacheKey, []TracedBlockTransaction]
blockTraceCache *utils.LRU[rpccore.TraceCacheKey, []TracedBlockTransaction]
// todo(rdr): Can this cache be genericified and can it be applied to the `blockTraceCache`
submittedTransactionsCache *rpccore.TransactionCache

Expand Down Expand Up @@ -94,7 +94,7 @@ func New(
l1Heads: feed.New[*core.L1Head](),
preLatestFeed: feed.New[*pending.PreLatest](),

blockTraceCache: lru.NewCache[
blockTraceCache: utils.NewLRU[
rpccore.TraceCacheKey,
[]TracedBlockTransaction,
](rpccore.TraceCacheSize),
Expand Down
8 changes: 4 additions & 4 deletions sync/pending_polling.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/juno/core/pending"
"github.com/NethermindEth/juno/db"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/NethermindEth/juno/utils"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -130,7 +130,7 @@ func (s *Synchronizer) storeEmptyPreConfirmed(
func (s *Synchronizer) handleTickerPreLatest(
ctx context.Context,
currentHead *core.Block,
seenByParent *lru.BasicLRU[felt.Felt, *pending.PreLatest],
seenByParent *utils.SimpleLRU[felt.Felt, *pending.PreLatest],
out chan<- *pending.PreLatest,
) bool {
preLatest, err := s.dataSource.BlockPreLatest(ctx)
Expand Down Expand Up @@ -168,7 +168,7 @@ func (s *Synchronizer) pollPreLatest(ctx context.Context, out chan<- *pending.Pr

// Cache of pre-latest blocks keyed by the hash of their parent.
// When we receive the head with this parent hash, we emit the cached pre-latest.
seenByParent := lru.NewBasicLRU[felt.Felt, *pending.PreLatest](preLatestCacheSize)
seenByParent := utils.NewSimpleLRU[felt.Felt, *pending.PreLatest](preLatestCacheSize)

ticker := time.NewTicker(s.preLatestPollInterval)
defer ticker.Stop()
Expand Down Expand Up @@ -219,7 +219,7 @@ func (s *Synchronizer) pollPreLatest(ctx context.Context, out chan<- *pending.Pr
deliveredForHead = s.handleTickerPreLatest(
ctx,
currentHead,
&seenByParent,
seenByParent,
out,
)
}
Expand Down
51 changes: 51 additions & 0 deletions utils/lru.go
Comment thread
brbrr marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package utils

import (
"fmt"

lru "github.com/hashicorp/golang-lru/v2"
"github.com/hashicorp/golang-lru/v2/simplelru"
)

// LRU is a thread-safe size-bounded LRU cache.
type LRU[K comparable, V any] struct {
c *lru.Cache[K, V]
}

// NewLRU returns a new thread-safe LRU cache or panics if size <= 0.
// Use for caches sized by constants or validated config, where a zero size
// indicates a programmer error rather than a runtime condition.
func NewLRU[K comparable, V any](size int) *LRU[K, V] {
c, err := lru.New[K, V](size)
if err != nil {
panic(fmt.Errorf("lru: %w (size=%d)", err, size))
}
return &LRU[K, V]{c: c}
}

func (l *LRU[K, V]) Add(key K, value V) (evicted bool) { return l.c.Add(key, value) }
func (l *LRU[K, V]) Get(key K) (V, bool) { return l.c.Get(key) }
func (l *LRU[K, V]) Remove(key K) (present bool) { return l.c.Remove(key) }
func (l *LRU[K, V]) Purge() { l.c.Purge() }

Check warning on line 29 in utils/lru.go

View check run for this annotation

Codecov / codecov/patch

utils/lru.go#L28-L29

Added lines #L28 - L29 were not covered by tests
func (l *LRU[K, V]) Len() int { return l.c.Len() }

// SimpleLRU is a non-thread-safe size-bounded LRU cache.
// Use when external synchronization is provided (e.g. single-goroutine ownership).
type SimpleLRU[K comparable, V any] struct {
c *simplelru.LRU[K, V]
}

// NewSimpleLRU returns a new non-thread-safe LRU cache or panics if size <= 0.
func NewSimpleLRU[K comparable, V any](size int) *SimpleLRU[K, V] {
c, err := simplelru.NewLRU[K, V](size, nil)
if err != nil {
panic(fmt.Errorf("simplelru: %w (size=%d)", err, size))
}
return &SimpleLRU[K, V]{c: c}
}

func (l *SimpleLRU[K, V]) Add(key K, value V) (evicted bool) { return l.c.Add(key, value) }
func (l *SimpleLRU[K, V]) Get(key K) (V, bool) { return l.c.Get(key) }
func (l *SimpleLRU[K, V]) Remove(key K) (present bool) { return l.c.Remove(key) }
func (l *SimpleLRU[K, V]) Purge() { l.c.Purge() }

Check warning on line 50 in utils/lru.go

View check run for this annotation

Codecov / codecov/patch

utils/lru.go#L50

Added line #L50 was not covered by tests
func (l *SimpleLRU[K, V]) Len() int { return l.c.Len() }
66 changes: 66 additions & 0 deletions utils/lru_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package utils

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

//nolint:dupl // duplicate tests as there's identical APIs
func TestNewLRU(t *testing.T) {
t.Run("returns usable cache for positive size", func(t *testing.T) {
c := NewLRU[string, int](2)
require.NotNil(t, c)
assert.Equal(t, 0, c.Len())

c.Add("a", 1)
c.Add("b", 2)
assert.Equal(t, 2, c.Len())

v, ok := c.Get("a")
assert.True(t, ok)
assert.Equal(t, 1, v)
})

t.Run("panics on zero size", func(t *testing.T) {
assert.PanicsWithError(t, "lru: must provide a positive size (size=0)", func() {
NewLRU[string, int](0)
})
})

t.Run("panics on negative size", func(t *testing.T) {
assert.PanicsWithError(t, "lru: must provide a positive size (size=-1)", func() {
NewLRU[string, int](-1)
})
})
}

//nolint:dupl // duplicate tests as there's identical APIs
func TestNewSimpleLRU(t *testing.T) {
t.Run("returns usable cache for positive size", func(t *testing.T) {
c := NewSimpleLRU[string, int](2)
require.NotNil(t, c)
assert.Equal(t, 0, c.Len())

c.Add("a", 1)
c.Add("b", 2)
assert.Equal(t, 2, c.Len())

v, ok := c.Get("a")
assert.True(t, ok)
assert.Equal(t, 1, v)
})

t.Run("panics on zero size", func(t *testing.T) {
assert.PanicsWithError(t, "simplelru: must provide a positive size (size=0)", func() {
NewSimpleLRU[string, int](0)
})
})

t.Run("panics on negative size", func(t *testing.T) {
assert.PanicsWithError(t, "simplelru: must provide a positive size (size=-1)", func() {
NewSimpleLRU[string, int](-1)
})
})
}
Loading