diff --git a/tsdb/head_append.go b/tsdb/head_append.go index 9b273db811..15841c4162 100644 --- a/tsdb/head_append.go +++ b/tsdb/head_append.go @@ -357,6 +357,18 @@ const ( stCustomBucketFloatHistogram // Native float histograms with custom bucket boundaries. Goes to `floatHistograms`. ) +// TypeLabel returns a name to use for instrumentation to reflect histogram vs float operations. +func (s sampleType) TypeLabel() string { + switch s { + default: + return "" + case stFloat: + return sampleMetricTypeFloat + case stHistogram, stCustomBucketHistogram, stFloatHistogram, stCustomBucketFloatHistogram: + return sampleMetricTypeHistogram + } +} + // appendBatch is used to partition all the appended data into batches that are // "type clean", i.e. every series receives only samples of one type within the // batch. Types in this regard are defined by the sampleType enum above. diff --git a/tsdb/head_append_v2.go b/tsdb/head_append_v2.go index 19fd320057..1a3d4fc33a 100644 --- a/tsdb/head_append_v2.go +++ b/tsdb/head_append_v2.go @@ -103,31 +103,43 @@ type headAppenderV2 struct { headAppenderBase } +// toBasicSampleType detects sample type based on a typical multi sample Append API. +// Basic because this does not detect some details like custom bucket histogram or native. +// TODO: Improve with https://github.com/prometheus/prometheus/issues/17925 +func toBasicSampleType(v float64, h *histogram.Histogram, fh *histogram.FloatHistogram) sampleType { + if v != 0 || (h == nil && fh == nil) { + return stFloat // Fast path. + } + if fh != nil { + return stFloatHistogram + } + return stHistogram +} + func (a *headAppenderV2) Append(ref storage.SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts storage.AOptions) (storage.SeriesRef, error) { var ( + sTyp = toBasicSampleType(v, h, fh) + isStale bool // Avoid shadowing err variables for reliability. - valErr, appErr, partialErr error - sampleMetricType = sampleMetricTypeFloat - isStale bool + appErr, partialErr error ) - // Fail fast on incorrect histograms. - switch { - case fh != nil: - sampleMetricType = sampleMetricTypeHistogram - valErr = fh.Validate() - case h != nil: - sampleMetricType = sampleMetricTypeHistogram - valErr = h.Validate() - } - if valErr != nil { - return 0, valErr + switch sTyp { + default: + case stFloatHistogram: + if err := fh.Validate(); err != nil { + return 0, err + } + case stHistogram: + if err := h.Validate(); err != nil { + return 0, err + } } // Fail fast if OOO is disabled and the sample is out of bounds. // Otherwise, a full check will be done later to decide if the sample is in-order or out-of-order. if a.oooTimeWindow == 0 && t < a.minValidTime { - a.head.metrics.outOfBoundSamples.WithLabelValues(sampleMetricType).Inc() + a.head.metrics.outOfBoundSamples.WithLabelValues(sTyp.TypeLabel()).Inc() return 0, storage.ErrOutOfBounds } @@ -141,15 +153,15 @@ func (a *headAppenderV2) Append(ref storage.SeriesRef, ls labels.Labels, st, t i } // TODO(bwplotka): Handle ST natively (as per PROM-60). - if a.head.opts.EnableSTAsZeroSample && st != 0 { + if st != 0 && a.head.opts.EnableSTAsZeroSample { a.bestEffortAppendSTZeroSample(s, ls, st, t, h, fh) } - switch { - case fh != nil: + switch sTyp { + case stFloatHistogram: isStale = value.IsStaleNaN(fh.Sum) appErr = a.appendFloatHistogram(s, t, fh, opts.RejectOutOfOrder) - case h != nil: + case stHistogram: isStale = value.IsStaleNaN(h.Sum) appErr = a.appendHistogram(s, t, h, opts.RejectOutOfOrder) default: @@ -171,6 +183,7 @@ func (a *headAppenderV2) Append(ref storage.SeriesRef, ls labels.Labels, st, t i return a.Append(storage.SeriesRef(s.ref), ls, st, t, 0, nil, &histogram.FloatHistogram{Sum: v}, storage.AOptions{ RejectOutOfOrder: opts.RejectOutOfOrder, }) + default: } // Note that a series reference not yet in the map will come out // as stNone, but since we do not handle that case separately, @@ -179,13 +192,14 @@ func (a *headAppenderV2) Append(ref storage.SeriesRef, ls labels.Labels, st, t i } appErr = a.appendFloat(s, t, v, opts.RejectOutOfOrder) } + // Handle append error, if any. if appErr != nil { switch { case errors.Is(appErr, storage.ErrOutOfOrderSample): - a.head.metrics.outOfOrderSamples.WithLabelValues(sampleMetricType).Inc() + a.head.metrics.outOfOrderSamples.WithLabelValues(sTyp.TypeLabel()).Inc() case errors.Is(appErr, storage.ErrTooOldSample): - a.head.metrics.tooOldSamples.WithLabelValues(sampleMetricType).Inc() + a.head.metrics.tooOldSamples.WithLabelValues(sTyp.TypeLabel()).Inc() } return 0, appErr }