@@ -10,16 +10,17 @@ import (
10
10
"sync"
11
11
"time"
12
12
13
- "github.com/cespare/xxhash/v2"
14
13
"github.com/oklog/ulid"
15
14
"github.com/prometheus/client_golang/prometheus"
16
15
"github.com/prometheus/client_golang/prometheus/promauto"
17
16
"github.com/prometheus/prometheus/model/labels"
18
17
"github.com/prometheus/prometheus/storage"
19
18
"github.com/prometheus/prometheus/tsdb"
20
19
"github.com/prometheus/prometheus/tsdb/index"
20
+ "github.com/segmentio/fasthash/fnv1a"
21
21
22
22
"github.com/cortexproject/cortex/pkg/util/extract"
23
+ logutil "github.com/cortexproject/cortex/pkg/util/log"
23
24
)
24
25
25
26
var (
29
30
30
31
const (
31
32
// size of the seed array. Each seed is a 64bits int (8 bytes)
32
- // totaling 8mb
33
- seedArraySize = 1024 * 1024
33
+ // totaling 16mb
34
+ seedArraySize = 2 * 1024 * 1024
34
35
35
36
numOfSeedsStripes = 512
36
37
)
@@ -67,7 +68,9 @@ type TSDBPostingsCacheConfig struct {
67
68
Head PostingsCacheConfig `yaml:"head" doc:"description=If enabled, ingesters will cache expanded postings for the head block. Only queries with with an equal matcher for metric __name__ are cached."`
68
69
Blocks PostingsCacheConfig `yaml:"blocks" doc:"description=If enabled, ingesters will cache expanded postings for the compacted blocks. The cache is shared between all blocks."`
69
70
71
+ // The configurations below are used only for testing purpose
70
72
PostingsForMatchers func (ctx context.Context , ix tsdb.IndexReader , ms ... * labels.Matcher ) (index.Postings , error ) `yaml:"-"`
73
+ SeedSize int `yaml:"-"`
71
74
timeNow func () time.Time `yaml:"-"`
72
75
}
73
76
@@ -89,25 +92,48 @@ func (cfg *PostingsCacheConfig) RegisterFlagsWithPrefix(prefix, block string, f
89
92
f .BoolVar (& cfg .Enabled , prefix + "expanded_postings_cache." + block + ".enabled" , false , "Whether the postings cache is enabled or not" )
90
93
}
91
94
95
+ type ExpandedPostingsCacheFactory struct {
96
+ seedByHash * seedByHash
97
+ cfg TSDBPostingsCacheConfig
98
+ }
99
+
100
+ func NewExpandedPostingsCacheFactory (cfg TSDBPostingsCacheConfig ) * ExpandedPostingsCacheFactory {
101
+ if cfg .Head .Enabled || cfg .Blocks .Enabled {
102
+ if cfg .SeedSize == 0 {
103
+ cfg .SeedSize = seedArraySize
104
+ }
105
+ logutil .WarnExperimentalUse ("expanded postings cache" )
106
+ return & ExpandedPostingsCacheFactory {
107
+ cfg : cfg ,
108
+ seedByHash : newSeedByHash (cfg .SeedSize ),
109
+ }
110
+ }
111
+
112
+ return nil
113
+ }
114
+
115
+ func (f * ExpandedPostingsCacheFactory ) NewExpandedPostingsCache (userId string , metrics * ExpandedPostingsCacheMetrics ) ExpandedPostingsCache {
116
+ return newBlocksPostingsForMatchersCache (userId , f .cfg , metrics , f .seedByHash )
117
+ }
118
+
92
119
type ExpandedPostingsCache interface {
93
120
PostingsForMatchers (ctx context.Context , blockID ulid.ULID , ix tsdb.IndexReader , ms ... * labels.Matcher ) (index.Postings , error )
94
121
ExpireSeries (metric labels.Labels )
95
122
}
96
123
97
- type BlocksPostingsForMatchersCache struct {
98
- strippedLock []sync.RWMutex
99
-
100
- headCache * fifoCache [[]storage.SeriesRef ]
101
- blocksCache * fifoCache [[]storage.SeriesRef ]
124
+ type blocksPostingsForMatchersCache struct {
125
+ userId string
102
126
103
- headSeedByMetricName []int
127
+ headCache * fifoCache [[]storage.SeriesRef ]
128
+ blocksCache * fifoCache [[]storage.SeriesRef ]
104
129
postingsForMatchersFunc func (ctx context.Context , ix tsdb.IndexReader , ms ... * labels.Matcher ) (index.Postings , error )
105
130
timeNow func () time.Time
106
131
107
- metrics * ExpandedPostingsCacheMetrics
132
+ metrics * ExpandedPostingsCacheMetrics
133
+ seedByHash * seedByHash
108
134
}
109
135
110
- func NewBlocksPostingsForMatchersCache ( cfg TSDBPostingsCacheConfig , metrics * ExpandedPostingsCacheMetrics ) ExpandedPostingsCache {
136
+ func newBlocksPostingsForMatchersCache ( userId string , cfg TSDBPostingsCacheConfig , metrics * ExpandedPostingsCacheMetrics , seedByHash * seedByHash ) ExpandedPostingsCache {
111
137
if cfg .PostingsForMatchers == nil {
112
138
cfg .PostingsForMatchers = tsdb .PostingsForMatchers
113
139
}
@@ -116,36 +142,30 @@ func NewBlocksPostingsForMatchersCache(cfg TSDBPostingsCacheConfig, metrics *Exp
116
142
cfg .timeNow = time .Now
117
143
}
118
144
119
- return & BlocksPostingsForMatchersCache {
145
+ return & blocksPostingsForMatchersCache {
120
146
headCache : newFifoCache [[]storage.SeriesRef ](cfg .Head , "head" , metrics , cfg .timeNow ),
121
147
blocksCache : newFifoCache [[]storage.SeriesRef ](cfg .Blocks , "block" , metrics , cfg .timeNow ),
122
- headSeedByMetricName : make ([]int , seedArraySize ),
123
- strippedLock : make ([]sync.RWMutex , numOfSeedsStripes ),
124
148
postingsForMatchersFunc : cfg .PostingsForMatchers ,
125
149
timeNow : cfg .timeNow ,
126
150
metrics : metrics ,
151
+ seedByHash : seedByHash ,
152
+ userId : userId ,
127
153
}
128
154
}
129
155
130
- func (c * BlocksPostingsForMatchersCache ) ExpireSeries (metric labels.Labels ) {
156
+ func (c * blocksPostingsForMatchersCache ) ExpireSeries (metric labels.Labels ) {
131
157
metricName , err := extract .MetricNameFromLabels (metric )
132
158
if err != nil {
133
159
return
134
160
}
135
-
136
- h := MemHashString (metricName )
137
- i := h % uint64 (len (c .headSeedByMetricName ))
138
- l := h % uint64 (len (c .strippedLock ))
139
- c .strippedLock [l ].Lock ()
140
- defer c .strippedLock [l ].Unlock ()
141
- c .headSeedByMetricName [i ]++
161
+ c .seedByHash .incrementSeed (c .userId , metricName )
142
162
}
143
163
144
- func (c * BlocksPostingsForMatchersCache ) PostingsForMatchers (ctx context.Context , blockID ulid.ULID , ix tsdb.IndexReader , ms ... * labels.Matcher ) (index.Postings , error ) {
164
+ func (c * blocksPostingsForMatchersCache ) PostingsForMatchers (ctx context.Context , blockID ulid.ULID , ix tsdb.IndexReader , ms ... * labels.Matcher ) (index.Postings , error ) {
145
165
return c .fetchPostings (blockID , ix , ms ... )(ctx )
146
166
}
147
167
148
- func (c * BlocksPostingsForMatchersCache ) fetchPostings (blockID ulid.ULID , ix tsdb.IndexReader , ms ... * labels.Matcher ) func (context.Context ) (index.Postings , error ) {
168
+ func (c * blocksPostingsForMatchersCache ) fetchPostings (blockID ulid.ULID , ix tsdb.IndexReader , ms ... * labels.Matcher ) func (context.Context ) (index.Postings , error ) {
149
169
var seed string
150
170
cache := c .blocksCache
151
171
@@ -197,7 +217,7 @@ func (c *BlocksPostingsForMatchersCache) fetchPostings(blockID ulid.ULID, ix tsd
197
217
return c .result (promise )
198
218
}
199
219
200
- func (c * BlocksPostingsForMatchersCache ) result (ce * cacheEntryPromise [[]storage.SeriesRef ]) func (ctx context.Context ) (index.Postings , error ) {
220
+ func (c * blocksPostingsForMatchersCache ) result (ce * cacheEntryPromise [[]storage.SeriesRef ]) func (ctx context.Context ) (index.Postings , error ) {
201
221
return func (ctx context.Context ) (index.Postings , error ) {
202
222
select {
203
223
case <- ctx .Done ():
@@ -211,16 +231,11 @@ func (c *BlocksPostingsForMatchersCache) result(ce *cacheEntryPromise[[]storage.
211
231
}
212
232
}
213
233
214
- func (c * BlocksPostingsForMatchersCache ) getSeedForMetricName (metricName string ) string {
215
- h := MemHashString (metricName )
216
- i := h % uint64 (len (c .headSeedByMetricName ))
217
- l := h % uint64 (len (c .strippedLock ))
218
- c .strippedLock [l ].RLock ()
219
- defer c .strippedLock [l ].RUnlock ()
220
- return strconv .Itoa (c .headSeedByMetricName [i ])
234
+ func (c * blocksPostingsForMatchersCache ) getSeedForMetricName (metricName string ) string {
235
+ return c .seedByHash .getSeed (c .userId , metricName )
221
236
}
222
237
223
- func (c * BlocksPostingsForMatchersCache ) cacheKey (seed string , blockID ulid.ULID , ms ... * labels.Matcher ) string {
238
+ func (c * blocksPostingsForMatchersCache ) cacheKey (seed string , blockID ulid.ULID , ms ... * labels.Matcher ) string {
224
239
slices .SortFunc (ms , func (i , j * labels.Matcher ) int {
225
240
if i .Type != j .Type {
226
241
return int (i .Type - j .Type )
@@ -272,6 +287,36 @@ func metricNameFromMatcher(ms []*labels.Matcher) (string, bool) {
272
287
return "" , false
273
288
}
274
289
290
+ type seedByHash struct {
291
+ strippedLock []sync.RWMutex
292
+ seedByHash []int
293
+ }
294
+
295
+ func newSeedByHash (size int ) * seedByHash {
296
+ return & seedByHash {
297
+ seedByHash : make ([]int , size ),
298
+ strippedLock : make ([]sync.RWMutex , numOfSeedsStripes ),
299
+ }
300
+ }
301
+
302
+ func (s * seedByHash ) getSeed (userId string , v string ) string {
303
+ h := memHashString (userId , v )
304
+ i := h % uint64 (len (s .seedByHash ))
305
+ l := h % uint64 (len (s .strippedLock ))
306
+ s .strippedLock [l ].RLock ()
307
+ defer s .strippedLock [l ].RUnlock ()
308
+ return strconv .Itoa (s .seedByHash [i ])
309
+ }
310
+
311
+ func (s * seedByHash ) incrementSeed (userId string , v string ) {
312
+ h := memHashString (userId , v )
313
+ i := h % uint64 (len (s .seedByHash ))
314
+ l := h % uint64 (len (s .strippedLock ))
315
+ s .strippedLock [l ].Lock ()
316
+ defer s .strippedLock [l ].Unlock ()
317
+ s .seedByHash [i ]++
318
+ }
319
+
275
320
type fifoCache [V any ] struct {
276
321
cfg PostingsCacheConfig
277
322
cachedValues * sync.Map
@@ -425,6 +470,7 @@ func (ce *cacheEntryPromise[V]) isExpired(ttl time.Duration, now time.Time) bool
425
470
return r >= ttl
426
471
}
427
472
428
- func MemHashString (str string ) uint64 {
429
- return xxhash .Sum64 (yoloBuf (str ))
473
+ func memHashString (userId , v string ) uint64 {
474
+ h := fnv1a .HashString64 (userId )
475
+ return fnv1a .AddString64 (h , v )
430
476
}
0 commit comments