Skip to content

Commit 93c2734

Browse files
Tracked validator TTL (#14957)
* `TrackedValidatorsCache`: Implement a 1-hour TTL by uding `go-cache`. * `TrackedValidatorsCache`: Add the `ItemCount` method. * `TrackedValidatorsCache`: Add the `Indices` method. * Add changelog. * `TrackedValidatorsCache`: Add prometheus metrics. * Update beacon-chain/cache/tracked_validators.go Co-authored-by: Preston Van Loon <[email protected]> --------- Co-authored-by: Preston Van Loon <[email protected]>
1 parent c3edb32 commit 93c2734

File tree

4 files changed

+202
-24
lines changed

4 files changed

+202
-24
lines changed

beacon-chain/cache/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ go_test(
8484
"sync_committee_head_state_test.go",
8585
"sync_committee_test.go",
8686
"sync_subnet_ids_test.go",
87+
"tracked_validators_test.go",
8788
],
8889
embed = [":go_default_library"],
8990
deps = [
Lines changed: 114 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,139 @@
11
package cache
22

33
import (
4-
"sync"
4+
"strconv"
5+
"time"
56

7+
"github.com/patrickmn/go-cache"
8+
"github.com/pkg/errors"
9+
"github.com/prometheus/client_golang/prometheus"
10+
"github.com/prometheus/client_golang/prometheus/promauto"
611
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
12+
"github.com/sirupsen/logrus"
713
)
814

9-
type TrackedValidator struct {
10-
Active bool
11-
FeeRecipient primitives.ExecutionAddress
12-
Index primitives.ValidatorIndex
13-
}
15+
const (
16+
defaultExpiration = 1 * time.Hour
17+
cleanupInterval = 15 * time.Minute
18+
)
1419

15-
type TrackedValidatorsCache struct {
16-
sync.Mutex
17-
trackedValidators map[primitives.ValidatorIndex]TrackedValidator
18-
}
20+
type (
21+
TrackedValidator struct {
22+
Active bool
23+
FeeRecipient primitives.ExecutionAddress
24+
Index primitives.ValidatorIndex
25+
}
26+
27+
TrackedValidatorsCache struct {
28+
trackedValidators cache.Cache
29+
}
30+
)
31+
32+
var (
33+
// Metrics.
34+
trackedValidatorsCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
35+
Name: "tracked_validators_cache_miss",
36+
Help: "The number of tracked validators requests that are not present in the cache.",
37+
})
38+
39+
trackedValidatorsCacheTotal = promauto.NewCounter(prometheus.CounterOpts{
40+
Name: "tracked_validators_cache_total",
41+
Help: "The total number of tracked validators requests in the cache.",
42+
})
1943

44+
trackedValidatorsCacheCount = promauto.NewGauge(prometheus.GaugeOpts{
45+
Name: "tracked_validators_cache_count",
46+
Help: "The number of tracked validators in the cache.",
47+
})
48+
)
49+
50+
// NewTrackedValidatorsCache creates a new cache for tracking validators.
2051
func NewTrackedValidatorsCache() *TrackedValidatorsCache {
2152
return &TrackedValidatorsCache{
22-
trackedValidators: make(map[primitives.ValidatorIndex]TrackedValidator),
53+
trackedValidators: *cache.New(defaultExpiration, cleanupInterval),
2354
}
2455
}
2556

57+
// Validator retrieves a tracked validator from the cache (if present).
2658
func (t *TrackedValidatorsCache) Validator(index primitives.ValidatorIndex) (TrackedValidator, bool) {
27-
t.Lock()
28-
defer t.Unlock()
29-
val, ok := t.trackedValidators[index]
30-
return val, ok
59+
trackedValidatorsCacheTotal.Inc()
60+
61+
key := toCacheKey(index)
62+
item, ok := t.trackedValidators.Get(key)
63+
if !ok {
64+
trackedValidatorsCacheMiss.Inc()
65+
return TrackedValidator{}, false
66+
}
67+
68+
val, ok := item.(TrackedValidator)
69+
if !ok {
70+
logrus.Errorf("Failed to cast tracked validator from cache, got unexpected item type %T", item)
71+
return TrackedValidator{}, false
72+
}
73+
74+
return val, true
3175
}
3276

77+
// Set adds a tracked validator to the cache.
3378
func (t *TrackedValidatorsCache) Set(val TrackedValidator) {
34-
t.Lock()
35-
defer t.Unlock()
36-
t.trackedValidators[val.Index] = val
79+
key := toCacheKey(val.Index)
80+
t.trackedValidators.Set(key, val, cache.DefaultExpiration)
3781
}
3882

83+
// Delete removes a tracked validator from the cache.
3984
func (t *TrackedValidatorsCache) Prune() {
40-
t.Lock()
41-
defer t.Unlock()
42-
t.trackedValidators = make(map[primitives.ValidatorIndex]TrackedValidator)
85+
t.trackedValidators.Flush()
86+
trackedValidatorsCacheCount.Set(0)
4387
}
4488

89+
// Validating returns true if there are at least one tracked validators in the cache.
4590
func (t *TrackedValidatorsCache) Validating() bool {
46-
t.Lock()
47-
defer t.Unlock()
48-
return len(t.trackedValidators) > 0
91+
count := t.trackedValidators.ItemCount()
92+
trackedValidatorsCacheCount.Set(float64(count))
93+
94+
return count > 0
95+
}
96+
97+
// ItemCount returns the number of tracked validators in the cache.
98+
func (t *TrackedValidatorsCache) ItemCount() int {
99+
count := t.trackedValidators.ItemCount()
100+
trackedValidatorsCacheCount.Set(float64(count))
101+
102+
return count
103+
}
104+
105+
// Indices returns a map of validator indices that are being tracked.
106+
func (t *TrackedValidatorsCache) Indices() map[primitives.ValidatorIndex]bool {
107+
items := t.trackedValidators.Items()
108+
count := len(items)
109+
trackedValidatorsCacheCount.Set(float64(count))
110+
111+
indices := make(map[primitives.ValidatorIndex]bool, count)
112+
113+
for cacheKey := range items {
114+
index, err := fromCacheKey(cacheKey)
115+
if err != nil {
116+
logrus.WithError(err).Error("Failed to get validator index from cache key")
117+
continue
118+
}
119+
120+
indices[index] = true
121+
}
122+
123+
return indices
124+
}
125+
126+
// toCacheKey creates a cache key from the validator index.
127+
func toCacheKey(validatorIndex primitives.ValidatorIndex) string {
128+
return strconv.FormatUint(uint64(validatorIndex), 10)
129+
}
130+
131+
// fromCacheKey gets the validator index from the cache key.
132+
func fromCacheKey(key string) (primitives.ValidatorIndex, error) {
133+
validatorIndex, err := strconv.ParseUint(key, 10, 64)
134+
if err != nil {
135+
return 0, errors.Wrapf(err, "parse Uint: %s", key)
136+
}
137+
138+
return primitives.ValidatorIndex(validatorIndex), nil
49139
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package cache
2+
3+
import (
4+
"testing"
5+
6+
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
7+
"github.com/prysmaticlabs/prysm/v5/testing/require"
8+
)
9+
10+
func mapEqual(a, b map[primitives.ValidatorIndex]bool) bool {
11+
if len(a) != len(b) {
12+
return false
13+
}
14+
15+
for k, v := range a {
16+
if b[k] != v {
17+
return false
18+
}
19+
}
20+
21+
return true
22+
}
23+
24+
func TestTrackedValidatorsCache(t *testing.T) {
25+
vc := NewTrackedValidatorsCache()
26+
27+
// No validators in cache.
28+
require.Equal(t, 0, vc.ItemCount())
29+
require.Equal(t, false, vc.Validating())
30+
require.Equal(t, 0, len(vc.Indices()))
31+
32+
_, ok := vc.Validator(41)
33+
require.Equal(t, false, ok)
34+
35+
// Add some validators (one twice).
36+
v42Expected := TrackedValidator{Active: true, FeeRecipient: [20]byte{1}, Index: 42}
37+
v43Expected := TrackedValidator{Active: false, FeeRecipient: [20]byte{2}, Index: 43}
38+
39+
vc.Set(v42Expected)
40+
vc.Set(v43Expected)
41+
vc.Set(v42Expected)
42+
43+
// Check if they are in the cache.
44+
v42Actual, ok := vc.Validator(42)
45+
require.Equal(t, true, ok)
46+
require.Equal(t, v42Expected, v42Actual)
47+
48+
v43Actual, ok := vc.Validator(43)
49+
require.Equal(t, true, ok)
50+
require.Equal(t, v43Expected, v43Actual)
51+
52+
expected := map[primitives.ValidatorIndex]bool{42: true, 43: true}
53+
actual := vc.Indices()
54+
require.Equal(t, true, mapEqual(expected, actual))
55+
56+
// Check the item count and if the cache is validating.
57+
require.Equal(t, 2, vc.ItemCount())
58+
require.Equal(t, true, vc.Validating())
59+
60+
// Check if a non-existing validator is in the cache.
61+
_, ok = vc.Validator(41)
62+
require.Equal(t, false, ok)
63+
64+
// Prune the cache and test it.
65+
vc.Prune()
66+
67+
_, ok = vc.Validator(41)
68+
require.Equal(t, false, ok)
69+
70+
_, ok = vc.Validator(42)
71+
require.Equal(t, false, ok)
72+
73+
_, ok = vc.Validator(43)
74+
require.Equal(t, false, ok)
75+
76+
require.Equal(t, 0, vc.ItemCount())
77+
require.Equal(t, false, vc.Validating())
78+
require.Equal(t, 0, len(vc.Indices()))
79+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
### Added
2+
3+
- Tracked validators cache: Added the `ItemCount` method.
4+
- Tracked validators cache: Added the `Indices` method.
5+
6+
### Changed
7+
8+
- Tracked validators cache: Remove validators from the cache if not seen after 1 hour.

0 commit comments

Comments
 (0)