diff --git a/tsdb/head_append.go b/tsdb/head_append.go index 7d0122e0b..90340b42c 100644 --- a/tsdb/head_append.go +++ b/tsdb/head_append.go @@ -505,6 +505,10 @@ func (s *memSeries) appendable(t int64, v float64, headMaxt, minValidTime, oooTi } } + if math.Float64bits(v) == value.QuietZeroNaN { // Say it's allowed; it will be dropped later in commitSamples. + return true, 0, nil + } + // The sample cannot go in the in-order chunk. Check if it can go in the out-of-order chunk. if oooTimeWindow > 0 && t >= headMaxt-oooTimeWindow { return true, headMaxt - t, nil @@ -1141,13 +1145,11 @@ func (a *headAppender) commitSamples(acc *appenderCommitContext) { handleAppendableError(err, &acc.floatsAppended, &acc.floatOOORejected, &acc.floatOOBRejected, &acc.floatTooOldRejected) } - if math.Float64bits(s.V) == value.QuietZeroNaN { - s.V = 0 - } - switch { case err != nil: // Do nothing here. + case oooSample && math.Float64bits(s.V) == value.QuietZeroNaN: + // No-op: we don't store quiet zeros out-of-order. case oooSample: // Sample is OOO and OOO handling is enabled // and the delta is within the OOO tolerance. @@ -1194,7 +1196,11 @@ func (a *headAppender) commitSamples(acc *appenderCommitContext) { acc.floatsAppended-- } default: - ok, chunkCreated = series.append(s.T, s.V, a.appendID, acc.appendChunkOpts) + v := s.V + if math.Float64bits(v) == value.QuietZeroNaN { + v = 0 + } + ok, chunkCreated = series.append(s.T, v, a.appendID, acc.appendChunkOpts) if ok { if s.T < acc.inOrderMint { acc.inOrderMint = s.T diff --git a/tsdb/head_test.go b/tsdb/head_test.go index 36e2fccf3..dd4636880 100644 --- a/tsdb/head_test.go +++ b/tsdb/head_test.go @@ -6123,6 +6123,15 @@ func TestAppendDuplicates(t *testing.T) { sample{t: ts + 10, f: 0}, } require.Equal(t, expectedSamples, result[`{foo="bar"}`]) + + a = h.Appender(context.Background()) + _, err = a.Append(0, lbls, ts+5, math.Float64frombits(value.QuietZeroNaN)) // This is out-of-order, so should be dropped. + require.NoError(t, err) + require.NoError(t, a.Commit()) + + result, err = queryHead(t, h, math.MinInt64, math.MaxInt64, labels.Label{Name: "foo", Value: "bar"}) + require.NoError(t, err) + require.Equal(t, expectedSamples, result[`{foo="bar"}`]) // Same expectedSamples as before. } // TestHeadDetectsDuplicateSampleAtSizeLimit tests a regression where a duplicate sample