From 56052262c920fed09a51c162bd12be9e55ecf411 Mon Sep 17 00:00:00 2001 From: Michael Kaplan Date: Tue, 7 Oct 2025 16:48:12 -0400 Subject: [PATCH 1/3] Cache subnet IDs in cachedState --- snow/validators/state.go | 35 ++++++++++++--- snow/validators/state_test.go | 84 ++++++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 8 deletions(-) diff --git a/snow/validators/state.go b/snow/validators/state.go index 6c2164a79206..b025f84c142b 100644 --- a/snow/validators/state.go +++ b/snow/validators/state.go @@ -13,7 +13,10 @@ import ( "github.com/ava-labs/avalanchego/ids" ) -const validatorSetsCacheSize = 8 +const ( + validatorSetsCacheSize = 8 + subnetIDsCacheSize = 4096 // At 64 bytes per entry, this is ~256 KB +) var ( _ State = (*lockedState)(nil) @@ -189,10 +192,15 @@ type cachedState struct { // long as it is cached. activation time.Time + // Caches the subnet ID for given blockchain IDs. + // Key: blockchain ID + // Value: subnet ID + subnetIDsCache cache.Cacher[ids.ID, ids.ID] + // Caches validators for all subnets at various heights. // Key: height // Value: mapping subnet ID -> validator set - cache cache.Cacher[uint64, map[ids.ID]WarpSet] + validatorsSetsCache cache.Cacher[uint64, map[ids.ID]WarpSet] } // TODO: Remove the graniteActivation parameter once all networks have @@ -202,17 +210,30 @@ func NewCachedState( graniteActivation time.Time, ) State { return &cachedState{ - State: state, - activation: graniteActivation, - cache: lru.NewCache[uint64, map[ids.ID]WarpSet](validatorSetsCacheSize), + State: state, + activation: graniteActivation, + validatorsSetsCache: lru.NewCache[uint64, map[ids.ID]WarpSet](validatorSetsCacheSize), + subnetIDsCache: lru.NewCache[ids.ID, ids.ID](subnetIDsCacheSize), + } +} + +func (c *cachedState) GetSubnetID(ctx context.Context, chainID ids.ID) (ids.ID, error) { + if s, ok := c.subnetIDsCache.Get(chainID); ok { + return s, nil + } + s, err := c.State.GetSubnetID(ctx, chainID) + if err != nil { + return ids.Empty, err } + c.subnetIDsCache.Put(chainID, s) + return s, err } func (c *cachedState) GetWarpValidatorSets( ctx context.Context, height uint64, ) (map[ids.ID]WarpSet, error) { - if s, ok := c.cache.Get(height); ok { + if s, ok := c.validatorsSetsCache.Get(height); ok { return s, nil } @@ -220,7 +241,7 @@ func (c *cachedState) GetWarpValidatorSets( if err != nil { return nil, err } - c.cache.Put(height, s) + c.validatorsSetsCache.Put(height, s) return s, nil } diff --git a/snow/validators/state_test.go b/snow/validators/state_test.go index a09b65c6cb2a..4672038d21dd 100644 --- a/snow/validators/state_test.go +++ b/snow/validators/state_test.go @@ -21,6 +21,88 @@ import ( var errTest = errors.New("test error") +func TestCachedState_GetSubnetID(t *testing.T) { + require := require.New(t) + + uncached := &validatorstest.State{ + T: t, + CantGetSubnetID: true, + } + cached := NewCachedState(uncached, upgrade.InitiallyActiveTime) + + blockchainID1 := ids.GenerateTestID() + blockchainID2 := ids.GenerateTestID() + subnetIDs := map[ids.ID]ids.ID{ + blockchainID1: ids.GenerateTestID(), + blockchainID2: ids.GenerateTestID(), + } + tests := []struct { + name string + expectCached bool + blockchainID ids.ID + want ids.ID + wantErr error + }{ + { + name: "populate_initial_entry", + expectCached: false, + blockchainID: blockchainID1, + want: subnetIDs[blockchainID1], + }, + { + name: "initial_entry_was_cached", + expectCached: true, + blockchainID: blockchainID1, + want: subnetIDs[blockchainID1], + }, + { + name: "cache_miss_error", + expectCached: false, + blockchainID: blockchainID2, + wantErr: errTest, + }, + { + name: "cache_after_miss_error", + expectCached: false, + blockchainID: blockchainID2, + want: subnetIDs[blockchainID2], + }, + { + name: "second_cache_hit", + expectCached: true, + blockchainID: blockchainID2, + want: subnetIDs[blockchainID2], + }, + { + name: "cache_multiple", + expectCached: true, + blockchainID: blockchainID1, + want: subnetIDs[blockchainID1], + }, + } + for _, test := range tests { + t.Logf("starting test: %s", test.name) + + var cacheMiss bool + if !test.expectCached { + uncached.GetSubnetIDF = func(_ context.Context, chainID ids.ID) (ids.ID, error) { + require.Equal(test.blockchainID, chainID) + require.False(cacheMiss) + + cacheMiss = true + return test.want, test.wantErr + } + } else { + uncached.GetSubnetIDF = nil + } + + got, err := cached.GetSubnetID(context.Background(), test.blockchainID) + require.ErrorIs(err, test.wantErr) + require.Equal(test.want, got) + require.Equal(test.expectCached, !cacheMiss) + } +} + func TestCachedState_GetWarpValidatorSets(t *testing.T) { require := require.New(t) @@ -68,7 +150,7 @@ func TestCachedState_GetWarpValidatorSets(t *testing.T) { want: allVdrs1, }, { - name: "cache_after_miss_error", + name: "cache_hit", expectCached: true, height: 1, want: allVdrs1, From 4d2bfdb9e30bc26c438e2c37913c350185b7634a Mon Sep 17 00:00:00 2001 From: Michael Kaplan Date: Tue, 7 Oct 2025 16:49:47 -0400 Subject: [PATCH 2/3] Reduce diff --- snow/validators/state_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snow/validators/state_test.go b/snow/validators/state_test.go index 4672038d21dd..75dd8745f2c2 100644 --- a/snow/validators/state_test.go +++ b/snow/validators/state_test.go @@ -150,7 +150,7 @@ func TestCachedState_GetWarpValidatorSets(t *testing.T) { want: allVdrs1, }, { - name: "cache_hit", + name: "cache_after_miss_error", expectCached: true, height: 1, want: allVdrs1, From 2d3640fb42a76adcb4994cbc1c39fdf4b8512bf7 Mon Sep 17 00:00:00 2001 From: Michael Kaplan <55204436+michaelkaplan13@users.noreply.github.com> Date: Tue, 7 Oct 2025 17:04:22 -0400 Subject: [PATCH 3/3] Update snow/validators/state.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Michael Kaplan <55204436+michaelkaplan13@users.noreply.github.com> --- snow/validators/state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snow/validators/state.go b/snow/validators/state.go index b025f84c142b..dca7ab936337 100644 --- a/snow/validators/state.go +++ b/snow/validators/state.go @@ -226,7 +226,7 @@ func (c *cachedState) GetSubnetID(ctx context.Context, chainID ids.ID) (ids.ID, return ids.Empty, err } c.subnetIDsCache.Put(chainID, s) - return s, err + return s, nil } func (c *cachedState) GetWarpValidatorSets(