|
|
|
@ -19,7 +19,6 @@ import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"math"
|
|
|
|
|
"slices"
|
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
|
|
"github.com/prometheus/prometheus/model/histogram"
|
|
|
|
@ -136,13 +135,17 @@ func filterChunkQueriers(qs []ChunkQuerier) []ChunkQuerier {
|
|
|
|
|
// Select returns a set of series that matches the given label matchers.
|
|
|
|
|
func (q *mergeGenericQuerier) Select(ctx context.Context, sortSeries bool, hints *SelectHints, matchers ...*labels.Matcher) genericSeriesSet {
|
|
|
|
|
seriesSets := make([]genericSeriesSet, 0, len(q.queriers))
|
|
|
|
|
var limit int
|
|
|
|
|
if hints != nil {
|
|
|
|
|
limit = hints.Limit
|
|
|
|
|
}
|
|
|
|
|
if !q.concurrentSelect {
|
|
|
|
|
for _, querier := range q.queriers {
|
|
|
|
|
// We need to sort for merge to work.
|
|
|
|
|
seriesSets = append(seriesSets, querier.Select(ctx, true, hints, matchers...))
|
|
|
|
|
}
|
|
|
|
|
return &lazyGenericSeriesSet{init: func() (genericSeriesSet, bool) {
|
|
|
|
|
s := newGenericMergeSeriesSet(seriesSets, q.mergeFn)
|
|
|
|
|
s := newGenericMergeSeriesSet(seriesSets, limit, q.mergeFn)
|
|
|
|
|
return s, s.Next()
|
|
|
|
|
}}
|
|
|
|
|
}
|
|
|
|
@ -175,7 +178,7 @@ func (q *mergeGenericQuerier) Select(ctx context.Context, sortSeries bool, hints
|
|
|
|
|
seriesSets = append(seriesSets, r)
|
|
|
|
|
}
|
|
|
|
|
return &lazyGenericSeriesSet{init: func() (genericSeriesSet, bool) {
|
|
|
|
|
s := newGenericMergeSeriesSet(seriesSets, q.mergeFn)
|
|
|
|
|
s := newGenericMergeSeriesSet(seriesSets, limit, q.mergeFn)
|
|
|
|
|
return s, s.Next()
|
|
|
|
|
}}
|
|
|
|
|
}
|
|
|
|
@ -193,35 +196,44 @@ func (l labelGenericQueriers) SplitByHalf() (labelGenericQueriers, labelGenericQ
|
|
|
|
|
// If matchers are specified the returned result set is reduced
|
|
|
|
|
// to label values of metrics matching the matchers.
|
|
|
|
|
func (q *mergeGenericQuerier) LabelValues(ctx context.Context, name string, hints *LabelHints, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) {
|
|
|
|
|
res, ws, err := q.lvals(ctx, q.queriers, name, hints, matchers...)
|
|
|
|
|
res, ws, err := q.mergeResults(q.queriers, hints, func(q LabelQuerier) ([]string, annotations.Annotations, error) {
|
|
|
|
|
return q.LabelValues(ctx, name, hints, matchers...)
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, fmt.Errorf("LabelValues() from merge generic querier for label %s: %w", name, err)
|
|
|
|
|
}
|
|
|
|
|
return res, ws, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// lvals performs merge sort for LabelValues from multiple queriers.
|
|
|
|
|
func (q *mergeGenericQuerier) lvals(ctx context.Context, lq labelGenericQueriers, n string, hints *LabelHints, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) {
|
|
|
|
|
// mergeResults performs merge sort on the results of invoking the resultsFn against multiple queriers.
|
|
|
|
|
func (q *mergeGenericQuerier) mergeResults(lq labelGenericQueriers, hints *LabelHints, resultsFn func(q LabelQuerier) ([]string, annotations.Annotations, error)) ([]string, annotations.Annotations, error) {
|
|
|
|
|
if lq.Len() == 0 {
|
|
|
|
|
return nil, nil, nil
|
|
|
|
|
}
|
|
|
|
|
if lq.Len() == 1 {
|
|
|
|
|
return lq.Get(0).LabelValues(ctx, n, hints, matchers...)
|
|
|
|
|
return resultsFn(lq.Get(0))
|
|
|
|
|
}
|
|
|
|
|
a, b := lq.SplitByHalf()
|
|
|
|
|
|
|
|
|
|
var ws annotations.Annotations
|
|
|
|
|
s1, w, err := q.lvals(ctx, a, n, hints, matchers...)
|
|
|
|
|
s1, w, err := q.mergeResults(a, hints, resultsFn)
|
|
|
|
|
ws.Merge(w)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, ws, err
|
|
|
|
|
}
|
|
|
|
|
s2, ws, err := q.lvals(ctx, b, n, hints, matchers...)
|
|
|
|
|
s2, w, err := q.mergeResults(b, hints, resultsFn)
|
|
|
|
|
ws.Merge(w)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, ws, err
|
|
|
|
|
}
|
|
|
|
|
return mergeStrings(s1, s2), ws, nil
|
|
|
|
|
|
|
|
|
|
s1 = truncateToLimit(s1, hints)
|
|
|
|
|
s2 = truncateToLimit(s2, hints)
|
|
|
|
|
|
|
|
|
|
merged := mergeStrings(s1, s2)
|
|
|
|
|
merged = truncateToLimit(merged, hints)
|
|
|
|
|
|
|
|
|
|
return merged, ws, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mergeStrings(a, b []string) []string {
|
|
|
|
@ -253,33 +265,13 @@ func mergeStrings(a, b []string) []string {
|
|
|
|
|
|
|
|
|
|
// LabelNames returns all the unique label names present in all queriers in sorted order.
|
|
|
|
|
func (q *mergeGenericQuerier) LabelNames(ctx context.Context, hints *LabelHints, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) {
|
|
|
|
|
var (
|
|
|
|
|
labelNamesMap = make(map[string]struct{})
|
|
|
|
|
warnings annotations.Annotations
|
|
|
|
|
)
|
|
|
|
|
for _, querier := range q.queriers {
|
|
|
|
|
names, wrn, err := querier.LabelNames(ctx, hints, matchers...)
|
|
|
|
|
if wrn != nil {
|
|
|
|
|
// TODO(bwplotka): We could potentially wrap warnings.
|
|
|
|
|
warnings.Merge(wrn)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, fmt.Errorf("LabelNames() from merge generic querier: %w", err)
|
|
|
|
|
}
|
|
|
|
|
for _, name := range names {
|
|
|
|
|
labelNamesMap[name] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(labelNamesMap) == 0 {
|
|
|
|
|
return nil, warnings, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
labelNames := make([]string, 0, len(labelNamesMap))
|
|
|
|
|
for name := range labelNamesMap {
|
|
|
|
|
labelNames = append(labelNames, name)
|
|
|
|
|
res, ws, err := q.mergeResults(q.queriers, hints, func(q LabelQuerier) ([]string, annotations.Annotations, error) {
|
|
|
|
|
return q.LabelNames(ctx, hints, matchers...)
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, fmt.Errorf("LabelNames() from merge generic querier: %w", err)
|
|
|
|
|
}
|
|
|
|
|
slices.Sort(labelNames)
|
|
|
|
|
return labelNames, warnings, nil
|
|
|
|
|
return res, ws, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close releases the resources of the generic querier.
|
|
|
|
@ -293,17 +285,25 @@ func (q *mergeGenericQuerier) Close() error {
|
|
|
|
|
return errs.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func truncateToLimit(s []string, hints *LabelHints) []string {
|
|
|
|
|
if hints != nil && hints.Limit > 0 && len(s) > hints.Limit {
|
|
|
|
|
s = s[:hints.Limit]
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VerticalSeriesMergeFunc returns merged series implementation that merges series with same labels together.
|
|
|
|
|
// It has to handle time-overlapped series as well.
|
|
|
|
|
type VerticalSeriesMergeFunc func(...Series) Series
|
|
|
|
|
|
|
|
|
|
// NewMergeSeriesSet returns a new SeriesSet that merges many SeriesSets together.
|
|
|
|
|
func NewMergeSeriesSet(sets []SeriesSet, mergeFunc VerticalSeriesMergeFunc) SeriesSet {
|
|
|
|
|
// If limit is set, the SeriesSet will be limited up-to the limit. 0 means disabled.
|
|
|
|
|
func NewMergeSeriesSet(sets []SeriesSet, limit int, mergeFunc VerticalSeriesMergeFunc) SeriesSet {
|
|
|
|
|
genericSets := make([]genericSeriesSet, 0, len(sets))
|
|
|
|
|
for _, s := range sets {
|
|
|
|
|
genericSets = append(genericSets, &genericSeriesSetAdapter{s})
|
|
|
|
|
}
|
|
|
|
|
return &seriesSetAdapter{newGenericMergeSeriesSet(genericSets, (&seriesMergerAdapter{VerticalSeriesMergeFunc: mergeFunc}).Merge)}
|
|
|
|
|
return &seriesSetAdapter{newGenericMergeSeriesSet(genericSets, limit, (&seriesMergerAdapter{VerticalSeriesMergeFunc: mergeFunc}).Merge)}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VerticalChunkSeriesMergeFunc returns merged chunk series implementation that merges potentially time-overlapping
|
|
|
|
@ -313,12 +313,12 @@ func NewMergeSeriesSet(sets []SeriesSet, mergeFunc VerticalSeriesMergeFunc) Seri
|
|
|
|
|
type VerticalChunkSeriesMergeFunc func(...ChunkSeries) ChunkSeries
|
|
|
|
|
|
|
|
|
|
// NewMergeChunkSeriesSet returns a new ChunkSeriesSet that merges many SeriesSet together.
|
|
|
|
|
func NewMergeChunkSeriesSet(sets []ChunkSeriesSet, mergeFunc VerticalChunkSeriesMergeFunc) ChunkSeriesSet {
|
|
|
|
|
func NewMergeChunkSeriesSet(sets []ChunkSeriesSet, limit int, mergeFunc VerticalChunkSeriesMergeFunc) ChunkSeriesSet {
|
|
|
|
|
genericSets := make([]genericSeriesSet, 0, len(sets))
|
|
|
|
|
for _, s := range sets {
|
|
|
|
|
genericSets = append(genericSets, &genericChunkSeriesSetAdapter{s})
|
|
|
|
|
}
|
|
|
|
|
return &chunkSeriesSetAdapter{newGenericMergeSeriesSet(genericSets, (&chunkSeriesMergerAdapter{VerticalChunkSeriesMergeFunc: mergeFunc}).Merge)}
|
|
|
|
|
return &chunkSeriesSetAdapter{newGenericMergeSeriesSet(genericSets, limit, (&chunkSeriesMergerAdapter{VerticalChunkSeriesMergeFunc: mergeFunc}).Merge)}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// genericMergeSeriesSet implements genericSeriesSet.
|
|
|
|
@ -326,9 +326,11 @@ type genericMergeSeriesSet struct {
|
|
|
|
|
currentLabels labels.Labels
|
|
|
|
|
mergeFunc genericSeriesMergeFunc
|
|
|
|
|
|
|
|
|
|
heap genericSeriesSetHeap
|
|
|
|
|
sets []genericSeriesSet
|
|
|
|
|
currentSets []genericSeriesSet
|
|
|
|
|
heap genericSeriesSetHeap
|
|
|
|
|
sets []genericSeriesSet
|
|
|
|
|
currentSets []genericSeriesSet
|
|
|
|
|
seriesLimit int
|
|
|
|
|
mergedSeries int // tracks the total number of series merged and returned.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// newGenericMergeSeriesSet returns a new genericSeriesSet that merges (and deduplicates)
|
|
|
|
@ -336,7 +338,8 @@ type genericMergeSeriesSet struct {
|
|
|
|
|
// Each series set must return its series in labels order, otherwise
|
|
|
|
|
// merged series set will be incorrect.
|
|
|
|
|
// Overlapped situations are merged using provided mergeFunc.
|
|
|
|
|
func newGenericMergeSeriesSet(sets []genericSeriesSet, mergeFunc genericSeriesMergeFunc) genericSeriesSet {
|
|
|
|
|
// If seriesLimit is set, only limited series are returned.
|
|
|
|
|
func newGenericMergeSeriesSet(sets []genericSeriesSet, seriesLimit int, mergeFunc genericSeriesMergeFunc) genericSeriesSet {
|
|
|
|
|
if len(sets) == 1 {
|
|
|
|
|
return sets[0]
|
|
|
|
|
}
|
|
|
|
@ -356,13 +359,19 @@ func newGenericMergeSeriesSet(sets []genericSeriesSet, mergeFunc genericSeriesMe
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return &genericMergeSeriesSet{
|
|
|
|
|
mergeFunc: mergeFunc,
|
|
|
|
|
sets: sets,
|
|
|
|
|
heap: h,
|
|
|
|
|
mergeFunc: mergeFunc,
|
|
|
|
|
sets: sets,
|
|
|
|
|
heap: h,
|
|
|
|
|
seriesLimit: seriesLimit,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *genericMergeSeriesSet) Next() bool {
|
|
|
|
|
if c.seriesLimit > 0 && c.mergedSeries >= c.seriesLimit {
|
|
|
|
|
// Exit early if seriesLimit is set.
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run in a loop because the "next" series sets may not be valid anymore.
|
|
|
|
|
// If, for the current label set, all the next series sets come from
|
|
|
|
|
// failed remote storage sources, we want to keep trying with the next label set.
|
|
|
|
@ -393,6 +402,7 @@ func (c *genericMergeSeriesSet) Next() bool {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
c.mergedSeries++
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|