github.com/thanos-io/thanos@v0.32.5/pkg/api/query/grpc.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package v1
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"github.com/prometheus/prometheus/promql"
    11  	v1 "github.com/prometheus/prometheus/web/api/v1"
    12  	"google.golang.org/grpc"
    13  	"google.golang.org/grpc/codes"
    14  	"google.golang.org/grpc/status"
    15  
    16  	"github.com/thanos-io/thanos/pkg/api/query/querypb"
    17  	"github.com/thanos-io/thanos/pkg/query"
    18  	"github.com/thanos-io/thanos/pkg/store/labelpb"
    19  	"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
    20  )
    21  
    22  type GRPCAPI struct {
    23  	now                         func() time.Time
    24  	replicaLabels               []string
    25  	queryableCreate             query.QueryableCreator
    26  	engineFactory               *QueryEngineFactory
    27  	defaultEngine               querypb.EngineType
    28  	lookbackDeltaCreate         func(int64) time.Duration
    29  	defaultMaxResolutionSeconds time.Duration
    30  }
    31  
    32  func NewGRPCAPI(
    33  	now func() time.Time,
    34  	replicaLabels []string,
    35  	creator query.QueryableCreator,
    36  	engineFactory *QueryEngineFactory,
    37  	defaultEngine querypb.EngineType,
    38  	lookbackDeltaCreate func(int64) time.Duration,
    39  	defaultMaxResolutionSeconds time.Duration,
    40  ) *GRPCAPI {
    41  	return &GRPCAPI{
    42  		now:                         now,
    43  		replicaLabels:               replicaLabels,
    44  		queryableCreate:             creator,
    45  		engineFactory:               engineFactory,
    46  		defaultEngine:               defaultEngine,
    47  		lookbackDeltaCreate:         lookbackDeltaCreate,
    48  		defaultMaxResolutionSeconds: defaultMaxResolutionSeconds,
    49  	}
    50  }
    51  
    52  func RegisterQueryServer(queryServer querypb.QueryServer) func(*grpc.Server) {
    53  	return func(s *grpc.Server) {
    54  		querypb.RegisterQueryServer(s, queryServer)
    55  	}
    56  }
    57  
    58  func (g *GRPCAPI) Query(request *querypb.QueryRequest, server querypb.Query_QueryServer) error {
    59  	ctx := server.Context()
    60  	var ts time.Time
    61  	if request.TimeSeconds == 0 {
    62  		ts = g.now()
    63  	} else {
    64  		ts = time.Unix(request.TimeSeconds, 0)
    65  	}
    66  
    67  	if request.TimeoutSeconds != 0 {
    68  		var cancel context.CancelFunc
    69  		timeout := time.Duration(request.TimeoutSeconds) * time.Second
    70  		ctx, cancel = context.WithTimeout(ctx, timeout)
    71  		defer cancel()
    72  	}
    73  
    74  	maxResolution := request.MaxResolutionSeconds
    75  	if request.MaxResolutionSeconds == 0 {
    76  		maxResolution = g.defaultMaxResolutionSeconds.Milliseconds() / 1000
    77  	}
    78  
    79  	lookbackDelta := g.lookbackDeltaCreate(maxResolution * 1000)
    80  	if request.LookbackDeltaSeconds > 0 {
    81  		lookbackDelta = time.Duration(request.LookbackDeltaSeconds) * time.Second
    82  	}
    83  
    84  	storeMatchers, err := querypb.StoreMatchersToLabelMatchers(request.StoreMatchers)
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	replicaLabels := g.replicaLabels
    90  	if len(request.ReplicaLabels) != 0 {
    91  		replicaLabels = request.ReplicaLabels
    92  	}
    93  
    94  	queryable := g.queryableCreate(
    95  		request.EnableDedup,
    96  		replicaLabels,
    97  		storeMatchers,
    98  		maxResolution,
    99  		request.EnablePartialResponse,
   100  		request.EnableQueryPushdown,
   101  		false,
   102  		request.ShardInfo,
   103  		query.NoopSeriesStatsReporter,
   104  	)
   105  
   106  	var engine v1.QueryEngine
   107  	engineParam := request.Engine
   108  	if engineParam == querypb.EngineType_default {
   109  		engineParam = g.defaultEngine
   110  	}
   111  
   112  	switch engineParam {
   113  	case querypb.EngineType_prometheus:
   114  		engine = g.engineFactory.GetPrometheusEngine()
   115  	case querypb.EngineType_thanos:
   116  		engine = g.engineFactory.GetThanosEngine()
   117  	default:
   118  		return status.Error(codes.InvalidArgument, "invalid engine parameter")
   119  	}
   120  	qry, err := engine.NewInstantQuery(ctx, queryable, promql.NewPrometheusQueryOpts(false, lookbackDelta), request.Query, ts)
   121  	if err != nil {
   122  		return err
   123  	}
   124  	defer qry.Close()
   125  
   126  	result := qry.Exec(ctx)
   127  	if result.Err != nil {
   128  		return status.Error(codes.Aborted, result.Err.Error())
   129  	}
   130  
   131  	if len(result.Warnings) != 0 {
   132  		if err := server.Send(querypb.NewQueryWarningsResponse(result.Warnings...)); err != nil {
   133  			return err
   134  		}
   135  	}
   136  
   137  	switch vector := result.Value.(type) {
   138  	case promql.Scalar:
   139  		series := &prompb.TimeSeries{
   140  			Samples: []prompb.Sample{{Value: vector.V, Timestamp: vector.T}},
   141  		}
   142  		if err := server.Send(querypb.NewQueryResponse(series)); err != nil {
   143  			return err
   144  		}
   145  	case promql.Vector:
   146  		for _, sample := range vector {
   147  			floats, histograms := prompb.SamplesFromPromqlSamples(sample)
   148  			series := &prompb.TimeSeries{
   149  				Labels:     labelpb.ZLabelsFromPromLabels(sample.Metric),
   150  				Samples:    floats,
   151  				Histograms: histograms,
   152  			}
   153  			if err := server.Send(querypb.NewQueryResponse(series)); err != nil {
   154  				return err
   155  			}
   156  		}
   157  		return nil
   158  	}
   159  
   160  	return nil
   161  }
   162  
   163  func (g *GRPCAPI) QueryRange(request *querypb.QueryRangeRequest, srv querypb.Query_QueryRangeServer) error {
   164  	ctx := srv.Context()
   165  	if request.TimeoutSeconds != 0 {
   166  		var cancel context.CancelFunc
   167  		timeout := time.Duration(request.TimeoutSeconds) * time.Second
   168  		ctx, cancel = context.WithTimeout(ctx, timeout)
   169  		defer cancel()
   170  	}
   171  
   172  	maxResolution := request.MaxResolutionSeconds
   173  	if request.MaxResolutionSeconds == 0 {
   174  		maxResolution = g.defaultMaxResolutionSeconds.Milliseconds() / 1000
   175  	}
   176  
   177  	lookbackDelta := g.lookbackDeltaCreate(maxResolution * 1000)
   178  	if request.LookbackDeltaSeconds > 0 {
   179  		lookbackDelta = time.Duration(request.LookbackDeltaSeconds) * time.Second
   180  	}
   181  
   182  	storeMatchers, err := querypb.StoreMatchersToLabelMatchers(request.StoreMatchers)
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	replicaLabels := g.replicaLabels
   188  	if len(request.ReplicaLabels) != 0 {
   189  		replicaLabels = request.ReplicaLabels
   190  	}
   191  
   192  	queryable := g.queryableCreate(
   193  		request.EnableDedup,
   194  		replicaLabels,
   195  		storeMatchers,
   196  		maxResolution,
   197  		request.EnablePartialResponse,
   198  		request.EnableQueryPushdown,
   199  		false,
   200  		request.ShardInfo,
   201  		query.NoopSeriesStatsReporter,
   202  	)
   203  
   204  	startTime := time.Unix(request.StartTimeSeconds, 0)
   205  	endTime := time.Unix(request.EndTimeSeconds, 0)
   206  	interval := time.Duration(request.IntervalSeconds) * time.Second
   207  
   208  	var engine v1.QueryEngine
   209  	engineParam := request.Engine
   210  	if engineParam == querypb.EngineType_default {
   211  		engineParam = g.defaultEngine
   212  	}
   213  
   214  	switch engineParam {
   215  	case querypb.EngineType_prometheus:
   216  		engine = g.engineFactory.GetPrometheusEngine()
   217  	case querypb.EngineType_thanos:
   218  		engine = g.engineFactory.GetThanosEngine()
   219  	default:
   220  		return status.Error(codes.InvalidArgument, "invalid engine parameter")
   221  	}
   222  	qry, err := engine.NewRangeQuery(ctx, queryable, promql.NewPrometheusQueryOpts(false, lookbackDelta), request.Query, startTime, endTime, interval)
   223  	if err != nil {
   224  		return err
   225  	}
   226  	defer qry.Close()
   227  
   228  	result := qry.Exec(ctx)
   229  	if result.Err != nil {
   230  		return status.Error(codes.Aborted, result.Err.Error())
   231  	}
   232  
   233  	if len(result.Warnings) != 0 {
   234  		if err := srv.Send(querypb.NewQueryRangeWarningsResponse(result.Warnings...)); err != nil {
   235  			return err
   236  		}
   237  	}
   238  
   239  	switch value := result.Value.(type) {
   240  	case promql.Matrix:
   241  		for _, series := range value {
   242  			floats, histograms := prompb.SamplesFromPromqlSeries(series)
   243  			series := &prompb.TimeSeries{
   244  				Labels:     labelpb.ZLabelsFromPromLabels(series.Metric),
   245  				Samples:    floats,
   246  				Histograms: histograms,
   247  			}
   248  			if err := srv.Send(querypb.NewQueryRangeResponse(series)); err != nil {
   249  				return err
   250  			}
   251  		}
   252  	case promql.Vector:
   253  		for _, sample := range value {
   254  			floats, histograms := prompb.SamplesFromPromqlSamples(sample)
   255  			series := &prompb.TimeSeries{
   256  				Labels:     labelpb.ZLabelsFromPromLabels(sample.Metric),
   257  				Samples:    floats,
   258  				Histograms: histograms,
   259  			}
   260  			if err := srv.Send(querypb.NewQueryRangeResponse(series)); err != nil {
   261  				return err
   262  			}
   263  		}
   264  		return nil
   265  	case promql.Scalar:
   266  		series := &prompb.TimeSeries{
   267  			Samples: []prompb.Sample{{Value: value.V, Timestamp: value.T}},
   268  		}
   269  		return srv.Send(querypb.NewQueryRangeResponse(series))
   270  	}
   271  
   272  	return nil
   273  }