Skip to content

Commit 67bedbf

Browse files
committed
Add in-memory chunk cache
Signed-off-by: SungJin1212 <[email protected]>
1 parent fbe118b commit 67bedbf

File tree

8 files changed

+179
-14
lines changed

8 files changed

+179
-14
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
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.
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

Lines changed: 8 additions & 1 deletion
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 or inmemory.
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

Lines changed: 8 additions & 1 deletion
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 or inmemory.
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

Lines changed: 8 additions & 1 deletion
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, redis
1343+
# or inmemory.
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

Lines changed: 13 additions & 0 deletions
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

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,17 @@ import (
2424
const (
2525
CacheBackendMemcached = "memcached"
2626
CacheBackendRedis = "redis"
27+
CacheBackendInMemory = "inmemory"
2728
)
2829

29-
type CacheBackend struct {
30+
type MetadataCacheBackend struct {
3031
Backend string `yaml:"backend"`
3132
Memcached MemcachedClientConfig `yaml:"memcached"`
3233
Redis RedisClientConfig `yaml:"redis"`
3334
}
3435

3536
// Validate the config.
36-
func (cfg *CacheBackend) Validate() error {
37+
func (cfg *MetadataCacheBackend) Validate() error {
3738
switch cfg.Backend {
3839
case CacheBackendMemcached:
3940
return cfg.Memcached.Validate()
@@ -46,8 +47,31 @@ func (cfg *CacheBackend) Validate() error {
4647
return nil
4748
}
4849

50+
type ChunkCacheBackend struct {
51+
Backend string `yaml:"backend"`
52+
InMemory InMemoryChunkCacheConfig `yaml:"inmemory"`
53+
Memcached MemcachedClientConfig `yaml:"memcached"`
54+
Redis RedisClientConfig `yaml:"redis"`
55+
}
56+
57+
// Validate the config.
58+
func (cfg *ChunkCacheBackend) Validate() error {
59+
switch cfg.Backend {
60+
case CacheBackendMemcached:
61+
return cfg.Memcached.Validate()
62+
case CacheBackendRedis:
63+
return cfg.Redis.Validate()
64+
case CacheBackendInMemory:
65+
return nil
66+
case "":
67+
default:
68+
return fmt.Errorf("unsupported cache backend: %s", cfg.Backend)
69+
}
70+
return nil
71+
}
72+
4973
type ChunksCacheConfig struct {
50-
CacheBackend `yaml:",inline"`
74+
ChunkCacheBackend `yaml:",inline"`
5175

5276
SubrangeSize int64 `yaml:"subrange_size"`
5377
MaxGetRangeRequests int `yaml:"max_get_range_requests"`
@@ -56,10 +80,11 @@ type ChunksCacheConfig struct {
5680
}
5781

5882
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))
83+
f.StringVar(&cfg.Backend, prefix+"backend", "", fmt.Sprintf("Backend for chunks cache, if not empty. Supported values: %s, %s or %s.", CacheBackendMemcached, CacheBackendRedis, CacheBackendInMemory))
6084

6185
cfg.Memcached.RegisterFlagsWithPrefix(f, prefix+"memcached.")
6286
cfg.Redis.RegisterFlagsWithPrefix(f, prefix+"redis.")
87+
cfg.InMemory.RegisterFlagsWithPrefix(f, prefix+"inmemory.")
6388

6489
f.Int64Var(&cfg.SubrangeSize, prefix+"subrange-size", 16000, "Size of each subrange that bucket object is split into for better caching.")
6590
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 +93,11 @@ func (cfg *ChunksCacheConfig) RegisterFlagsWithPrefix(f *flag.FlagSet, prefix st
6893
}
6994

7095
func (cfg *ChunksCacheConfig) Validate() error {
71-
return cfg.CacheBackend.Validate()
96+
return cfg.ChunkCacheBackend.Validate()
7297
}
7398

7499
type MetadataCacheConfig struct {
75-
CacheBackend `yaml:",inline"`
100+
MetadataCacheBackend `yaml:",inline"`
76101

77102
TenantsListTTL time.Duration `yaml:"tenants_list_ttl"`
78103
TenantBlocksListTTL time.Duration `yaml:"tenant_blocks_list_ttl"`
@@ -107,14 +132,14 @@ func (cfg *MetadataCacheConfig) RegisterFlagsWithPrefix(f *flag.FlagSet, prefix
107132
}
108133

109134
func (cfg *MetadataCacheConfig) Validate() error {
110-
return cfg.CacheBackend.Validate()
135+
return cfg.MetadataCacheBackend.Validate()
111136
}
112137

113138
func CreateCachingBucket(chunksConfig ChunksCacheConfig, metadataConfig MetadataCacheConfig, matchers Matchers, bkt objstore.InstrumentedBucket, logger log.Logger, reg prometheus.Registerer) (objstore.InstrumentedBucket, error) {
114139
cfg := cache.NewCachingBucketConfig()
115140
cachingConfigured := false
116141

117-
chunksCache, err := createCache("chunks-cache", &chunksConfig.CacheBackend, logger, reg)
142+
chunksCache, err := createChunkCache("chunks-cache", &chunksConfig.ChunkCacheBackend, logger, reg)
118143
if err != nil {
119144
return nil, errors.Wrapf(err, "chunks-cache")
120145
}
@@ -124,7 +149,7 @@ func CreateCachingBucket(chunksConfig ChunksCacheConfig, metadataConfig Metadata
124149
cfg.CacheGetRange("chunks", chunksCache, matchers.GetChunksMatcher(), chunksConfig.SubrangeSize, chunksConfig.AttributesTTL, chunksConfig.SubrangeTTL, chunksConfig.MaxGetRangeRequests)
125150
}
126151

127-
metadataCache, err := createCache("metadata-cache", &metadataConfig.CacheBackend, logger, reg)
152+
metadataCache, err := createMetadataCache("metadata-cache", &metadataConfig.MetadataCacheBackend, logger, reg)
128153
if err != nil {
129154
return nil, errors.Wrapf(err, "metadata-cache")
130155
}
@@ -152,12 +177,38 @@ func CreateCachingBucket(chunksConfig ChunksCacheConfig, metadataConfig Metadata
152177
return storecache.NewCachingBucket(bkt, cfg, logger, reg)
153178
}
154179

155-
func createCache(cacheName string, cacheBackend *CacheBackend, logger log.Logger, reg prometheus.Registerer) (cache.Cache, error) {
180+
func createMetadataCache(cacheName string, cacheBackend *MetadataCacheBackend, logger log.Logger, reg prometheus.Registerer) (cache.Cache, error) {
156181
switch cacheBackend.Backend {
157182
case "":
158183
// No caching.
159184
return nil, nil
185+
case CacheBackendMemcached:
186+
var client cacheutil.MemcachedClient
187+
client, err := cacheutil.NewMemcachedClientWithConfig(logger, cacheName, cacheBackend.Memcached.ToMemcachedClientConfig(), reg)
188+
if err != nil {
189+
return nil, errors.Wrapf(err, "failed to create memcached client")
190+
}
191+
return cache.NewMemcachedCache(cacheName, logger, client, reg), nil
192+
193+
case CacheBackendRedis:
194+
redisCache, err := cacheutil.NewRedisClientWithConfig(logger, cacheName, cacheBackend.Redis.ToRedisClientConfig(), reg)
195+
if err != nil {
196+
return nil, errors.Wrapf(err, "failed to create redis client")
197+
}
198+
return cache.NewRedisCache(cacheName, logger, redisCache, reg), nil
160199

200+
default:
201+
return nil, errors.Errorf("unsupported cache type for cache %s: %s", cacheName, cacheBackend.Backend)
202+
}
203+
}
204+
205+
func createChunkCache(cacheName string, cacheBackend *ChunkCacheBackend, logger log.Logger, reg prometheus.Registerer) (cache.Cache, error) {
206+
switch cacheBackend.Backend {
207+
case "":
208+
// No caching.
209+
return nil, nil
210+
case CacheBackendInMemory:
211+
return newInMemoryChunkCache(cacheName, cacheBackend.InMemory, logger, reg)
161212
case CacheBackendMemcached:
162213
var client cacheutil.MemcachedClient
163214
client, err := cacheutil.NewMemcachedClientWithConfig(logger, cacheName, cacheBackend.Memcached.ToMemcachedClientConfig(), reg)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package tsdb
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/go-kit/log"
8+
"github.com/pkg/errors"
9+
"github.com/prometheus/client_golang/prometheus"
10+
"github.com/thanos-io/thanos/pkg/cache"
11+
"github.com/thanos-io/thanos/pkg/model"
12+
13+
"github.com/VictoriaMetrics/fastcache"
14+
)
15+
16+
type InMemoryChunkCache struct {
17+
cache *cache.InMemoryCache
18+
fcache *fastcache.Cache
19+
name string
20+
}
21+
22+
func newInMemoryChunkCache(name string, cfg InMemoryChunkCacheConfig, logger log.Logger, reg prometheus.Registerer) (*InMemoryChunkCache, error) {
23+
maxCacheSize := model.Bytes(cfg.MaxSizeBytes)
24+
25+
// Calculate the max item size.
26+
maxItemSize := defaultMaxItemSize
27+
if maxItemSize > maxCacheSize {
28+
maxItemSize = maxCacheSize
29+
}
30+
31+
config := cache.InMemoryCacheConfig{
32+
MaxSize: maxCacheSize,
33+
MaxItemSize: maxItemSize,
34+
}
35+
36+
inMemoryCache, err := cache.NewInMemoryCacheWithConfig(name, logger, reg, config)
37+
if err != nil {
38+
return nil, errors.Wrap(err, "create in-memory chunk cache")
39+
}
40+
41+
fcache := fastcache.New(int(config.MaxSize))
42+
43+
inMemoryChunkCache := &InMemoryChunkCache{
44+
cache: inMemoryCache,
45+
fcache: fcache,
46+
name: name,
47+
}
48+
49+
return inMemoryChunkCache, nil
50+
}
51+
52+
func (c *InMemoryChunkCache) Store(data map[string][]byte, ttl time.Duration) {
53+
c.cache.Store(data, ttl)
54+
}
55+
56+
// Fetch fetches multiple keys and returns a map containing cache hits
57+
// In case of error, it logs and return an empty cache hits map.
58+
func (c *InMemoryChunkCache) Fetch(ctx context.Context, keys []string) map[string][]byte {
59+
return c.cache.Fetch(ctx, keys)
60+
}
61+
62+
func (c *InMemoryChunkCache) Name() string {
63+
return c.name
64+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package tsdb
2+
3+
import (
4+
"flag"
5+
6+
"github.com/alecthomas/units"
7+
)
8+
9+
type InMemoryChunkCacheConfig struct {
10+
MaxSizeBytes uint64 `yaml:"max_size_bytes"`
11+
}
12+
13+
func (cfg *InMemoryChunkCacheConfig) RegisterFlagsWithPrefix(f *flag.FlagSet, prefix string) {
14+
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).")
15+
}

0 commit comments

Comments
 (0)