Skip to content

Commit 97ba770

Browse files
Cache subnetIDs in cachedState (#4402)
Signed-off-by: Michael Kaplan <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 807b97e commit 97ba770

File tree

2 files changed

+110
-7
lines changed

2 files changed

+110
-7
lines changed

snow/validators/state.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import (
1313
"github.com/ava-labs/avalanchego/ids"
1414
)
1515

16-
const validatorSetsCacheSize = 8
16+
const (
17+
validatorSetsCacheSize = 8
18+
subnetIDsCacheSize = 4096 // At 64 bytes per entry, this is ~256 KB
19+
)
1720

1821
var (
1922
_ State = (*lockedState)(nil)
@@ -189,10 +192,15 @@ type cachedState struct {
189192
// long as it is cached.
190193
activation time.Time
191194

195+
// Caches the subnet ID for given blockchain IDs.
196+
// Key: blockchain ID
197+
// Value: subnet ID
198+
subnetIDsCache cache.Cacher[ids.ID, ids.ID]
199+
192200
// Caches validators for all subnets at various heights.
193201
// Key: height
194202
// Value: mapping subnet ID -> validator set
195-
cache cache.Cacher[uint64, map[ids.ID]WarpSet]
203+
validatorSetsCache cache.Cacher[uint64, map[ids.ID]WarpSet]
196204
}
197205

198206
// TODO: Remove the graniteActivation parameter once all networks have
@@ -202,25 +210,38 @@ func NewCachedState(
202210
graniteActivation time.Time,
203211
) State {
204212
return &cachedState{
205-
State: state,
206-
activation: graniteActivation,
207-
cache: lru.NewCache[uint64, map[ids.ID]WarpSet](validatorSetsCacheSize),
213+
State: state,
214+
activation: graniteActivation,
215+
validatorSetsCache: lru.NewCache[uint64, map[ids.ID]WarpSet](validatorSetsCacheSize),
216+
subnetIDsCache: lru.NewCache[ids.ID, ids.ID](subnetIDsCacheSize),
208217
}
209218
}
210219

220+
func (c *cachedState) GetSubnetID(ctx context.Context, chainID ids.ID) (ids.ID, error) {
221+
if s, ok := c.subnetIDsCache.Get(chainID); ok {
222+
return s, nil
223+
}
224+
s, err := c.State.GetSubnetID(ctx, chainID)
225+
if err != nil {
226+
return ids.Empty, err
227+
}
228+
c.subnetIDsCache.Put(chainID, s)
229+
return s, nil
230+
}
231+
211232
func (c *cachedState) GetWarpValidatorSets(
212233
ctx context.Context,
213234
height uint64,
214235
) (map[ids.ID]WarpSet, error) {
215-
if s, ok := c.cache.Get(height); ok {
236+
if s, ok := c.validatorSetsCache.Get(height); ok {
216237
return s, nil
217238
}
218239

219240
s, err := c.State.GetWarpValidatorSets(ctx, height)
220241
if err != nil {
221242
return nil, err
222243
}
223-
c.cache.Put(height, s)
244+
c.validatorSetsCache.Put(height, s)
224245
return s, nil
225246
}
226247

snow/validators/state_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,88 @@ import (
2121

2222
var errTest = errors.New("test error")
2323

24+
func TestCachedState_GetSubnetID(t *testing.T) {
25+
require := require.New(t)
26+
27+
uncached := &validatorstest.State{
28+
T: t,
29+
CantGetSubnetID: true,
30+
}
31+
cached := NewCachedState(uncached, upgrade.InitiallyActiveTime)
32+
33+
blockchainID1 := ids.GenerateTestID()
34+
blockchainID2 := ids.GenerateTestID()
35+
subnetIDs := map[ids.ID]ids.ID{
36+
blockchainID1: ids.GenerateTestID(),
37+
blockchainID2: ids.GenerateTestID(),
38+
}
39+
tests := []struct {
40+
name string
41+
expectCached bool
42+
blockchainID ids.ID
43+
want ids.ID
44+
wantErr error
45+
}{
46+
{
47+
name: "populate_initial_entry",
48+
expectCached: false,
49+
blockchainID: blockchainID1,
50+
want: subnetIDs[blockchainID1],
51+
},
52+
{
53+
name: "initial_entry_was_cached",
54+
expectCached: true,
55+
blockchainID: blockchainID1,
56+
want: subnetIDs[blockchainID1],
57+
},
58+
{
59+
name: "cache_miss_error",
60+
expectCached: false,
61+
blockchainID: blockchainID2,
62+
wantErr: errTest,
63+
},
64+
{
65+
name: "cache_after_miss_error",
66+
expectCached: false,
67+
blockchainID: blockchainID2,
68+
want: subnetIDs[blockchainID2],
69+
},
70+
{
71+
name: "second_cache_hit",
72+
expectCached: true,
73+
blockchainID: blockchainID2,
74+
want: subnetIDs[blockchainID2],
75+
},
76+
{
77+
name: "cache_multiple",
78+
expectCached: true,
79+
blockchainID: blockchainID1,
80+
want: subnetIDs[blockchainID1],
81+
},
82+
}
83+
for _, test := range tests {
84+
t.Logf("starting test: %s", test.name)
85+
86+
var cacheMiss bool
87+
if !test.expectCached {
88+
uncached.GetSubnetIDF = func(_ context.Context, chainID ids.ID) (ids.ID, error) {
89+
require.Equal(test.blockchainID, chainID)
90+
require.False(cacheMiss)
91+
92+
cacheMiss = true
93+
return test.want, test.wantErr
94+
}
95+
} else {
96+
uncached.GetSubnetIDF = nil
97+
}
98+
99+
got, err := cached.GetSubnetID(t.Context(), test.blockchainID)
100+
require.ErrorIs(err, test.wantErr)
101+
require.Equal(test.want, got)
102+
require.Equal(test.expectCached, !cacheMiss)
103+
}
104+
}
105+
24106
func TestCachedState_GetWarpValidatorSets(t *testing.T) {
25107
require := require.New(t)
26108

0 commit comments

Comments
 (0)