Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
13 changes: 8 additions & 5 deletions blockchain/aggregated_bloom_filter_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ 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"
lru "github.com/hashicorp/golang-lru/v2"
)

// NOTE(Ege): consider making it configurable
Expand All @@ -27,15 +28,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 *lru.Cache[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{
Comment on lines 36 to 37
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is out of scope, but since we are touching it, could NewAggregatedBloomCache return *AggregatedBloomFilterCache?

Its methods are all pointer-receivers, so a pointer is more consistent and avoids copies diverging on fallbackFunc in future uses.

Also, it's only called in blockchain.go and tests today, so the change stays small.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its methods are all pointer-receivers, so a pointer is more consistent

This doesn't make sense to me, can you explain what you mean? How having pointer receivers affects what the constructor should return?

avoids copies diverging on fallbackFunc in future uses.

This is a valid one.

could NewAggregatedBloomCache return *AggregatedBloomFilterCache?

Out of scope changes should be address in a different PR, let's try not squeeze them here

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
Comment thread
brbrr marked this conversation as resolved.
Outdated
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()
Comment thread
rodrodros marked this conversation as resolved.
Outdated
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
6 changes: 4 additions & 2 deletions rpc/v10/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ 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"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/sourcegraph/conc"
)

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

return &Handler{
bcReader: bcReader,
syncReader: syncReader,
Expand All @@ -95,7 +97,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
8 changes: 6 additions & 2 deletions rpc/v8/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ 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"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/sourcegraph/conc"
)

Expand Down Expand Up @@ -89,7 +90,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
5 changes: 3 additions & 2 deletions rpc/v9/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ 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"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/sourcegraph/conc"
)

Expand Down Expand Up @@ -94,7 +95,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
9 changes: 5 additions & 4 deletions sync/pending_polling.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ 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"
"github.com/hashicorp/golang-lru/v2/simplelru"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -130,7 +131,7 @@ func (s *Synchronizer) storeEmptyPreConfirmed(
func (s *Synchronizer) handleTickerPreLatest(
ctx context.Context,
currentHead *core.Block,
seenByParent *lru.BasicLRU[felt.Felt, *pending.PreLatest],
seenByParent *simplelru.LRU[felt.Felt, *pending.PreLatest],
out chan<- *pending.PreLatest,
) bool {
preLatest, err := s.dataSource.BlockPreLatest(ctx)
Expand Down Expand Up @@ -168,7 +169,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 +220,7 @@ func (s *Synchronizer) pollPreLatest(ctx context.Context, out chan<- *pending.Pr
deliveredForHead = s.handleTickerPreLatest(
ctx,
currentHead,
&seenByParent,
seenByParent,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is great that you made NewSimpleLRU to return a ptr.
This way it gets harder in the future to accidentally have two copies of the same LRUcache.

out,
)
}
Expand Down
30 changes: 30 additions & 0 deletions utils/lru.go
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking it is better to create a package inside utils/lru/lru.go so that imports are called lru by default. We would wrap whatever implementation we used from there

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this comment was marked as resolved without being resolved, why?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was updated in 3ad6be2. Let me know if that's not enough, or it's not what you meant.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a git error, is not showing up in the diff

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brbrr to clarify, we should have a utils/lru/lru.go, but what I see is utils/lru.go? Is git or the code wrong

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package utils

import (
"fmt"

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

// 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.Cache[K, V] {
c, err := lru.New[K, V](size)
if err != nil {
panic(fmt.Errorf("lru: %w (size=%d)", err, size))
}
return c
}

// NewSimpleLRU returns a new non-thread-safe LRU cache or panics if size <= 0.
// Use the same way as NewLRU when external synchronization is provided
// (e.g. single-goroutine ownership).
func NewSimpleLRU[K comparable, V any](size int) *simplelru.LRU[K, V] {
c, err := simplelru.NewLRU[K, V](size, nil)
if err != nil {
panic(fmt.Errorf("simplelru: %w (size=%d)", err, size))
}
return c
Comment thread
brbrr marked this conversation as resolved.
Outdated
}
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