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/querier/queryrange/labels_cache.go

126 lines
4.2 KiB

package queryrange
import (
"context"
"flag"
"fmt"
"time"
"github.com/go-kit/log"
"github.com/grafana/loki/pkg/querier/queryrange/queryrangebase"
"github.com/grafana/loki/pkg/storage/chunk/cache"
"github.com/grafana/loki/pkg/storage/chunk/cache/resultscache"
"github.com/grafana/loki/pkg/util/validation"
)
type cacheKeyLabels struct {
Limits
transformer UserIDTransformer
}
// metadataSplitIntervalForTimeRange returns split interval for series and label requests.
// If `recent_metadata_query_window` is configured and the query start interval is within this window,
// it returns `split_recent_metadata_queries_by_interval`.
// For other cases, the default split interval of `split_metadata_queries_by_interval` will be used.
func metadataSplitIntervalForTimeRange(limits Limits, tenantIDs []string, ref, start time.Time) time.Duration {
split := validation.MaxDurationOrZeroPerTenant(tenantIDs, limits.MetadataQuerySplitDuration)
recentMetadataQueryWindow := validation.MaxDurationOrZeroPerTenant(tenantIDs, limits.RecentMetadataQueryWindow)
recentMetadataQuerySplitInterval := validation.MaxDurationOrZeroPerTenant(tenantIDs, limits.RecentMetadataQuerySplitDuration)
// if either of the options are not configured, use the default metadata split interval
if recentMetadataQueryWindow == 0 || recentMetadataQuerySplitInterval == 0 {
return split
}
// if the query start is not before window start, it would be split using recentMetadataQuerySplitInterval
if windowStart := ref.Add(-recentMetadataQueryWindow); !start.Before(windowStart) {
split = recentMetadataQuerySplitInterval
}
return split
}
// GenerateCacheKey generates a cache key based on the userID, split duration and the interval of the request.
// It also includes the label name and the provided query for label values request.
func (i cacheKeyLabels) GenerateCacheKey(ctx context.Context, userID string, r resultscache.Request) string {
lr := r.(*LabelRequest)
split := metadataSplitIntervalForTimeRange(i.Limits, []string{userID}, time.Now().UTC(), r.GetStart().UTC())
var currentInterval int64
if denominator := int64(split / time.Millisecond); denominator > 0 {
currentInterval = lr.GetStart().UnixMilli() / denominator
}
if i.transformer != nil {
userID = i.transformer(ctx, userID)
}
if lr.GetValues() {
return fmt.Sprintf("labelvalues:%s:%s:%s:%d:%d", userID, lr.GetName(), lr.GetQuery(), currentInterval, split)
}
return fmt.Sprintf("labels:%s:%d:%d", userID, currentInterval, split)
}
type labelsExtractor struct{}
// Extract extracts the labels response for the specific time range.
// It is a no-op since it is not possible to partition the labels data by time range as it is just a slice of strings.
func (p labelsExtractor) Extract(_, _ int64, res resultscache.Response, _, _ int64) resultscache.Response {
return res
}
func (p labelsExtractor) ResponseWithoutHeaders(resp queryrangebase.Response) queryrangebase.Response {
labelsResp := resp.(*LokiLabelNamesResponse)
return &LokiLabelNamesResponse{
Status: labelsResp.Status,
Data: labelsResp.Data,
Version: labelsResp.Version,
Statistics: labelsResp.Statistics,
}
}
type LabelsCacheConfig struct {
queryrangebase.ResultsCacheConfig `yaml:",inline"`
}
// RegisterFlags registers flags.
func (cfg *LabelsCacheConfig) RegisterFlags(f *flag.FlagSet) {
cfg.RegisterFlagsWithPrefix(f, "frontend.label-results-cache.")
}
func (cfg *LabelsCacheConfig) Validate() error {
return cfg.ResultsCacheConfig.Validate()
}
func NewLabelsCacheMiddleware(
logger log.Logger,
limits Limits,
merger queryrangebase.Merger,
c cache.Cache,
cacheGenNumberLoader queryrangebase.CacheGenNumberLoader,
shouldCache queryrangebase.ShouldCacheFn,
parallelismForReq queryrangebase.ParallelismForReqFn,
retentionEnabled bool,
transformer UserIDTransformer,
metrics *queryrangebase.ResultsCacheMetrics,
) (queryrangebase.Middleware, error) {
return queryrangebase.NewResultsCacheMiddleware(
logger,
c,
cacheKeyLabels{limits, transformer},
limits,
merger,
labelsExtractor{},
cacheGenNumberLoader,
func(ctx context.Context, r queryrangebase.Request) bool {
return shouldCacheMetadataReq(ctx, logger, shouldCache, r, limits)
},
parallelismForReq,
retentionEnabled,
true,
metrics,
)
}