textparse: Optimize CreatedTimestamp; It returns int64 value now. (#16072)

..instead of *int64. This is as an optimization and ease of use. We already
accepted in many places (proto histograms, PRW) that CT (or any timestamp really) 0
means not set.

Signed-off-by: bwplotka <bwplotka@gmail.com>
This commit is contained in:
Bartlomiej Plotka 2025-03-07 13:43:13 +01:00 committed by GitHub
parent 71cb219eb6
commit dc85d677d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 180 additions and 107 deletions

View file

@ -262,3 +262,77 @@ func readTestdataFile(tb testing.TB, file string) []byte {
require.NoError(tb, err) require.NoError(tb, err)
return buf return buf
} }
/*
export bench=v1 && go test ./model/textparse/... \
-run '^$' -bench '^BenchmarkCreatedTimestampPromProto' \
-benchtime 2s -count 6 -cpu 2 -benchmem -timeout 999m \
| tee ${bench}.txt
*/
func BenchmarkCreatedTimestampPromProto(b *testing.B) {
data := createTestProtoBuf(b).Bytes()
st := labels.NewSymbolTable()
p := NewProtobufParser(data, true, st)
found := false
Inner:
for {
t, err := p.Next()
switch t {
case EntryInvalid:
b.Fatal(err)
case EntryType:
m, _ := p.Type()
if string(m) == "go_memstats_alloc_bytes_total" {
found = true
break Inner
}
// Parser impl requires this (bug?)
case EntryHistogram:
_, _, _, _ = p.Histogram()
case EntrySeries:
_, _, _ = p.Series()
}
}
require.True(b, found)
b.Run("case=no-ct", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if p.CreatedTimestamp() != 0 {
b.Fatal("should be nil")
}
}
})
found = false
Inner2:
for {
t, err := p.Next()
switch t {
case EntryInvalid:
b.Fatal(err)
case EntryType:
m, _ := p.Type()
if string(m) == "test_counter_with_createdtimestamp" {
found = true
break Inner2
}
case EntryHistogram:
_, _, _, _ = p.Histogram()
case EntrySeries:
_, _, _ = p.Series()
}
}
require.True(b, found)
b.Run("case=ct", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if p.CreatedTimestamp() == 0 {
b.Fatal("should be not nil")
}
}
})
}

View file

@ -29,12 +29,18 @@ import (
type Parser interface { type Parser interface {
// Series returns the bytes of a series with a simple float64 as a // Series returns the bytes of a series with a simple float64 as a
// value, the timestamp if set, and the value of the current sample. // value, the timestamp if set, and the value of the current sample.
// TODO(bwplotka): Similar to CreatedTimestamp, have ts == 0 meaning no timestamp provided.
// We already accepted in many places (PRW, proto parsing histograms) that 0 timestamp is not a
// a valid timestamp. If needed it can be represented as 0+1ms.
Series() ([]byte, *int64, float64) Series() ([]byte, *int64, float64)
// Histogram returns the bytes of a series with a sparse histogram as a // Histogram returns the bytes of a series with a sparse histogram as a
// value, the timestamp if set, and the histogram in the current sample. // value, the timestamp if set, and the histogram in the current sample.
// Depending on the parsed input, the function returns an (integer) Histogram // Depending on the parsed input, the function returns an (integer) Histogram
// or a FloatHistogram, with the respective other return value being nil. // or a FloatHistogram, with the respective other return value being nil.
// TODO(bwplotka): Similar to CreatedTimestamp, have ts == 0 meaning no timestamp provided.
// We already accepted in many places (PRW, proto parsing histograms) that 0 timestamp is not a
// a valid timestamp. If needed it can be represented as 0+1ms.
Histogram() ([]byte, *int64, *histogram.Histogram, *histogram.FloatHistogram) Histogram() ([]byte, *int64, *histogram.Histogram, *histogram.FloatHistogram)
// Help returns the metric name and help text in the current entry. // Help returns the metric name and help text in the current entry.
@ -69,11 +75,9 @@ type Parser interface {
Exemplar(l *exemplar.Exemplar) bool Exemplar(l *exemplar.Exemplar) bool
// CreatedTimestamp returns the created timestamp (in milliseconds) for the // CreatedTimestamp returns the created timestamp (in milliseconds) for the
// current sample. It returns nil if it is unknown e.g. if it wasn't set, // current sample. It returns 0 if it is unknown e.g. if it wasn't set or
// if the scrape protocol or metric type does not support created timestamps. // if the scrape protocol or metric type does not support created timestamps.
// Assume the CreatedTimestamp returned pointer is only valid until CreatedTimestamp() int64
// the Next iteration.
CreatedTimestamp() *int64
// Next advances the parser to the next sample. // Next advances the parser to the next sample.
// It returns (EntryInvalid, io.EOF) if no samples were read. // It returns (EntryInvalid, io.EOF) if no samples were read.

View file

@ -195,7 +195,7 @@ type parsedEntry struct {
lset labels.Labels lset labels.Labels
t *int64 t *int64
es []exemplar.Exemplar es []exemplar.Exemplar
ct *int64 ct int64
// In EntryType. // In EntryType.
typ model.MetricType typ model.MetricType
@ -255,11 +255,8 @@ func testParse(t *testing.T, p Parser) (ret []parsedEntry) {
} }
got.m = string(m) got.m = string(m)
p.Labels(&got.lset) p.Labels(&got.lset)
got.ct = p.CreatedTimestamp()
// Parser reuses int pointer.
if ct := p.CreatedTimestamp(); ct != nil {
got.ct = int64p(*ct)
}
for e := (exemplar.Exemplar{}); p.Exemplar(&e); { for e := (exemplar.Exemplar{}); p.Exemplar(&e); {
got.es = append(got.es, e) got.es = append(got.es, e)
} }

View file

@ -83,7 +83,7 @@ type NHCBParser struct {
fhNHCB *histogram.FloatHistogram fhNHCB *histogram.FloatHistogram
lsetNHCB labels.Labels lsetNHCB labels.Labels
exemplars []exemplar.Exemplar exemplars []exemplar.Exemplar
ctNHCB *int64 ctNHCB int64
metricStringNHCB string metricStringNHCB string
// Collates values from the classic histogram series to build // Collates values from the classic histogram series to build
@ -92,7 +92,7 @@ type NHCBParser struct {
tempNHCB convertnhcb.TempHistogram tempNHCB convertnhcb.TempHistogram
tempExemplars []exemplar.Exemplar tempExemplars []exemplar.Exemplar
tempExemplarCount int tempExemplarCount int
tempCT *int64 tempCT int64
// Remembers the last base histogram metric name (assuming it's // Remembers the last base histogram metric name (assuming it's
// a classic histogram) so we can tell if the next float series // a classic histogram) so we can tell if the next float series
@ -160,7 +160,7 @@ func (p *NHCBParser) Exemplar(ex *exemplar.Exemplar) bool {
return p.parser.Exemplar(ex) return p.parser.Exemplar(ex)
} }
func (p *NHCBParser) CreatedTimestamp() *int64 { func (p *NHCBParser) CreatedTimestamp() int64 {
switch p.state { switch p.state {
case stateStart: case stateStart:
if p.entry == EntrySeries || p.entry == EntryHistogram { if p.entry == EntrySeries || p.entry == EntryHistogram {
@ -171,7 +171,7 @@ func (p *NHCBParser) CreatedTimestamp() *int64 {
case stateEmitting: case stateEmitting:
return p.ctNHCB return p.ctNHCB
} }
return nil return 0
} }
func (p *NHCBParser) Next() (Entry, error) { func (p *NHCBParser) Next() (Entry, error) {
@ -375,6 +375,6 @@ func (p *NHCBParser) processNHCB() bool {
} }
p.tempNHCB.Reset() p.tempNHCB.Reset()
p.tempExemplarCount = 0 p.tempExemplarCount = 0
p.tempCT = nil p.tempCT = 0
return err == nil return err == nil
} }

View file

@ -293,14 +293,14 @@ foobar{quantile="0.99"} 150.1`
lset: labels.FromStrings("__name__", "foo_total"), lset: labels.FromStrings("__name__", "foo_total"),
t: int64p(1520879607789), t: int64p(1520879607789),
es: []exemplar.Exemplar{{Labels: labels.FromStrings("id", "counter-test"), Value: 5}}, es: []exemplar.Exemplar{{Labels: labels.FromStrings("id", "counter-test"), Value: 5}},
ct: int64p(1520872607123), ct: 1520872607123,
}, { }, {
m: `foo_total{a="b"}`, m: `foo_total{a="b"}`,
v: 17.0, v: 17.0,
lset: labels.FromStrings("__name__", "foo_total", "a", "b"), lset: labels.FromStrings("__name__", "foo_total", "a", "b"),
t: int64p(1520879607789), t: int64p(1520879607789),
es: []exemplar.Exemplar{{Labels: labels.FromStrings("id", "counter-test"), Value: 5}}, es: []exemplar.Exemplar{{Labels: labels.FromStrings("id", "counter-test"), Value: 5}},
ct: int64p(1520872607123), ct: 1520872607123,
}, { }, {
m: "bar", m: "bar",
help: "Summary with CT at the end, making sure we find CT even if it's multiple lines a far", help: "Summary with CT at the end, making sure we find CT even if it's multiple lines a far",
@ -311,22 +311,22 @@ foobar{quantile="0.99"} 150.1`
m: "bar_count", m: "bar_count",
v: 17.0, v: 17.0,
lset: labels.FromStrings("__name__", "bar_count"), lset: labels.FromStrings("__name__", "bar_count"),
ct: int64p(1520872608124), ct: 1520872608124,
}, { }, {
m: "bar_sum", m: "bar_sum",
v: 324789.3, v: 324789.3,
lset: labels.FromStrings("__name__", "bar_sum"), lset: labels.FromStrings("__name__", "bar_sum"),
ct: int64p(1520872608124), ct: 1520872608124,
}, { }, {
m: `bar{quantile="0.95"}`, m: `bar{quantile="0.95"}`,
v: 123.7, v: 123.7,
lset: labels.FromStrings("__name__", "bar", "quantile", "0.95"), lset: labels.FromStrings("__name__", "bar", "quantile", "0.95"),
ct: int64p(1520872608124), ct: 1520872608124,
}, { }, {
m: `bar{quantile="0.99"}`, m: `bar{quantile="0.99"}`,
v: 150.0, v: 150.0,
lset: labels.FromStrings("__name__", "bar", "quantile", "0.99"), lset: labels.FromStrings("__name__", "bar", "quantile", "0.99"),
ct: int64p(1520872608124), ct: 1520872608124,
}, { }, {
m: "baz", m: "baz",
help: "Histogram with the same objective as above's summary", help: "Histogram with the same objective as above's summary",
@ -344,7 +344,7 @@ foobar{quantile="0.99"} 150.1`
CustomValues: []float64{0.0}, // We do not store the +Inf boundary. CustomValues: []float64{0.0}, // We do not store the +Inf boundary.
}, },
lset: labels.FromStrings("__name__", "baz"), lset: labels.FromStrings("__name__", "baz"),
ct: int64p(1520872609125), ct: 1520872609125,
}, { }, {
m: "fizz_created", m: "fizz_created",
help: "Gauge which shouldn't be parsed as CT", help: "Gauge which shouldn't be parsed as CT",
@ -372,7 +372,7 @@ foobar{quantile="0.99"} 150.1`
CustomValues: []float64{0.0}, // We do not store the +Inf boundary. CustomValues: []float64{0.0}, // We do not store the +Inf boundary.
}, },
lset: labels.FromStrings("__name__", "something"), lset: labels.FromStrings("__name__", "something"),
ct: int64p(1520430001000), ct: 1520430001000,
}, { }, {
m: `something{a="b"}`, m: `something{a="b"}`,
shs: &histogram.Histogram{ shs: &histogram.Histogram{
@ -384,7 +384,7 @@ foobar{quantile="0.99"} 150.1`
CustomValues: []float64{0.0}, // We do not store the +Inf boundary. CustomValues: []float64{0.0}, // We do not store the +Inf boundary.
}, },
lset: labels.FromStrings("__name__", "something", "a", "b"), lset: labels.FromStrings("__name__", "something", "a", "b"),
ct: int64p(1520430002000), ct: 1520430002000,
}, { }, {
m: "yum", m: "yum",
help: "Summary with _created between sum and quantiles", help: "Summary with _created between sum and quantiles",
@ -395,22 +395,22 @@ foobar{quantile="0.99"} 150.1`
m: `yum_count`, m: `yum_count`,
v: 20, v: 20,
lset: labels.FromStrings("__name__", "yum_count"), lset: labels.FromStrings("__name__", "yum_count"),
ct: int64p(1520430003000), ct: 1520430003000,
}, { }, {
m: `yum_sum`, m: `yum_sum`,
v: 324789.5, v: 324789.5,
lset: labels.FromStrings("__name__", "yum_sum"), lset: labels.FromStrings("__name__", "yum_sum"),
ct: int64p(1520430003000), ct: 1520430003000,
}, { }, {
m: `yum{quantile="0.95"}`, m: `yum{quantile="0.95"}`,
v: 123.7, v: 123.7,
lset: labels.FromStrings("__name__", "yum", "quantile", "0.95"), lset: labels.FromStrings("__name__", "yum", "quantile", "0.95"),
ct: int64p(1520430003000), ct: 1520430003000,
}, { }, {
m: `yum{quantile="0.99"}`, m: `yum{quantile="0.99"}`,
v: 150.0, v: 150.0,
lset: labels.FromStrings("__name__", "yum", "quantile", "0.99"), lset: labels.FromStrings("__name__", "yum", "quantile", "0.99"),
ct: int64p(1520430003000), ct: 1520430003000,
}, { }, {
m: "foobar", m: "foobar",
help: "Summary with _created as the first line", help: "Summary with _created as the first line",
@ -421,22 +421,22 @@ foobar{quantile="0.99"} 150.1`
m: `foobar_count`, m: `foobar_count`,
v: 21, v: 21,
lset: labels.FromStrings("__name__", "foobar_count"), lset: labels.FromStrings("__name__", "foobar_count"),
ct: int64p(1520430004000), ct: 1520430004000,
}, { }, {
m: `foobar_sum`, m: `foobar_sum`,
v: 324789.6, v: 324789.6,
lset: labels.FromStrings("__name__", "foobar_sum"), lset: labels.FromStrings("__name__", "foobar_sum"),
ct: int64p(1520430004000), ct: 1520430004000,
}, { }, {
m: `foobar{quantile="0.95"}`, m: `foobar{quantile="0.95"}`,
v: 123.8, v: 123.8,
lset: labels.FromStrings("__name__", "foobar", "quantile", "0.95"), lset: labels.FromStrings("__name__", "foobar", "quantile", "0.95"),
ct: int64p(1520430004000), ct: 1520430004000,
}, { }, {
m: `foobar{quantile="0.99"}`, m: `foobar{quantile="0.99"}`,
v: 150.1, v: 150.1,
lset: labels.FromStrings("__name__", "foobar", "quantile", "0.99"), lset: labels.FromStrings("__name__", "foobar", "quantile", "0.99"),
ct: int64p(1520430004000), ct: 1520430004000,
}, { }, {
m: "metric", m: "metric",
help: "foo\x00bar", help: "foo\x00bar",
@ -646,9 +646,9 @@ func TestNHCBParser_NoNHCBWhenExponential(t *testing.T) {
typ: model.MetricTypeHistogram, typ: model.MetricTypeHistogram,
}) })
var ct *int64 var ct int64
if options.hasCreatedTimeStamp { if options.hasCreatedTimeStamp {
ct = int64p(1000) ct = 1000
} }
var bucketForMetric func(string) string var bucketForMetric func(string) string
@ -973,7 +973,7 @@ something_bucket{a="b",le="+Inf"} 9
CustomValues: []float64{0.0}, // We do not store the +Inf boundary. CustomValues: []float64{0.0}, // We do not store the +Inf boundary.
}, },
lset: labels.FromStrings("__name__", "something", "a", "b"), lset: labels.FromStrings("__name__", "something", "a", "b"),
ct: int64p(1520430002000), ct: 1520430002000,
}, },
} }

View file

@ -259,11 +259,11 @@ func (p *OpenMetricsParser) Exemplar(e *exemplar.Exemplar) bool {
// CreatedTimestamp returns the created timestamp for a current Metric if exists or nil. // CreatedTimestamp returns the created timestamp for a current Metric if exists or nil.
// NOTE(Maniktherana): Might use additional CPU/mem resources due to deep copy of parser required for peeking given 1.0 OM specification on _created series. // NOTE(Maniktherana): Might use additional CPU/mem resources due to deep copy of parser required for peeking given 1.0 OM specification on _created series.
func (p *OpenMetricsParser) CreatedTimestamp() *int64 { func (p *OpenMetricsParser) CreatedTimestamp() int64 {
if !typeRequiresCT(p.mtype) { if !typeRequiresCT(p.mtype) {
// Not a CT supported metric type, fast path. // Not a CT supported metric type, fast path.
p.ctHashSet = 0 // Use ctHashSet as a single way of telling "empty cache" p.ctHashSet = 0 // Use ctHashSet as a single way of telling "empty cache"
return nil return 0
} }
var ( var (
@ -280,7 +280,7 @@ func (p *OpenMetricsParser) CreatedTimestamp() *int64 {
currHash := p.seriesHash(&buf, currName) currHash := p.seriesHash(&buf, currName)
// Check cache, perhaps we fetched something already. // Check cache, perhaps we fetched something already.
if currHash == p.ctHashSet && p.ct > 0 { if currHash == p.ctHashSet && p.ct > 0 {
return &p.ct return p.ct
} }
// Create a new lexer to reset the parser once this function is done executing. // Create a new lexer to reset the parser once this function is done executing.
@ -310,12 +310,12 @@ func (p *OpenMetricsParser) CreatedTimestamp() *int64 {
// spec improvement would help. // spec improvement would help.
// TODO: Make sure OM 1.1/2.0 pass CT via metadata or exemplar-like to avoid this. // TODO: Make sure OM 1.1/2.0 pass CT via metadata or exemplar-like to avoid this.
p.resetCTParseValues() p.resetCTParseValues()
return nil return 0
} }
if eType != EntrySeries { if eType != EntrySeries {
// Assume we hit different family, no CT line found. // Assume we hit different family, no CT line found.
p.resetCTParseValues() p.resetCTParseValues()
return nil return 0
} }
peekedName := p.series[p.offsets[0]-p.start : p.offsets[1]-p.start] peekedName := p.series[p.offsets[0]-p.start : p.offsets[1]-p.start]
@ -329,14 +329,14 @@ func (p *OpenMetricsParser) CreatedTimestamp() *int64 {
if peekedHash != currHash { if peekedHash != currHash {
// Found CT line for a different series, for our series no CT. // Found CT line for a different series, for our series no CT.
p.resetCTParseValues() p.resetCTParseValues()
return nil return 0
} }
// All timestamps in OpenMetrics are Unix Epoch in seconds. Convert to milliseconds. // All timestamps in OpenMetrics are Unix Epoch in seconds. Convert to milliseconds.
// https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#timestamps // https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#timestamps
ct := int64(p.val * 1000.0) ct := int64(p.val * 1000.0)
p.setCTParseValues(ct, currHash, currName, true) p.setCTParseValues(ct, currHash, currName, true)
return &ct return ct
} }
} }

View file

@ -289,7 +289,7 @@ foobar{quantile="0.99"} 150.1`
es: []exemplar.Exemplar{ es: []exemplar.Exemplar{
{Labels: labels.FromStrings("id", "counter-test"), Value: 5}, {Labels: labels.FromStrings("id", "counter-test"), Value: 5},
}, },
ct: int64p(1520872607123), ct: 1520872607123,
}, { }, {
m: `foo_total{a="b"}`, m: `foo_total{a="b"}`,
v: 17.0, v: 17.0,
@ -298,12 +298,12 @@ foobar{quantile="0.99"} 150.1`
es: []exemplar.Exemplar{ es: []exemplar.Exemplar{
{Labels: labels.FromStrings("id", "counter-test"), Value: 5}, {Labels: labels.FromStrings("id", "counter-test"), Value: 5},
}, },
ct: int64p(1520872607123), ct: 1520872607123,
}, { }, {
m: `foo_total{le="c"}`, m: `foo_total{le="c"}`,
v: 21.0, v: 21.0,
lset: labels.FromStrings("__name__", "foo_total", "le", "c"), lset: labels.FromStrings("__name__", "foo_total", "le", "c"),
ct: int64p(1520872621123), ct: 1520872621123,
}, { }, {
m: `foo_total{le="1"}`, m: `foo_total{le="1"}`,
v: 10.0, v: 10.0,
@ -318,22 +318,22 @@ foobar{quantile="0.99"} 150.1`
m: "bar_count", m: "bar_count",
v: 17.0, v: 17.0,
lset: labels.FromStrings("__name__", "bar_count"), lset: labels.FromStrings("__name__", "bar_count"),
ct: int64p(1520872608124), ct: 1520872608124,
}, { }, {
m: "bar_sum", m: "bar_sum",
v: 324789.3, v: 324789.3,
lset: labels.FromStrings("__name__", "bar_sum"), lset: labels.FromStrings("__name__", "bar_sum"),
ct: int64p(1520872608124), ct: 1520872608124,
}, { }, {
m: `bar{quantile="0.95"}`, m: `bar{quantile="0.95"}`,
v: 123.7, v: 123.7,
lset: labels.FromStrings("__name__", "bar", "quantile", "0.95"), lset: labels.FromStrings("__name__", "bar", "quantile", "0.95"),
ct: int64p(1520872608124), ct: 1520872608124,
}, { }, {
m: `bar{quantile="0.99"}`, m: `bar{quantile="0.99"}`,
v: 150.0, v: 150.0,
lset: labels.FromStrings("__name__", "bar", "quantile", "0.99"), lset: labels.FromStrings("__name__", "bar", "quantile", "0.99"),
ct: int64p(1520872608124), ct: 1520872608124,
}, { }, {
m: "baz", m: "baz",
help: "Histogram with the same objective as above's summary", help: "Histogram with the same objective as above's summary",
@ -344,22 +344,22 @@ foobar{quantile="0.99"} 150.1`
m: `baz_bucket{le="0.0"}`, m: `baz_bucket{le="0.0"}`,
v: 0, v: 0,
lset: labels.FromStrings("__name__", "baz_bucket", "le", "0.0"), lset: labels.FromStrings("__name__", "baz_bucket", "le", "0.0"),
ct: int64p(1520872609125), ct: 1520872609125,
}, { }, {
m: `baz_bucket{le="+Inf"}`, m: `baz_bucket{le="+Inf"}`,
v: 17, v: 17,
lset: labels.FromStrings("__name__", "baz_bucket", "le", "+Inf"), lset: labels.FromStrings("__name__", "baz_bucket", "le", "+Inf"),
ct: int64p(1520872609125), ct: 1520872609125,
}, { }, {
m: `baz_count`, m: `baz_count`,
v: 17, v: 17,
lset: labels.FromStrings("__name__", "baz_count"), lset: labels.FromStrings("__name__", "baz_count"),
ct: int64p(1520872609125), ct: 1520872609125,
}, { }, {
m: `baz_sum`, m: `baz_sum`,
v: 324789.3, v: 324789.3,
lset: labels.FromStrings("__name__", "baz_sum"), lset: labels.FromStrings("__name__", "baz_sum"),
ct: int64p(1520872609125), ct: 1520872609125,
}, { }, {
m: "fizz_created", m: "fizz_created",
help: "Gauge which shouldn't be parsed as CT", help: "Gauge which shouldn't be parsed as CT",
@ -380,27 +380,27 @@ foobar{quantile="0.99"} 150.1`
m: `something_count`, m: `something_count`,
v: 18, v: 18,
lset: labels.FromStrings("__name__", "something_count"), lset: labels.FromStrings("__name__", "something_count"),
ct: int64p(1520430001000), ct: 1520430001000,
}, { }, {
m: `something_sum`, m: `something_sum`,
v: 324789.4, v: 324789.4,
lset: labels.FromStrings("__name__", "something_sum"), lset: labels.FromStrings("__name__", "something_sum"),
ct: int64p(1520430001000), ct: 1520430001000,
}, { }, {
m: `something_bucket{le="0.0"}`, m: `something_bucket{le="0.0"}`,
v: 1, v: 1,
lset: labels.FromStrings("__name__", "something_bucket", "le", "0.0"), lset: labels.FromStrings("__name__", "something_bucket", "le", "0.0"),
ct: int64p(1520430001000), ct: 1520430001000,
}, { }, {
m: `something_bucket{le="1"}`, m: `something_bucket{le="1"}`,
v: 2, v: 2,
lset: labels.FromStrings("__name__", "something_bucket", "le", "1.0"), lset: labels.FromStrings("__name__", "something_bucket", "le", "1.0"),
ct: int64p(1520430001000), ct: 1520430001000,
}, { }, {
m: `something_bucket{le="+Inf"}`, m: `something_bucket{le="+Inf"}`,
v: 18, v: 18,
lset: labels.FromStrings("__name__", "something_bucket", "le", "+Inf"), lset: labels.FromStrings("__name__", "something_bucket", "le", "+Inf"),
ct: int64p(1520430001000), ct: 1520430001000,
}, { }, {
m: "yum", m: "yum",
help: "Summary with _created between sum and quantiles", help: "Summary with _created between sum and quantiles",
@ -411,22 +411,22 @@ foobar{quantile="0.99"} 150.1`
m: `yum_count`, m: `yum_count`,
v: 20, v: 20,
lset: labels.FromStrings("__name__", "yum_count"), lset: labels.FromStrings("__name__", "yum_count"),
ct: int64p(1520430003000), ct: 1520430003000,
}, { }, {
m: `yum_sum`, m: `yum_sum`,
v: 324789.5, v: 324789.5,
lset: labels.FromStrings("__name__", "yum_sum"), lset: labels.FromStrings("__name__", "yum_sum"),
ct: int64p(1520430003000), ct: 1520430003000,
}, { }, {
m: `yum{quantile="0.95"}`, m: `yum{quantile="0.95"}`,
v: 123.7, v: 123.7,
lset: labels.FromStrings("__name__", "yum", "quantile", "0.95"), lset: labels.FromStrings("__name__", "yum", "quantile", "0.95"),
ct: int64p(1520430003000), ct: 1520430003000,
}, { }, {
m: `yum{quantile="0.99"}`, m: `yum{quantile="0.99"}`,
v: 150.0, v: 150.0,
lset: labels.FromStrings("__name__", "yum", "quantile", "0.99"), lset: labels.FromStrings("__name__", "yum", "quantile", "0.99"),
ct: int64p(1520430003000), ct: 1520430003000,
}, { }, {
m: "foobar", m: "foobar",
help: "Summary with _created as the first line", help: "Summary with _created as the first line",
@ -437,22 +437,22 @@ foobar{quantile="0.99"} 150.1`
m: `foobar_count`, m: `foobar_count`,
v: 21, v: 21,
lset: labels.FromStrings("__name__", "foobar_count"), lset: labels.FromStrings("__name__", "foobar_count"),
ct: int64p(1520430004000), ct: 1520430004000,
}, { }, {
m: `foobar_sum`, m: `foobar_sum`,
v: 324789.6, v: 324789.6,
lset: labels.FromStrings("__name__", "foobar_sum"), lset: labels.FromStrings("__name__", "foobar_sum"),
ct: int64p(1520430004000), ct: 1520430004000,
}, { }, {
m: `foobar{quantile="0.95"}`, m: `foobar{quantile="0.95"}`,
v: 123.8, v: 123.8,
lset: labels.FromStrings("__name__", "foobar", "quantile", "0.95"), lset: labels.FromStrings("__name__", "foobar", "quantile", "0.95"),
ct: int64p(1520430004000), ct: 1520430004000,
}, { }, {
m: `foobar{quantile="0.99"}`, m: `foobar{quantile="0.99"}`,
v: 150.1, v: 150.1,
lset: labels.FromStrings("__name__", "foobar", "quantile", "0.99"), lset: labels.FromStrings("__name__", "foobar", "quantile", "0.99"),
ct: int64p(1520430004000), ct: 1520430004000,
}, { }, {
m: "metric", m: "metric",
help: "foo\x00bar", help: "foo\x00bar",
@ -507,12 +507,12 @@ quotedexemplar2_count 1 # {"id.thing"="histogram-count-test",other="hello"} 4
m: `{"go.gc_duration_seconds",quantile="0"}`, m: `{"go.gc_duration_seconds",quantile="0"}`,
v: 4.9351e-05, v: 4.9351e-05,
lset: labels.FromStrings("__name__", "go.gc_duration_seconds", "quantile", "0.0"), lset: labels.FromStrings("__name__", "go.gc_duration_seconds", "quantile", "0.0"),
ct: int64p(1520872607123), ct: 1520872607123,
}, { }, {
m: `{"go.gc_duration_seconds",quantile="0.25"}`, m: `{"go.gc_duration_seconds",quantile="0.25"}`,
v: 7.424100000000001e-05, v: 7.424100000000001e-05,
lset: labels.FromStrings("__name__", "go.gc_duration_seconds", "quantile", "0.25"), lset: labels.FromStrings("__name__", "go.gc_duration_seconds", "quantile", "0.25"),
ct: int64p(1520872607123), ct: 1520872607123,
}, { }, {
m: `{"go.gc_duration_seconds",quantile="0.5",a="b"}`, m: `{"go.gc_duration_seconds",quantile="0.5",a="b"}`,
v: 8.3835e-05, v: 8.3835e-05,
@ -966,19 +966,19 @@ thing_c_total 14123.232
}, },
{ {
m: `thing_count`, m: `thing_count`,
ct: nil, // Should be int64p(1520872607123). ct: 0, // Should be int64p(1520872607123).
}, },
{ {
m: `thing_sum`, m: `thing_sum`,
ct: nil, // Should be int64p(1520872607123). ct: 0, // Should be int64p(1520872607123).
}, },
{ {
m: `thing_bucket{le="0.0"}`, m: `thing_bucket{le="0.0"}`,
ct: nil, // Should be int64p(1520872607123). ct: 0, // Should be int64p(1520872607123).
}, },
{ {
m: `thing_bucket{le="+Inf"}`, m: `thing_bucket{le="+Inf"}`,
ct: nil, // Should be int64p(1520872607123), ct: 0, // Should be int64p(1520872607123),
}, },
{ {
m: "thing_c", m: "thing_c",
@ -990,7 +990,7 @@ thing_c_total 14123.232
}, },
{ {
m: `thing_c_total`, m: `thing_c_total`,
ct: nil, // Should be int64p(1520872607123). ct: 0, // Should be int64p(1520872607123).
}, },
}, },
}, },

View file

@ -253,10 +253,10 @@ func (p *PromParser) Exemplar(*exemplar.Exemplar) bool {
return false return false
} }
// CreatedTimestamp returns nil as it's not implemented yet. // CreatedTimestamp returns 0 as it's not implemented yet.
// TODO(bwplotka): https://github.com/prometheus/prometheus/issues/12980 // TODO(bwplotka): https://github.com/prometheus/prometheus/issues/12980
func (p *PromParser) CreatedTimestamp() *int64 { func (p *PromParser) CreatedTimestamp() int64 {
return nil return 0
} }
// nextToken returns the next token from the promlexer. It skips over tabs // nextToken returns the next token from the promlexer. It skips over tabs

View file

@ -379,9 +379,8 @@ func (p *ProtobufParser) Exemplar(ex *exemplar.Exemplar) bool {
return true return true
} }
// CreatedTimestamp returns CT or nil if CT is not present or // CreatedTimestamp returns CT or 0 if CT is not present on counters, summaries or histograms.
// invalid (as timestamp e.g. negative value) on counters, summaries or histograms. func (p *ProtobufParser) CreatedTimestamp() int64 {
func (p *ProtobufParser) CreatedTimestamp() *int64 {
var ct *types.Timestamp var ct *types.Timestamp
switch p.dec.GetType() { switch p.dec.GetType() {
case dto.MetricType_COUNTER: case dto.MetricType_COUNTER:
@ -392,13 +391,12 @@ func (p *ProtobufParser) CreatedTimestamp() *int64 {
ct = p.dec.GetHistogram().GetCreatedTimestamp() ct = p.dec.GetHistogram().GetCreatedTimestamp()
default: default:
} }
ctAsTime, err := types.TimestampFromProto(ct) if ct == nil {
if err != nil { return 0
// Errors means ct == nil or invalid timestamp, which we silently ignore.
return nil
} }
ctMilis := ctAsTime.UnixMilli() // Same as the gogo proto types.TimestampFromProto but straight to integer.
return &ctMilis // and without validation.
return ct.GetSeconds()*1e3 + int64(ct.GetNanos())/1e6
} }
// Next advances the parser to the next "sample" (emulating the behavior of a // Next advances the parser to the next "sample" (emulating the behavior of a

View file

@ -582,8 +582,8 @@ metric: <
counter: < counter: <
value: 42 value: 42
created_timestamp: < created_timestamp: <
seconds: 1 seconds: 1625851153
nanos: 1 nanos: 146848499
> >
> >
> >
@ -597,8 +597,8 @@ metric: <
sample_count: 42 sample_count: 42
sample_sum: 1.234 sample_sum: 1.234
created_timestamp: < created_timestamp: <
seconds: 1 seconds: 1625851153
nanos: 1 nanos: 146848499
> >
> >
> >
@ -610,8 +610,8 @@ type: HISTOGRAM
metric: < metric: <
histogram: < histogram: <
created_timestamp: < created_timestamp: <
seconds: 1 seconds: 1625851153
nanos: 1 nanos: 146848499
> >
positive_span: < positive_span: <
offset: 0 offset: 0
@ -627,8 +627,8 @@ type: GAUGE_HISTOGRAM
metric: < metric: <
histogram: < histogram: <
created_timestamp: < created_timestamp: <
seconds: 1 seconds: 1625851153
nanos: 1 nanos: 146848499
> >
positive_span: < positive_span: <
offset: 0 offset: 0
@ -1324,7 +1324,7 @@ func TestProtobufParse(t *testing.T) {
{ {
m: "test_counter_with_createdtimestamp", m: "test_counter_with_createdtimestamp",
v: 42, v: 42,
ct: int64p(1000), ct: 1625851153146,
lset: labels.FromStrings( lset: labels.FromStrings(
"__name__", "test_counter_with_createdtimestamp", "__name__", "test_counter_with_createdtimestamp",
), ),
@ -1340,7 +1340,7 @@ func TestProtobufParse(t *testing.T) {
{ {
m: "test_summary_with_createdtimestamp_count", m: "test_summary_with_createdtimestamp_count",
v: 42, v: 42,
ct: int64p(1000), ct: 1625851153146,
lset: labels.FromStrings( lset: labels.FromStrings(
"__name__", "test_summary_with_createdtimestamp_count", "__name__", "test_summary_with_createdtimestamp_count",
), ),
@ -1348,7 +1348,7 @@ func TestProtobufParse(t *testing.T) {
{ {
m: "test_summary_with_createdtimestamp_sum", m: "test_summary_with_createdtimestamp_sum",
v: 1.234, v: 1.234,
ct: int64p(1000), ct: 1625851153146,
lset: labels.FromStrings( lset: labels.FromStrings(
"__name__", "test_summary_with_createdtimestamp_sum", "__name__", "test_summary_with_createdtimestamp_sum",
), ),
@ -1363,7 +1363,7 @@ func TestProtobufParse(t *testing.T) {
}, },
{ {
m: "test_histogram_with_createdtimestamp", m: "test_histogram_with_createdtimestamp",
ct: int64p(1000), ct: 1625851153146,
shs: &histogram.Histogram{ shs: &histogram.Histogram{
CounterResetHint: histogram.UnknownCounterReset, CounterResetHint: histogram.UnknownCounterReset,
PositiveSpans: []histogram.Span{}, PositiveSpans: []histogram.Span{},
@ -1383,7 +1383,7 @@ func TestProtobufParse(t *testing.T) {
}, },
{ {
m: "test_gaugehistogram_with_createdtimestamp", m: "test_gaugehistogram_with_createdtimestamp",
ct: int64p(1000), ct: 1625851153146,
shs: &histogram.Histogram{ shs: &histogram.Histogram{
CounterResetHint: histogram.GaugeType, CounterResetHint: histogram.GaugeType,
PositiveSpans: []histogram.Span{}, PositiveSpans: []histogram.Span{},
@ -2277,7 +2277,7 @@ func TestProtobufParse(t *testing.T) {
{ {
m: "test_counter_with_createdtimestamp", m: "test_counter_with_createdtimestamp",
v: 42, v: 42,
ct: int64p(1000), ct: 1625851153146,
lset: labels.FromStrings( lset: labels.FromStrings(
"__name__", "test_counter_with_createdtimestamp", "__name__", "test_counter_with_createdtimestamp",
), ),
@ -2293,7 +2293,7 @@ func TestProtobufParse(t *testing.T) {
{ {
m: "test_summary_with_createdtimestamp_count", m: "test_summary_with_createdtimestamp_count",
v: 42, v: 42,
ct: int64p(1000), ct: 1625851153146,
lset: labels.FromStrings( lset: labels.FromStrings(
"__name__", "test_summary_with_createdtimestamp_count", "__name__", "test_summary_with_createdtimestamp_count",
), ),
@ -2301,7 +2301,7 @@ func TestProtobufParse(t *testing.T) {
{ {
m: "test_summary_with_createdtimestamp_sum", m: "test_summary_with_createdtimestamp_sum",
v: 1.234, v: 1.234,
ct: int64p(1000), ct: 1625851153146,
lset: labels.FromStrings( lset: labels.FromStrings(
"__name__", "test_summary_with_createdtimestamp_sum", "__name__", "test_summary_with_createdtimestamp_sum",
), ),
@ -2316,7 +2316,7 @@ func TestProtobufParse(t *testing.T) {
}, },
{ {
m: "test_histogram_with_createdtimestamp", m: "test_histogram_with_createdtimestamp",
ct: int64p(1000), ct: 1625851153146,
shs: &histogram.Histogram{ shs: &histogram.Histogram{
CounterResetHint: histogram.UnknownCounterReset, CounterResetHint: histogram.UnknownCounterReset,
PositiveSpans: []histogram.Span{}, PositiveSpans: []histogram.Span{},
@ -2336,7 +2336,7 @@ func TestProtobufParse(t *testing.T) {
}, },
{ {
m: "test_gaugehistogram_with_createdtimestamp", m: "test_gaugehistogram_with_createdtimestamp",
ct: int64p(1000), ct: 1625851153146,
shs: &histogram.Histogram{ shs: &histogram.Histogram{
CounterResetHint: histogram.GaugeType, CounterResetHint: histogram.GaugeType,
PositiveSpans: []histogram.Span{}, PositiveSpans: []histogram.Span{},

View file

@ -1747,20 +1747,20 @@ loop:
err = storage.ErrDuplicateSampleForTimestamp err = storage.ErrDuplicateSampleForTimestamp
} else { } else {
if sl.enableCTZeroIngestion { if sl.enableCTZeroIngestion {
if ctMs := p.CreatedTimestamp(); ctMs != nil { if ctMs := p.CreatedTimestamp(); ctMs != 0 {
if isHistogram && sl.enableNativeHistogramIngestion { if isHistogram && sl.enableNativeHistogramIngestion {
if h != nil { if h != nil {
ref, err = app.AppendHistogramCTZeroSample(ref, lset, t, *ctMs, h, nil) ref, err = app.AppendHistogramCTZeroSample(ref, lset, t, ctMs, h, nil)
} else { } else {
ref, err = app.AppendHistogramCTZeroSample(ref, lset, t, *ctMs, nil, fh) ref, err = app.AppendHistogramCTZeroSample(ref, lset, t, ctMs, nil, fh)
} }
} else { } else {
ref, err = app.AppendCTZeroSample(ref, lset, t, *ctMs) ref, err = app.AppendCTZeroSample(ref, lset, t, ctMs)
} }
if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { // OOO is a common case, ignoring completely for now. if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { // OOO is a common case, ignoring completely for now.
// CT is an experimental feature. For now, we don't need to fail the // CT is an experimental feature. For now, we don't need to fail the
// scrape on errors updating the created timestamp, log debug. // scrape on errors updating the created timestamp, log debug.
sl.l.Debug("Error when appending CT in scrape loop", "series", string(met), "ct", *ctMs, "t", t, "err", err) sl.l.Debug("Error when appending CT in scrape loop", "series", string(met), "ct", ctMs, "t", t, "err", err)
} }
} }
} }