Skip to content

Commit 153e97b

Browse files
Limit the number of buckets in verifiedTransactionCache to 3. (#2775)
The number of buckets in verifiedTransactionCache can be arbitrarily large depending on the requested cache size, and each lookup in the cache does that many map lookups. This PR limits the number of buckets to 3. Now, the lookup takes at most 3 lookups and the size overhead is 1.5.
1 parent b302a21 commit 153e97b

File tree

2 files changed

+16
-15
lines changed

2 files changed

+16
-15
lines changed

data/transactions/verify/verifiedTxnCache.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"github.com/algorand/go-algorand/protocol"
2727
)
2828

29-
const entriesPerBucket = 8179 // the default bucket size; a prime number could promote a lower hash collisions in case the hash function isn't perfect.
3029
const maxPinnedEntries = 500000
3130

3231
// VerifiedTxnCacheError helps to identify the errors of a cache error and diffrenciate these from a general verification errors.
@@ -72,6 +71,8 @@ type VerifiedTransactionCache interface {
7271

7372
// verifiedTransactionCache provides an implementation of the VerifiedTransactionCache interface
7473
type verifiedTransactionCache struct {
74+
// Number of entries in each map (bucket).
75+
entriesPerBucket int
7576
// bucketsLock is the lock for syncornizing the access to the cache
7677
bucketsLock deadlock.Mutex
7778
// buckets is the circular cache buckets buffer
@@ -84,14 +85,14 @@ type verifiedTransactionCache struct {
8485

8586
// MakeVerifiedTransactionCache creates an instance of verifiedTransactionCache and returns it.
8687
func MakeVerifiedTransactionCache(cacheSize int) VerifiedTransactionCache {
87-
bucketsCount := 1 + (cacheSize / entriesPerBucket)
8888
impl := &verifiedTransactionCache{
89-
buckets: make([]map[transactions.Txid]*GroupContext, bucketsCount),
90-
pinned: make(map[transactions.Txid]*GroupContext, cacheSize),
91-
base: 0,
89+
entriesPerBucket: (cacheSize + 1) / 2,
90+
buckets: make([]map[transactions.Txid]*GroupContext, 3),
91+
pinned: make(map[transactions.Txid]*GroupContext, cacheSize),
92+
base: 0,
9293
}
93-
for i := 0; i < bucketsCount; i++ {
94-
impl.buckets[i] = make(map[transactions.Txid]*GroupContext, entriesPerBucket)
94+
for i := 0; i < len(impl.buckets); i++ {
95+
impl.buckets[i] = make(map[transactions.Txid]*GroupContext, impl.entriesPerBucket)
9596
}
9697
return impl
9798
}
@@ -245,10 +246,10 @@ func (v *verifiedTransactionCache) Pin(txgroup []transactions.SignedTxn) (err er
245246

246247
// add is the internal implementation of Add/AddPayset which adds a transaction group to the buffer.
247248
func (v *verifiedTransactionCache) add(txgroup []transactions.SignedTxn, groupCtx *GroupContext) {
248-
if len(v.buckets[v.base])+len(txgroup) > entriesPerBucket {
249+
if len(v.buckets[v.base])+len(txgroup) > v.entriesPerBucket {
249250
// move to the next bucket while deleting the content of the next bucket.
250251
v.base = (v.base + 1) % len(v.buckets)
251-
v.buckets[v.base] = make(map[transactions.Txid]*GroupContext, entriesPerBucket)
252+
v.buckets[v.base] = make(map[transactions.Txid]*GroupContext, v.entriesPerBucket)
252253
}
253254
currentBucket := v.buckets[v.base]
254255
for _, txn := range txgroup {

data/transactions/verify/verifiedTxnCache_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ func TestBucketCycling(t *testing.T) {
4949
partitiontest.PartitionTest(t)
5050

5151
bucketCount := 3
52-
icache := MakeVerifiedTransactionCache(entriesPerBucket * bucketCount)
52+
entriesPerBucket := 100
53+
icache := MakeVerifiedTransactionCache(entriesPerBucket * (bucketCount - 1))
5354
impl := icache.(*verifiedTransactionCache)
5455
_, signedTxn, _, _ := generateTestObjects(entriesPerBucket*bucketCount*2, bucketCount, 0)
5556

@@ -58,7 +59,7 @@ func TestBucketCycling(t *testing.T) {
5859
require.NoError(t, err)
5960

6061
// fill up the cache with entries.
61-
for i := 0; i < entriesPerBucket*(bucketCount+1); i++ {
62+
for i := 0; i < entriesPerBucket*bucketCount; i++ {
6263
impl.Add([]transactions.SignedTxn{signedTxn[i]}, groupCtx)
6364
// test to see that the base is sliding when bucket get filled up.
6465
require.Equal(t, i/entriesPerBucket, impl.base)
@@ -136,13 +137,13 @@ func BenchmarkGetUnverifiedTranscationGroups50(b *testing.B) {
136137
func TestUpdatePinned(t *testing.T) {
137138
partitiontest.PartitionTest(t)
138139

139-
size := entriesPerBucket
140+
size := 100
140141
icache := MakeVerifiedTransactionCache(size * 10)
141142
impl := icache.(*verifiedTransactionCache)
142143
_, signedTxn, secrets, addrs := generateTestObjects(size*2, 10, 0)
143144
txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
144145

145-
// insert half of the entries.
146+
// insert some entries.
146147
for i := 0; i < len(txnGroups); i++ {
147148
groupCtx, _ := PrepareGroupContext(txnGroups[i], blockHeader)
148149
impl.Add(txnGroups[i], groupCtx)
@@ -165,7 +166,7 @@ func TestUpdatePinned(t *testing.T) {
165166
func TestPinningTransactions(t *testing.T) {
166167
partitiontest.PartitionTest(t)
167168

168-
size := entriesPerBucket
169+
size := 100
169170
icache := MakeVerifiedTransactionCache(size)
170171
impl := icache.(*verifiedTransactionCache)
171172
_, signedTxn, secrets, addrs := generateTestObjects(size*2, 10, 0)
@@ -182,5 +183,4 @@ func TestPinningTransactions(t *testing.T) {
182183

183184
// try to pin an entry that was not added.
184185
require.Error(t, impl.Pin(txnGroups[len(txnGroups)-1]))
185-
186186
}

0 commit comments

Comments
 (0)