Skip to content

Commit 53556fe

Browse files
authored
Add in-memory chunk cache (#6245)
Signed-off-by: SungJin1212 <[email protected]>
1 parent 6a7f44c commit 53556fe

File tree

7 files changed

+182
-14
lines changed

7 files changed

+182
-14
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
## master / unreleased
44

5-
* [FEATURE] Query Frontend/Querier: Add protobuf codec `-api.querier-default-codec` and the option to choose response compression type `-querier.response-compression`. #5527
65
* [CHANGE] Enable Compactor and Alertmanager in target all. #6204
6+
* [FEATURE] Query Frontend/Querier: Add protobuf codec `-api.querier-default-codec` and the option to choose response compression type `-querier.response-compression`. #5527
77
* [FEATURE] Ruler: Experimental: Add `ruler.frontend-address` to allow query to query frontends instead of ingesters. #6151
88
* [FEATURE] Ruler: Minimize chances of missed rule group evaluations that can occur due to OOM kills, bad underlying nodes, or due to an unhealthy ruler that appears in the ring as healthy. This feature is enabled via `-ruler.enable-ha-evaluation` flag. #6129
9+
* [FEATURE] Store Gateway: Add an in-memory chunk cache. #6245
910
* [ENHANCEMENT] Ingester: Add `blocks-storage.tsdb.wal-compression-type` to support zstd wal compression type. #6232
1011
* [ENHANCEMENT] Query Frontend: Add info field to query response. #6207
1112
* [ENHANCEMENT] Query Frontend: Add peakSample in query stats response. #6188

docs/blocks-storage/querier.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -788,10 +788,17 @@ blocks_storage:
788788
[max_backfill_items: <int> | default = 10000]
789789

790790
chunks_cache:
791-
# Backend for chunks cache, if not empty. Supported values: memcached.
791+
# Backend for chunks cache, if not empty. Supported values: memcached,
792+
# redis, inmemory, and '' (disable).
792793
# CLI flag: -blocks-storage.bucket-store.chunks-cache.backend
793794
[backend: <string> | default = ""]
794795

796+
inmemory:
797+
# Maximum size in bytes of in-memory chunk cache used to speed up chunk
798+
# lookups (shared between all tenants).
799+
# CLI flag: -blocks-storage.bucket-store.chunks-cache.inmemory.max-size-bytes
800+
[max_size_bytes: <int> | default = 1073741824]
801+
795802
memcached:
796803
# Comma separated list of memcached addresses. Supported prefixes are:
797804
# dns+ (looked up as an A/AAAA query), dnssrv+ (looked up as a SRV

docs/blocks-storage/store-gateway.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -903,10 +903,17 @@ blocks_storage:
903903
[max_backfill_items: <int> | default = 10000]
904904

905905
chunks_cache:
906-
# Backend for chunks cache, if not empty. Supported values: memcached.
906+
# Backend for chunks cache, if not empty. Supported values: memcached,
907+
# redis, inmemory, and '' (disable).
907908
# CLI flag: -blocks-storage.bucket-store.chunks-cache.backend
908909
[backend: <string> | default = ""]
909910

911+
inmemory:
912+
# Maximum size in bytes of in-memory chunk cache used to speed up chunk
913+
# lookups (shared between all tenants).
914+
# CLI flag: -blocks-storage.bucket-store.chunks-cache.inmemory.max-size-bytes
915+
[max_size_bytes: <int> | default = 1073741824]
916+
910917
memcached:
911918
# Comma separated list of memcached addresses. Supported prefixes are:
912919
# dns+ (looked up as an A/AAAA query), dnssrv+ (looked up as a SRV

docs/configuration/config-file-reference.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -1339,10 +1339,17 @@ bucket_store:
13391339
[max_backfill_items: <int> | default = 10000]
13401340

13411341
chunks_cache:
1342-
# Backend for chunks cache, if not empty. Supported values: memcached.
1342+
# Backend for chunks cache, if not empty. Supported values: memcached,
1343+
# redis, inmemory, and '' (disable).
13431344
# CLI flag: -blocks-storage.bucket-store.chunks-cache.backend
13441345
[backend: <string> | default = ""]
13451346

1347+
inmemory:
1348+
# Maximum size in bytes of in-memory chunk cache used to speed up chunk
1349+
# lookups (shared between all tenants).
1350+
# CLI flag: -blocks-storage.bucket-store.chunks-cache.inmemory.max-size-bytes
1351+
[max_size_bytes: <int> | default = 1073741824]
1352+
13461353
memcached:
13471354
# Comma separated list of memcached addresses. Supported prefixes are:
13481355
# dns+ (looked up as an A/AAAA query), dnssrv+ (looked up as a SRV query,

integration/querier_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,19 @@ func TestQuerierWithBlocksStorageRunningInMicroservicesMode(t *testing.T) {
9797
chunkCacheBackend: tsdb.CacheBackendRedis,
9898
bucketIndexEnabled: true,
9999
},
100+
"blocks default sharding, in-memory chunk cache": {
101+
blocksShardingStrategy: "default",
102+
indexCacheBackend: tsdb.IndexCacheBackendRedis,
103+
chunkCacheBackend: tsdb.CacheBackendInMemory,
104+
bucketIndexEnabled: true,
105+
},
106+
"blocks shuffle sharding, in-memory chunk cache": {
107+
blocksShardingStrategy: "shuffle-sharding",
108+
tenantShardSize: 1,
109+
indexCacheBackend: tsdb.IndexCacheBackendRedis,
110+
chunkCacheBackend: tsdb.CacheBackendInMemory,
111+
bucketIndexEnabled: true,
112+
},
100113
}
101114

102115
for testName, testCfg := range tests {

pkg/storage/tsdb/caching_bucket.go

+92-10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99
"time"
1010

11+
"github.com/alecthomas/units"
1112
"github.com/go-kit/log"
1213
"github.com/golang/snappy"
1314
"github.com/oklog/ulid"
@@ -18,22 +19,28 @@ import (
1819
"github.com/thanos-io/thanos/pkg/block/metadata"
1920
"github.com/thanos-io/thanos/pkg/cache"
2021
"github.com/thanos-io/thanos/pkg/cacheutil"
22+
"github.com/thanos-io/thanos/pkg/model"
2123
storecache "github.com/thanos-io/thanos/pkg/store/cache"
2224
)
2325

26+
var (
27+
errUnsupportedChunkCacheBackend = errors.New("unsupported chunk cache backend")
28+
)
29+
2430
const (
2531
CacheBackendMemcached = "memcached"
2632
CacheBackendRedis = "redis"
33+
CacheBackendInMemory = "inmemory"
2734
)
2835

29-
type CacheBackend struct {
36+
type MetadataCacheBackend struct {
3037
Backend string `yaml:"backend"`
3138
Memcached MemcachedClientConfig `yaml:"memcached"`
3239
Redis RedisClientConfig `yaml:"redis"`
3340
}
3441

3542
// Validate the config.
36-
func (cfg *CacheBackend) Validate() error {
43+
func (cfg *MetadataCacheBackend) Validate() error {
3744
switch cfg.Backend {
3845
case CacheBackendMemcached:
3946
return cfg.Memcached.Validate()
@@ -46,8 +53,29 @@ func (cfg *CacheBackend) Validate() error {
4653
return nil
4754
}
4855

56+
type ChunkCacheBackend struct {
57+
Backend string `yaml:"backend"`
58+
InMemory InMemoryChunkCacheConfig `yaml:"inmemory"`
59+
Memcached MemcachedClientConfig `yaml:"memcached"`
60+
Redis RedisClientConfig `yaml:"redis"`
61+
}
62+
63+
// Validate the config.
64+
func (cfg *ChunkCacheBackend) Validate() error {
65+
switch cfg.Backend {
66+
case CacheBackendMemcached:
67+
return cfg.Memcached.Validate()
68+
case CacheBackendRedis:
69+
return cfg.Redis.Validate()
70+
case CacheBackendInMemory, "":
71+
default:
72+
return errUnsupportedChunkCacheBackend
73+
}
74+
return nil
75+
}
76+
4977
type ChunksCacheConfig struct {
50-
CacheBackend `yaml:",inline"`
78+
ChunkCacheBackend `yaml:",inline"`
5179

5280
SubrangeSize int64 `yaml:"subrange_size"`
5381
MaxGetRangeRequests int `yaml:"max_get_range_requests"`
@@ -56,10 +84,11 @@ type ChunksCacheConfig struct {
5684
}
5785

5886
func (cfg *ChunksCacheConfig) RegisterFlagsWithPrefix(f *flag.FlagSet, prefix string) {
59-
f.StringVar(&cfg.Backend, prefix+"backend", "", fmt.Sprintf("Backend for chunks cache, if not empty. Supported values: %s.", CacheBackendMemcached))
87+
f.StringVar(&cfg.Backend, prefix+"backend", "", fmt.Sprintf("Backend for chunks cache, if not empty. Supported values: %s, %s, %s, and '' (disable).", CacheBackendMemcached, CacheBackendRedis, CacheBackendInMemory))
6088

6189
cfg.Memcached.RegisterFlagsWithPrefix(f, prefix+"memcached.")
6290
cfg.Redis.RegisterFlagsWithPrefix(f, prefix+"redis.")
91+
cfg.InMemory.RegisterFlagsWithPrefix(f, prefix+"inmemory.")
6392

6493
f.Int64Var(&cfg.SubrangeSize, prefix+"subrange-size", 16000, "Size of each subrange that bucket object is split into for better caching.")
6594
f.IntVar(&cfg.MaxGetRangeRequests, prefix+"max-get-range-requests", 3, "Maximum number of sub-GetRange requests that a single GetRange request can be split into when fetching chunks. Zero or negative value = unlimited number of sub-requests.")
@@ -68,11 +97,34 @@ func (cfg *ChunksCacheConfig) RegisterFlagsWithPrefix(f *flag.FlagSet, prefix st
6897
}
6998

7099
func (cfg *ChunksCacheConfig) Validate() error {
71-
return cfg.CacheBackend.Validate()
100+
return cfg.ChunkCacheBackend.Validate()
101+
}
102+
103+
type InMemoryChunkCacheConfig struct {
104+
MaxSizeBytes uint64 `yaml:"max_size_bytes"`
105+
}
106+
107+
func (cfg *InMemoryChunkCacheConfig) RegisterFlagsWithPrefix(f *flag.FlagSet, prefix string) {
108+
f.Uint64Var(&cfg.MaxSizeBytes, prefix+"max-size-bytes", uint64(1*units.Gibibyte), "Maximum size in bytes of in-memory chunk cache used to speed up chunk lookups (shared between all tenants).")
109+
}
110+
111+
func (cfg *InMemoryChunkCacheConfig) toInMemoryChunkCacheConfig() cache.InMemoryCacheConfig {
112+
maxCacheSize := model.Bytes(cfg.MaxSizeBytes)
113+
114+
// Calculate the max item size.
115+
maxItemSize := defaultMaxItemSize
116+
if maxItemSize > maxCacheSize {
117+
maxItemSize = maxCacheSize
118+
}
119+
120+
return cache.InMemoryCacheConfig{
121+
MaxSize: maxCacheSize,
122+
MaxItemSize: maxItemSize,
123+
}
72124
}
73125

74126
type MetadataCacheConfig struct {
75-
CacheBackend `yaml:",inline"`
127+
MetadataCacheBackend `yaml:",inline"`
76128

77129
TenantsListTTL time.Duration `yaml:"tenants_list_ttl"`
78130
TenantBlocksListTTL time.Duration `yaml:"tenant_blocks_list_ttl"`
@@ -107,14 +159,14 @@ func (cfg *MetadataCacheConfig) RegisterFlagsWithPrefix(f *flag.FlagSet, prefix
107159
}
108160

109161
func (cfg *MetadataCacheConfig) Validate() error {
110-
return cfg.CacheBackend.Validate()
162+
return cfg.MetadataCacheBackend.Validate()
111163
}
112164

113165
func CreateCachingBucket(chunksConfig ChunksCacheConfig, metadataConfig MetadataCacheConfig, matchers Matchers, bkt objstore.InstrumentedBucket, logger log.Logger, reg prometheus.Registerer) (objstore.InstrumentedBucket, error) {
114166
cfg := cache.NewCachingBucketConfig()
115167
cachingConfigured := false
116168

117-
chunksCache, err := createCache("chunks-cache", &chunksConfig.CacheBackend, logger, reg)
169+
chunksCache, err := createChunkCache("chunks-cache", &chunksConfig.ChunkCacheBackend, logger, reg)
118170
if err != nil {
119171
return nil, errors.Wrapf(err, "chunks-cache")
120172
}
@@ -124,7 +176,7 @@ func CreateCachingBucket(chunksConfig ChunksCacheConfig, metadataConfig Metadata
124176
cfg.CacheGetRange("chunks", chunksCache, matchers.GetChunksMatcher(), chunksConfig.SubrangeSize, chunksConfig.AttributesTTL, chunksConfig.SubrangeTTL, chunksConfig.MaxGetRangeRequests)
125177
}
126178

127-
metadataCache, err := createCache("metadata-cache", &metadataConfig.CacheBackend, logger, reg)
179+
metadataCache, err := createMetadataCache("metadata-cache", &metadataConfig.MetadataCacheBackend, logger, reg)
128180
if err != nil {
129181
return nil, errors.Wrapf(err, "metadata-cache")
130182
}
@@ -152,12 +204,42 @@ func CreateCachingBucket(chunksConfig ChunksCacheConfig, metadataConfig Metadata
152204
return storecache.NewCachingBucket(bkt, cfg, logger, reg)
153205
}
154206

155-
func createCache(cacheName string, cacheBackend *CacheBackend, logger log.Logger, reg prometheus.Registerer) (cache.Cache, error) {
207+
func createMetadataCache(cacheName string, cacheBackend *MetadataCacheBackend, logger log.Logger, reg prometheus.Registerer) (cache.Cache, error) {
156208
switch cacheBackend.Backend {
157209
case "":
158210
// No caching.
159211
return nil, nil
212+
case CacheBackendMemcached:
213+
var client cacheutil.MemcachedClient
214+
client, err := cacheutil.NewMemcachedClientWithConfig(logger, cacheName, cacheBackend.Memcached.ToMemcachedClientConfig(), reg)
215+
if err != nil {
216+
return nil, errors.Wrapf(err, "failed to create memcached client")
217+
}
218+
return cache.NewMemcachedCache(cacheName, logger, client, reg), nil
160219

220+
case CacheBackendRedis:
221+
redisCache, err := cacheutil.NewRedisClientWithConfig(logger, cacheName, cacheBackend.Redis.ToRedisClientConfig(), reg)
222+
if err != nil {
223+
return nil, errors.Wrapf(err, "failed to create redis client")
224+
}
225+
return cache.NewRedisCache(cacheName, logger, redisCache, reg), nil
226+
227+
default:
228+
return nil, errors.Errorf("unsupported cache type for cache %s: %s", cacheName, cacheBackend.Backend)
229+
}
230+
}
231+
232+
func createChunkCache(cacheName string, cacheBackend *ChunkCacheBackend, logger log.Logger, reg prometheus.Registerer) (cache.Cache, error) {
233+
switch cacheBackend.Backend {
234+
case "":
235+
// No caching.
236+
return nil, nil
237+
case CacheBackendInMemory:
238+
inMemoryCache, err := cache.NewInMemoryCacheWithConfig(cacheName, logger, reg, cacheBackend.InMemory.toInMemoryChunkCacheConfig())
239+
if err != nil {
240+
return nil, errors.Wrapf(err, "failed to create in-memory chunk cache")
241+
}
242+
return inMemoryCache, nil
161243
case CacheBackendMemcached:
162244
var client cacheutil.MemcachedClient
163245
client, err := cacheutil.NewMemcachedClientWithConfig(logger, cacheName, cacheBackend.Memcached.ToMemcachedClientConfig(), reg)

pkg/storage/tsdb/caching_bucket_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,57 @@ import (
88
"github.com/stretchr/testify/assert"
99
)
1010

11+
func Test_ChunkCacheBackendValidation(t *testing.T) {
12+
tests := map[string]struct {
13+
cfg ChunkCacheBackend
14+
expectedErr error
15+
}{
16+
"valid chunk cache type ('')": {
17+
cfg: ChunkCacheBackend{
18+
Backend: "",
19+
},
20+
expectedErr: nil,
21+
},
22+
"valid chunk cache type (in-memory)": {
23+
cfg: ChunkCacheBackend{
24+
Backend: CacheBackendInMemory,
25+
},
26+
expectedErr: nil,
27+
},
28+
"valid chunk cache type (memcached)": {
29+
cfg: ChunkCacheBackend{
30+
Backend: CacheBackendMemcached,
31+
Memcached: MemcachedClientConfig{
32+
Addresses: "dns+localhost:11211",
33+
},
34+
},
35+
expectedErr: nil,
36+
},
37+
"valid chunk cache type (redis)": {
38+
cfg: ChunkCacheBackend{
39+
Backend: CacheBackendRedis,
40+
Redis: RedisClientConfig{
41+
Addresses: "localhost:6379",
42+
},
43+
},
44+
expectedErr: nil,
45+
},
46+
"invalid chunk cache type": {
47+
cfg: ChunkCacheBackend{
48+
Backend: "dummy",
49+
},
50+
expectedErr: errUnsupportedChunkCacheBackend,
51+
},
52+
}
53+
54+
for name, tc := range tests {
55+
t.Run(name, func(t *testing.T) {
56+
err := tc.cfg.Validate()
57+
assert.Equal(t, tc.expectedErr, err)
58+
})
59+
}
60+
}
61+
1162
func TestIsTenantDir(t *testing.T) {
1263
assert.False(t, isTenantBlocksDir(""))
1364
assert.True(t, isTenantBlocksDir("test"))

0 commit comments

Comments
 (0)