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 }