github.com/thanos-io/thanos@v0.32.5/pkg/queryfrontend/downsampled.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package queryfrontend
     5  
     6  import (
     7  	"context"
     8  	"math"
     9  
    10  	"github.com/prometheus/client_golang/prometheus"
    11  	"github.com/prometheus/client_golang/prometheus/promauto"
    12  
    13  	"github.com/thanos-io/thanos/internal/cortex/querier/queryrange"
    14  	"github.com/thanos-io/thanos/pkg/compact/downsample"
    15  )
    16  
    17  // DownsampledMiddleware creates a new Middleware that requests downsampled data
    18  // should response to original request with auto max_source_resolution not contain data points.
    19  func DownsampledMiddleware(merger queryrange.Merger, registerer prometheus.Registerer) queryrange.Middleware {
    20  	return queryrange.MiddlewareFunc(func(next queryrange.Handler) queryrange.Handler {
    21  		return downsampled{
    22  			next:   next,
    23  			merger: merger,
    24  			additionalQueriesCount: promauto.With(registerer).NewCounter(prometheus.CounterOpts{
    25  				Namespace: "thanos",
    26  				Name:      "frontend_downsampled_extra_queries_total",
    27  				Help:      "Total number of additional queries for downsampled data",
    28  			}),
    29  		}
    30  	})
    31  }
    32  
    33  type downsampled struct {
    34  	next   queryrange.Handler
    35  	merger queryrange.Merger
    36  
    37  	// Metrics.
    38  	additionalQueriesCount prometheus.Counter
    39  }
    40  
    41  var resolutions = []int64{downsample.ResLevel1, downsample.ResLevel2}
    42  
    43  func (d downsampled) Do(ctx context.Context, req queryrange.Request) (queryrange.Response, error) {
    44  	tqrr, ok := req.(*ThanosQueryRangeRequest)
    45  	if !ok || !tqrr.AutoDownsampling {
    46  		return d.next.Do(ctx, req)
    47  	}
    48  
    49  	var (
    50  		resps = make([]queryrange.Response, 0)
    51  		resp  queryrange.Response
    52  		err   error
    53  		i     int
    54  	)
    55  
    56  forLoop:
    57  	for i < len(resolutions) {
    58  		if i > 0 {
    59  			d.additionalQueriesCount.Inc()
    60  		}
    61  		r := *tqrr
    62  		resp, err = d.next.Do(ctx, &r)
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  		resps = append(resps, resp)
    67  		// Set MaxSourceResolution for next request, if any.
    68  		for i < len(resolutions) {
    69  			if tqrr.MaxSourceResolution < resolutions[i] {
    70  				tqrr.AutoDownsampling = false
    71  				tqrr.MaxSourceResolution = resolutions[i]
    72  				break
    73  			}
    74  			i++
    75  		}
    76  		m := minResponseTime(resp)
    77  		switch m {
    78  		case tqrr.Start: // Response not impacted by retention policy.
    79  			break forLoop
    80  		case -1: // Empty response, retry with higher MaxSourceResolution.
    81  			continue
    82  		default: // Data partially present, query for empty part with higher MaxSourceResolution.
    83  			tqrr.End = m - tqrr.Step
    84  		}
    85  		if tqrr.Start > tqrr.End {
    86  			break forLoop
    87  		}
    88  	}
    89  	response, err := d.merger.MergeResponse(req, resps...)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	return response, nil
    94  }
    95  
    96  // minResponseTime returns earliest timestamp in r.Data.Result.
    97  // -1 is returned if r contains no data points.
    98  // Each SampleStream within r.Data.Result must be sorted by timestamp.
    99  func minResponseTime(r queryrange.Response) int64 {
   100  	var res = r.(*queryrange.PrometheusResponse).Data.Result
   101  	if len(res) == 0 || (len(res[0].Samples) == 0 && len(res[0].Histograms) == 0) {
   102  		return -1
   103  	}
   104  
   105  	minTs := int64(math.MaxInt64)
   106  
   107  	for _, sampleStream := range res {
   108  		if len(sampleStream.Samples) > 0 {
   109  			if ts := sampleStream.Samples[0].TimestampMs; ts < minTs {
   110  				minTs = ts
   111  			}
   112  		}
   113  
   114  		if len(sampleStream.Histograms) > 0 {
   115  			if ts := sampleStream.Histograms[0].Timestamp; ts < minTs {
   116  				minTs = ts
   117  			}
   118  		}
   119  	}
   120  
   121  	return minTs
   122  }