github.com/thanos-io/thanos@v0.32.5/pkg/receive/writer.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package receive
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"github.com/go-kit/log"
    11  	"github.com/go-kit/log/level"
    12  	"github.com/pkg/errors"
    13  	"github.com/prometheus/common/model"
    14  	"github.com/prometheus/prometheus/model/exemplar"
    15  	"github.com/prometheus/prometheus/model/histogram"
    16  	"github.com/prometheus/prometheus/model/labels"
    17  	"github.com/prometheus/prometheus/storage"
    18  	"github.com/prometheus/prometheus/tsdb"
    19  
    20  	"github.com/thanos-io/thanos/pkg/store/labelpb"
    21  	"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
    22  )
    23  
    24  // Appendable returns an Appender.
    25  type Appendable interface {
    26  	Appender(ctx context.Context) (storage.Appender, error)
    27  }
    28  
    29  type TenantStorage interface {
    30  	TenantAppendable(string) (Appendable, error)
    31  }
    32  
    33  // Wraps storage.Appender to add validation and logging.
    34  type ReceiveAppender struct {
    35  	tLogger        log.Logger
    36  	tooFarInFuture int64 // Unit: nanoseconds
    37  	storage.Appender
    38  }
    39  
    40  func (ra *ReceiveAppender) Append(ref storage.SeriesRef, lset labels.Labels, t int64, v float64) (storage.SeriesRef, error) {
    41  	if ra.tooFarInFuture > 0 {
    42  		tooFar := model.Now().Add(time.Duration(ra.tooFarInFuture))
    43  		if tooFar.Before(model.Time(t)) {
    44  			level.Warn(ra.tLogger).Log("msg", "block metric too far in the future", "lset", lset,
    45  				"timestamp", t, "bound", tooFar)
    46  			// now + tooFarInFutureTimeWindow < sample timestamp
    47  			return 0, storage.ErrOutOfBounds
    48  		}
    49  	}
    50  	return ra.Appender.Append(ref, lset, t, v)
    51  }
    52  
    53  type WriterOptions struct {
    54  	Intern                   bool
    55  	TooFarInFutureTimeWindow int64 // Unit: nanoseconds
    56  }
    57  
    58  type Writer struct {
    59  	logger    log.Logger
    60  	multiTSDB TenantStorage
    61  	opts      *WriterOptions
    62  }
    63  
    64  func NewWriter(logger log.Logger, multiTSDB TenantStorage, opts *WriterOptions) *Writer {
    65  	if opts == nil {
    66  		opts = &WriterOptions{}
    67  	}
    68  	return &Writer{
    69  		logger:    logger,
    70  		multiTSDB: multiTSDB,
    71  		opts:      opts,
    72  	}
    73  }
    74  
    75  func (r *Writer) Write(ctx context.Context, tenantID string, wreq *prompb.WriteRequest) error {
    76  	tLogger := log.With(r.logger, "tenant", tenantID)
    77  
    78  	var (
    79  		numLabelsOutOfOrder = 0
    80  		numLabelsDuplicates = 0
    81  		numLabelsEmpty      = 0
    82  
    83  		numSamplesOutOfOrder  = 0
    84  		numSamplesDuplicates  = 0
    85  		numSamplesOutOfBounds = 0
    86  		numSamplesTooOld      = 0
    87  
    88  		numExemplarsOutOfOrder  = 0
    89  		numExemplarsDuplicate   = 0
    90  		numExemplarsLabelLength = 0
    91  	)
    92  
    93  	s, err := r.multiTSDB.TenantAppendable(tenantID)
    94  	if err != nil {
    95  		return errors.Wrap(err, "get tenant appendable")
    96  	}
    97  
    98  	app, err := s.Appender(ctx)
    99  	if err == tsdb.ErrNotReady {
   100  		return err
   101  	}
   102  	if err != nil {
   103  		return errors.Wrap(err, "get appender")
   104  	}
   105  	getRef := app.(storage.GetRef)
   106  	var (
   107  		ref  storage.SeriesRef
   108  		errs writeErrors
   109  	)
   110  	app = &ReceiveAppender{
   111  		tLogger:        tLogger,
   112  		tooFarInFuture: r.opts.TooFarInFutureTimeWindow,
   113  		Appender:       app,
   114  	}
   115  	for _, t := range wreq.Timeseries {
   116  		// Check if time series labels are valid. If not, skip the time series
   117  		// and report the error.
   118  		if err := labelpb.ValidateLabels(t.Labels); err != nil {
   119  			lset := &labelpb.ZLabelSet{Labels: t.Labels}
   120  			switch err {
   121  			case labelpb.ErrOutOfOrderLabels:
   122  				numLabelsOutOfOrder++
   123  				level.Debug(tLogger).Log("msg", "Out of order labels in the label set", "lset", lset.String())
   124  			case labelpb.ErrDuplicateLabels:
   125  				numLabelsDuplicates++
   126  				level.Debug(tLogger).Log("msg", "Duplicate labels in the label set", "lset", lset.String())
   127  			case labelpb.ErrEmptyLabels:
   128  				numLabelsEmpty++
   129  				level.Debug(tLogger).Log("msg", "Labels with empty name in the label set", "lset", lset.String())
   130  			default:
   131  				level.Debug(tLogger).Log("msg", "Error validating labels", "err", err)
   132  			}
   133  
   134  			continue
   135  		}
   136  
   137  		lset := labelpb.ZLabelsToPromLabels(t.Labels)
   138  
   139  		// Check if the TSDB has cached reference for those labels.
   140  		ref, lset = getRef.GetRef(lset, lset.Hash())
   141  		if ref == 0 {
   142  			// If not, copy labels, as TSDB will hold those strings long term. Given no
   143  			// copy unmarshal we don't want to keep memory for whole protobuf, only for labels.
   144  			labelpb.ReAllocZLabelsStrings(&t.Labels, r.opts.Intern)
   145  			lset = labelpb.ZLabelsToPromLabels(t.Labels)
   146  		}
   147  
   148  		// Append as many valid samples as possible, but keep track of the errors.
   149  		for _, s := range t.Samples {
   150  			ref, err = app.Append(ref, lset, s.Timestamp, s.Value)
   151  			switch err {
   152  			case storage.ErrOutOfOrderSample:
   153  				numSamplesOutOfOrder++
   154  				level.Debug(tLogger).Log("msg", "Out of order sample", "lset", lset, "value", s.Value, "timestamp", s.Timestamp)
   155  			case storage.ErrDuplicateSampleForTimestamp:
   156  				numSamplesDuplicates++
   157  				level.Debug(tLogger).Log("msg", "Duplicate sample for timestamp", "lset", lset, "value", s.Value, "timestamp", s.Timestamp)
   158  			case storage.ErrOutOfBounds:
   159  				numSamplesOutOfBounds++
   160  				level.Debug(tLogger).Log("msg", "Out of bounds metric", "lset", lset, "value", s.Value, "timestamp", s.Timestamp)
   161  			case storage.ErrTooOldSample:
   162  				numSamplesTooOld++
   163  				level.Debug(tLogger).Log("msg", "Sample is too old", "lset", lset, "value", s.Value, "timestamp", s.Timestamp)
   164  			default:
   165  				if err != nil {
   166  					level.Debug(tLogger).Log("msg", "Error ingesting sample", "err", err)
   167  				}
   168  			}
   169  		}
   170  
   171  		for _, hp := range t.Histograms {
   172  			var (
   173  				h  *histogram.Histogram
   174  				fh *histogram.FloatHistogram
   175  			)
   176  
   177  			if hp.IsFloatHistogram() {
   178  				fh = prompb.FloatHistogramProtoToFloatHistogram(hp)
   179  			} else {
   180  				h = prompb.HistogramProtoToHistogram(hp)
   181  			}
   182  
   183  			ref, err = app.AppendHistogram(ref, lset, hp.Timestamp, h, fh)
   184  			switch err {
   185  			case storage.ErrOutOfOrderSample:
   186  				numSamplesOutOfOrder++
   187  				level.Debug(tLogger).Log("msg", "Out of order histogram", "lset", lset, "timestamp", hp.Timestamp)
   188  			case storage.ErrDuplicateSampleForTimestamp:
   189  				numSamplesDuplicates++
   190  				level.Debug(tLogger).Log("msg", "Duplicate histogram for timestamp", "lset", lset, "timestamp", hp.Timestamp)
   191  			case storage.ErrOutOfBounds:
   192  				numSamplesOutOfBounds++
   193  				level.Debug(tLogger).Log("msg", "Out of bounds metric", "lset", lset, "timestamp", hp.Timestamp)
   194  			case storage.ErrTooOldSample:
   195  				numSamplesTooOld++
   196  				level.Debug(tLogger).Log("msg", "Histogram is too old", "lset", lset, "timestamp", hp.Timestamp)
   197  			default:
   198  				if err != nil {
   199  					level.Debug(tLogger).Log("msg", "Error ingesting histogram", "err", err)
   200  				}
   201  			}
   202  		}
   203  
   204  		// Current implemetation of app.AppendExemplar doesn't create a new series, so it must be already present.
   205  		// We drop the exemplars in case the series doesn't exist.
   206  		if ref != 0 && len(t.Exemplars) > 0 {
   207  			for _, ex := range t.Exemplars {
   208  				exLset := labelpb.ZLabelsToPromLabels(ex.Labels)
   209  				exLogger := log.With(tLogger, "exemplarLset", exLset, "exemplar", ex.String())
   210  
   211  				if _, err = app.AppendExemplar(ref, lset, exemplar.Exemplar{
   212  					Labels: exLset,
   213  					Value:  ex.Value,
   214  					Ts:     ex.Timestamp,
   215  					HasTs:  true,
   216  				}); err != nil {
   217  					switch err {
   218  					case storage.ErrOutOfOrderExemplar:
   219  						numExemplarsOutOfOrder++
   220  						level.Debug(exLogger).Log("msg", "Out of order exemplar")
   221  					case storage.ErrDuplicateExemplar:
   222  						numExemplarsDuplicate++
   223  						level.Debug(exLogger).Log("msg", "Duplicate exemplar")
   224  					case storage.ErrExemplarLabelLength:
   225  						numExemplarsLabelLength++
   226  						level.Debug(exLogger).Log("msg", "Label length for exemplar exceeds max limit", "limit", exemplar.ExemplarMaxLabelSetLength)
   227  					default:
   228  						if err != nil {
   229  							level.Debug(exLogger).Log("msg", "Error ingesting exemplar", "err", err)
   230  						}
   231  					}
   232  				}
   233  			}
   234  		}
   235  	}
   236  
   237  	if numLabelsOutOfOrder > 0 {
   238  		level.Warn(tLogger).Log("msg", "Error on series with out-of-order labels", "numDropped", numLabelsOutOfOrder)
   239  		errs.Add(errors.Wrapf(labelpb.ErrOutOfOrderLabels, "add %d series", numLabelsOutOfOrder))
   240  	}
   241  	if numLabelsDuplicates > 0 {
   242  		level.Warn(tLogger).Log("msg", "Error on series with duplicate labels", "numDropped", numLabelsDuplicates)
   243  		errs.Add(errors.Wrapf(labelpb.ErrDuplicateLabels, "add %d series", numLabelsDuplicates))
   244  	}
   245  	if numLabelsEmpty > 0 {
   246  		level.Warn(tLogger).Log("msg", "Error on series with empty label name or value", "numDropped", numLabelsEmpty)
   247  		errs.Add(errors.Wrapf(labelpb.ErrEmptyLabels, "add %d series", numLabelsEmpty))
   248  	}
   249  
   250  	if numSamplesOutOfOrder > 0 {
   251  		level.Warn(tLogger).Log("msg", "Error on ingesting out-of-order samples", "numDropped", numSamplesOutOfOrder)
   252  		errs.Add(errors.Wrapf(storage.ErrOutOfOrderSample, "add %d samples", numSamplesOutOfOrder))
   253  	}
   254  	if numSamplesDuplicates > 0 {
   255  		level.Warn(tLogger).Log("msg", "Error on ingesting samples with different value but same timestamp", "numDropped", numSamplesDuplicates)
   256  		errs.Add(errors.Wrapf(storage.ErrDuplicateSampleForTimestamp, "add %d samples", numSamplesDuplicates))
   257  	}
   258  	if numSamplesOutOfBounds > 0 {
   259  		level.Warn(tLogger).Log("msg", "Error on ingesting samples that are too old or are too far into the future", "numDropped", numSamplesOutOfBounds)
   260  		errs.Add(errors.Wrapf(storage.ErrOutOfBounds, "add %d samples", numSamplesOutOfBounds))
   261  	}
   262  	if numSamplesTooOld > 0 {
   263  		level.Warn(tLogger).Log("msg", "Error on ingesting samples that are outside of the allowed out-of-order time window", "numDropped", numSamplesTooOld)
   264  		errs.Add(errors.Wrapf(storage.ErrTooOldSample, "add %d samples", numSamplesTooOld))
   265  	}
   266  
   267  	if numExemplarsOutOfOrder > 0 {
   268  		level.Warn(tLogger).Log("msg", "Error on ingesting out-of-order exemplars", "numDropped", numExemplarsOutOfOrder)
   269  		errs.Add(errors.Wrapf(storage.ErrOutOfOrderExemplar, "add %d exemplars", numExemplarsOutOfOrder))
   270  	}
   271  	if numExemplarsDuplicate > 0 {
   272  		level.Warn(tLogger).Log("msg", "Error on ingesting duplicate exemplars", "numDropped", numExemplarsDuplicate)
   273  		errs.Add(errors.Wrapf(storage.ErrDuplicateExemplar, "add %d exemplars", numExemplarsDuplicate))
   274  	}
   275  	if numExemplarsLabelLength > 0 {
   276  		level.Warn(tLogger).Log("msg", "Error on ingesting exemplars with label length exceeding maximum limit", "numDropped", numExemplarsLabelLength)
   277  		errs.Add(errors.Wrapf(storage.ErrExemplarLabelLength, "add %d exemplars", numExemplarsLabelLength))
   278  	}
   279  
   280  	if err := app.Commit(); err != nil {
   281  		errs.Add(errors.Wrap(err, "commit samples"))
   282  	}
   283  	return errs.ErrOrNil()
   284  }