Skip to content

Commit a55844d

Browse files
authored
receive/expandedpostingscache: fix race (#7937)
Porting cortexproject/cortex#6369 to our code base. Add test that fails without the fix. Signed-off-by: Giedrius Statkevičius <[email protected]>
1 parent b144eba commit a55844d

File tree

3 files changed

+56
-10
lines changed

3 files changed

+56
-10
lines changed

pkg/receive/expandedpostingscache/cache.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type BlocksPostingsForMatchersCache struct {
4343
postingsForMatchersFunc func(ctx context.Context, ix tsdb.IndexReader, ms ...*labels.Matcher) (index.Postings, error)
4444
timeNow func() time.Time
4545

46-
metrics *ExpandedPostingsCacheMetrics
46+
metrics ExpandedPostingsCacheMetrics
4747
}
4848

4949
var (
@@ -66,8 +66,8 @@ type ExpandedPostingsCacheMetrics struct {
6666
NonCacheableQueries *prometheus.CounterVec
6767
}
6868

69-
func NewPostingCacheMetrics(r prometheus.Registerer) *ExpandedPostingsCacheMetrics {
70-
return &ExpandedPostingsCacheMetrics{
69+
func NewPostingCacheMetrics(r prometheus.Registerer) ExpandedPostingsCacheMetrics {
70+
return ExpandedPostingsCacheMetrics{
7171
CacheRequests: promauto.With(r).NewCounterVec(prometheus.CounterOpts{
7272
Name: "expanded_postings_cache_requests_total",
7373
Help: "Total number of requests to the cache.",
@@ -87,11 +87,15 @@ func NewPostingCacheMetrics(r prometheus.Registerer) *ExpandedPostingsCacheMetri
8787
}
8888
}
8989

90-
func NewBlocksPostingsForMatchersCache(metrics *ExpandedPostingsCacheMetrics, headExpandedPostingsCacheSize uint64, blockExpandedPostingsCacheSize uint64) ExpandedPostingsCache {
90+
func NewBlocksPostingsForMatchersCache(metrics ExpandedPostingsCacheMetrics, headExpandedPostingsCacheSize uint64, blockExpandedPostingsCacheSize uint64, seedSize int64) *BlocksPostingsForMatchersCache {
91+
if seedSize <= 0 {
92+
seedSize = seedArraySize
93+
}
94+
9195
return &BlocksPostingsForMatchersCache{
9296
headCache: newFifoCache[[]storage.SeriesRef]("head", metrics, time.Now, headExpandedPostingsCacheSize),
9397
blocksCache: newFifoCache[[]storage.SeriesRef]("block", metrics, time.Now, blockExpandedPostingsCacheSize),
94-
headSeedByMetricName: make([]int, seedArraySize),
98+
headSeedByMetricName: make([]int, seedSize),
9599
strippedLock: make([]sync.RWMutex, numOfSeedsStripes),
96100
postingsForMatchersFunc: tsdb.PostingsForMatchers,
97101
timeNow: time.Now,
@@ -129,7 +133,7 @@ func (c *BlocksPostingsForMatchersCache) ExpireSeries(metric labels.Labels) {
129133

130134
h := MemHashString(metricName)
131135
i := h % uint64(len(c.headSeedByMetricName))
132-
l := h % uint64(len(c.strippedLock))
136+
l := i % uint64(len(c.strippedLock))
133137
c.strippedLock[l].Lock()
134138
defer c.strippedLock[l].Unlock()
135139
c.headSeedByMetricName[i]++
@@ -200,7 +204,7 @@ func (c *BlocksPostingsForMatchersCache) result(ce *cacheEntryPromise[[]storage.
200204
func (c *BlocksPostingsForMatchersCache) getSeedForMetricName(metricName string) string {
201205
h := MemHashString(metricName)
202206
i := h % uint64(len(c.headSeedByMetricName))
203-
l := h % uint64(len(c.strippedLock))
207+
l := i % uint64(len(c.strippedLock))
204208
c.strippedLock[l].RLock()
205209
defer c.strippedLock[l].RUnlock()
206210
return strconv.Itoa(c.headSeedByMetricName[i])
@@ -276,13 +280,13 @@ type fifoCache[V any] struct {
276280
cachedBytes int64
277281
}
278282

279-
func newFifoCache[V any](name string, metrics *ExpandedPostingsCacheMetrics, timeNow func() time.Time, maxBytes uint64) *fifoCache[V] {
283+
func newFifoCache[V any](name string, metrics ExpandedPostingsCacheMetrics, timeNow func() time.Time, maxBytes uint64) *fifoCache[V] {
280284
return &fifoCache[V]{
281285
cachedValues: new(sync.Map),
282286
cached: list.New(),
283287
timeNow: timeNow,
284288
name: name,
285-
metrics: *metrics,
289+
metrics: metrics,
286290
ttl: 10 * time.Minute,
287291
maxBytes: int64(maxBytes),
288292
}

pkg/receive/expandedpostingscache/cache_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import (
1515
"time"
1616

1717
"go.uber.org/atomic"
18+
"golang.org/x/exp/rand"
1819

1920
"github.com/prometheus/client_golang/prometheus"
2021
promtest "github.com/prometheus/client_golang/prometheus/testutil"
22+
"github.com/prometheus/prometheus/model/labels"
2123
"github.com/stretchr/testify/require"
2224
)
2325

@@ -166,3 +168,43 @@ func repeatStringIfNeeded(seed string, length int) string {
166168

167169
return strings.Repeat(seed, 1+length/len(seed))[:max(length, len(seed))]
168170
}
171+
172+
func TestLockRaceExpireSeries(t *testing.T) {
173+
for j := 0; j < 10; j++ {
174+
wg := &sync.WaitGroup{}
175+
176+
c := NewBlocksPostingsForMatchersCache(ExpandedPostingsCacheMetrics{}, 1<<7, 1<<7, 3)
177+
for i := 0; i < 1000; i++ {
178+
wg.Add(2)
179+
180+
go func() {
181+
defer wg.Done()
182+
for i := 0; i < 10; i++ {
183+
c.ExpireSeries(
184+
labels.FromMap(map[string]string{"__name__": randSeq(10)}),
185+
)
186+
}
187+
}()
188+
189+
go func() {
190+
defer wg.Done()
191+
192+
for i := 0; i < 10; i++ {
193+
c.getSeedForMetricName(randSeq(10))
194+
}
195+
}()
196+
}
197+
wg.Wait()
198+
}
199+
}
200+
201+
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
202+
203+
func randSeq(n int) string {
204+
b := make([]rune, n)
205+
rand.Seed(uint64(time.Now().UnixNano()))
206+
for i := range b {
207+
b[i] = letters[rand.Intn(len(letters))]
208+
}
209+
return string(b)
210+
}

pkg/receive/multitsdb.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ func (t *MultiTSDB) startTSDB(logger log.Logger, tenantID string, tenant *tenant
709709
if t.headExpandedPostingsCacheSize > 0 || t.blockExpandedPostingsCacheSize > 0 {
710710
var expandedPostingsCacheMetrics = expandedpostingscache.NewPostingCacheMetrics(extprom.WrapRegistererWithPrefix("thanos_", reg))
711711

712-
expandedPostingsCache = expandedpostingscache.NewBlocksPostingsForMatchersCache(expandedPostingsCacheMetrics, t.headExpandedPostingsCacheSize, t.blockExpandedPostingsCacheSize)
712+
expandedPostingsCache = expandedpostingscache.NewBlocksPostingsForMatchersCache(expandedPostingsCacheMetrics, t.headExpandedPostingsCacheSize, t.blockExpandedPostingsCacheSize, 0)
713713
}
714714

715715
opts := *t.tsdbOpts

0 commit comments

Comments
 (0)