github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/ruler/base/error_translate_queryable.go (about)

     1  package base
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/gogo/status"
     7  	"github.com/pkg/errors"
     8  	"github.com/prometheus/prometheus/model/labels"
     9  	"github.com/prometheus/prometheus/promql"
    10  	"github.com/prometheus/prometheus/storage"
    11  
    12  	storage_errors "github.com/grafana/loki/pkg/storage/errors"
    13  	"github.com/grafana/loki/pkg/util/validation"
    14  )
    15  
    16  // TranslateToPromqlAPIError converts error to one of promql.Errors for consumption in PromQL API.
    17  // PromQL API only recognizes few errors, and converts everything else to HTTP status code 422.
    18  //
    19  // Specifically, it supports:
    20  //
    21  //   promql.ErrQueryCanceled, mapped to 503
    22  //   promql.ErrQueryTimeout, mapped to 503
    23  //   promql.ErrStorage mapped to 500
    24  //   anything else is mapped to 422
    25  //
    26  // Querier code produces different kinds of errors, and we want to map them to above-mentioned HTTP status codes correctly.
    27  //
    28  // Details:
    29  // - vendor/github.com/prometheus/prometheus/web/api/v1/api.go, respondError function only accepts *apiError types.
    30  // - translation of error to *apiError happens in vendor/github.com/prometheus/prometheus/web/api/v1/api.go, returnAPIError method.
    31  func TranslateToPromqlAPIError(err error) error {
    32  	if err == nil {
    33  		return err
    34  	}
    35  
    36  	switch errors.Cause(err).(type) {
    37  	case promql.ErrStorage, promql.ErrTooManySamples, promql.ErrQueryCanceled, promql.ErrQueryTimeout:
    38  		// Don't translate those, just in case we use them internally.
    39  		return err
    40  	case storage_errors.QueryError, validation.LimitError:
    41  		// This will be returned with status code 422 by Prometheus API.
    42  		return err
    43  	default:
    44  		if errors.Is(err, context.Canceled) {
    45  			return err // 422
    46  		}
    47  
    48  		s, ok := status.FromError(err)
    49  
    50  		if !ok {
    51  			s, ok = status.FromError(errors.Cause(err))
    52  		}
    53  
    54  		if ok {
    55  			code := s.Code()
    56  
    57  			// Treat these as HTTP status codes, even though they are supposed to be grpc codes.
    58  			if code >= 400 && code < 500 {
    59  				// Return directly, will be mapped to 422
    60  				return err
    61  			} else if code >= 500 && code < 599 {
    62  				// Wrap into ErrStorage for mapping to 500
    63  				return promql.ErrStorage{Err: err}
    64  			}
    65  		}
    66  
    67  		// All other errors will be returned as 500.
    68  		return promql.ErrStorage{Err: err}
    69  	}
    70  }
    71  
    72  // ErrTranslateFn is used to translate or wrap error before returning it by functions in
    73  // storage.SampleAndChunkQueryable interface.
    74  // Input error may be nil.
    75  type ErrTranslateFn func(err error) error
    76  
    77  func NewErrorTranslateQueryableWithFn(q storage.Queryable, fn ErrTranslateFn) storage.Queryable {
    78  	return errorTranslateQueryable{q: q, fn: fn}
    79  }
    80  
    81  type errorTranslateQueryable struct {
    82  	q  storage.Queryable
    83  	fn ErrTranslateFn
    84  }
    85  
    86  func (e errorTranslateQueryable) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
    87  	q, err := e.q.Querier(ctx, mint, maxt)
    88  	return errorTranslateQuerier{q: q, fn: e.fn}, e.fn(err)
    89  }
    90  
    91  type errorTranslateQuerier struct {
    92  	q  storage.Querier
    93  	fn ErrTranslateFn
    94  }
    95  
    96  func (e errorTranslateQuerier) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) {
    97  	values, warnings, err := e.q.LabelValues(name, matchers...)
    98  	return values, warnings, e.fn(err)
    99  }
   100  
   101  func (e errorTranslateQuerier) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) {
   102  	values, warnings, err := e.q.LabelNames(matchers...)
   103  	return values, warnings, e.fn(err)
   104  }
   105  
   106  func (e errorTranslateQuerier) Close() error {
   107  	return e.fn(e.q.Close())
   108  }
   109  
   110  func (e errorTranslateQuerier) Select(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet {
   111  	s := e.q.Select(sortSeries, hints, matchers...)
   112  	return errorTranslateSeriesSet{s: s, fn: e.fn}
   113  }
   114  
   115  type errorTranslateSeriesSet struct {
   116  	s  storage.SeriesSet
   117  	fn ErrTranslateFn
   118  }
   119  
   120  func (e errorTranslateSeriesSet) Next() bool {
   121  	return e.s.Next()
   122  }
   123  
   124  func (e errorTranslateSeriesSet) At() storage.Series {
   125  	return e.s.At()
   126  }
   127  
   128  func (e errorTranslateSeriesSet) Err() error {
   129  	return e.fn(e.s.Err())
   130  }
   131  
   132  func (e errorTranslateSeriesSet) Warnings() storage.Warnings {
   133  	return e.s.Warnings()
   134  }