Like Prometheus, but for logs.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
loki/pkg/storage/chunk/cache/cache.go

141 lines
4.9 KiB

package cache
import (
"context"
"flag"
"fmt"
"time"
"github.com/pkg/errors"
"github.com/go-kit/log"
"github.com/prometheus/client_golang/prometheus"
"github.com/grafana/loki/v3/pkg/logqlmodel/stats"
)
// Cache byte arrays by key.
type Cache interface {
Store(ctx context.Context, key []string, buf [][]byte) error
Fetch(ctx context.Context, keys []string) (found []string, bufs [][]byte, missing []string, err error)
Stop()
// GetCacheType returns a string indicating the cache "type" for the purpose of grouping cache usage statistics
GetCacheType() stats.CacheType
}
// Config for building Caches.
type Config struct {
DefaultValidity time.Duration `yaml:"default_validity"`
Background BackgroundConfig `yaml:"background"`
Memcache MemcachedConfig `yaml:"memcached"`
MemcacheClient MemcachedClientConfig `yaml:"memcached_client"`
Redis RedisConfig `yaml:"redis"`
EmbeddedCache EmbeddedCacheConfig `yaml:"embedded_cache"`
// This is to name the cache metrics properly.
Prefix string `yaml:"prefix" doc:"hidden"`
// For tests to inject specific implementations.
Cache Cache `yaml:"-"`
}
// RegisterFlagsWithPrefix adds the flags required to config this to the given FlagSet
func (cfg *Config) RegisterFlagsWithPrefix(prefix string, description string, f *flag.FlagSet) {
cfg.Background.RegisterFlagsWithPrefix(prefix, description, f)
cfg.Memcache.RegisterFlagsWithPrefix(prefix, description, f)
cfg.MemcacheClient.RegisterFlagsWithPrefix(prefix, description, f)
cfg.Redis.RegisterFlagsWithPrefix(prefix, description, f)
cfg.EmbeddedCache.RegisterFlagsWithPrefix(prefix+"embedded-cache.", description, f)
f.DurationVar(&cfg.DefaultValidity, prefix+"default-validity", time.Hour, description+"The default validity of entries for caches unless overridden.")
cfg.Prefix = prefix
}
// IsMemcacheSet returns whether a non empty Memcache config is set or not, based on the configured
// host or addresses.
//
// Internally, this function is used to set Memcache as the cache storage to be used.
func IsMemcacheSet(cfg Config) bool {
return cfg.MemcacheClient.Host != "" || cfg.MemcacheClient.Addresses != ""
}
// IsRedisSet returns whether a non empty Redis config is set or not, based on the configured endpoint.
//
// Internally, this function is used to set Redis as the cache storage to be used.
func IsRedisSet(cfg Config) bool {
return cfg.Redis.Endpoint != ""
}
func IsEmbeddedCacheSet(cfg Config) bool {
return cfg.EmbeddedCache.Enabled
}
func IsSpecificImplementationSet(cfg Config) bool {
return cfg.Cache != nil
}
// IsCacheConfigured determines if either of the following caches is configured:
// - memcached
// - redis
// - embedded-cache
// - specific cache implementation
func IsCacheConfigured(cfg Config) bool {
return IsMemcacheSet(cfg) || IsRedisSet(cfg) || IsEmbeddedCacheSet(cfg) || IsSpecificImplementationSet(cfg)
}
// New creates a new Cache using Config.
func New(cfg Config, reg prometheus.Registerer, logger log.Logger, cacheType stats.CacheType, metricsNamespace string) (Cache, error) {
// Have additional check for embeddedcache with distributed mode, because those cache will already be initialized in modules
// but still need stats collector wrapper for it.
if cfg.Cache != nil && !cfg.EmbeddedCache.IsEnabled() {
return cfg.Cache, nil
}
var caches []Cache
if cfg.EmbeddedCache.IsEnabled() {
if cfg.EmbeddedCache.TTL == 0 && cfg.DefaultValidity != 0 {
cfg.EmbeddedCache.TTL = cfg.DefaultValidity
}
if cache := NewEmbeddedCache(cfg.Prefix+"embedded-cache", cfg.EmbeddedCache, reg, logger, cacheType); cache != nil {
caches = append(caches, CollectStats(Instrument(cfg.Prefix+"embedded-cache", cache, reg)))
}
}
if IsMemcacheSet(cfg) && IsRedisSet(cfg) {
return nil, errors.New("use of multiple cache storage systems is not supported")
}
if IsMemcacheSet(cfg) {
if cfg.Memcache.Expiration == 0 && cfg.DefaultValidity != 0 {
cfg.Memcache.Expiration = cfg.DefaultValidity
}
client := NewMemcachedClient(cfg.MemcacheClient, cfg.Prefix, reg, logger, metricsNamespace)
cache := NewMemcached(cfg.Memcache, client, cfg.Prefix, reg, logger, cacheType)
cacheName := cfg.Prefix + "memcache"
caches = append(caches, CollectStats(NewBackground(cacheName, cfg.Background, Instrument(cacheName, cache, reg), reg)))
}
if IsRedisSet(cfg) {
if cfg.Redis.Expiration == 0 && cfg.DefaultValidity != 0 {
cfg.Redis.Expiration = cfg.DefaultValidity
}
cacheName := cfg.Prefix + "redis"
client, err := NewRedisClient(&cfg.Redis)
if err != nil {
return nil, fmt.Errorf("redis client setup failed: %w", err)
}
cache := NewRedisCache(cacheName, client, logger, cacheType)
caches = append(caches, CollectStats(NewBackground(cacheName, cfg.Background, Instrument(cacheName, cache, reg), reg)))
}
cache := NewTiered(caches)
if len(caches) > 1 {
cache = Instrument(cfg.Prefix+"tiered", cache, reg)
}
return cache, nil
}