mirror of
https://github.com/prometheus/prometheus.git
synced 2026-02-03 20:39:32 -05:00
fix: optimizations to AppenderV2
Signed-off-by: bwplotka <bwplotka@gmail.com> fix: 48B alloc optimization to AppenderV2 Signed-off-by: bwplotka <bwplotka@gmail.com>
This commit is contained in:
parent
3cb378256d
commit
3f81c22189
3 changed files with 32 additions and 26 deletions
|
|
@ -32,6 +32,8 @@ import (
|
|||
"github.com/prometheus/prometheus/storage"
|
||||
)
|
||||
|
||||
const exemplarsForParsing = 1 // const for readability.
|
||||
|
||||
// appenderWithLimits returns an appender with additional validation.
|
||||
func appenderV2WithLimits(app storage.AppenderV2, sampleLimit, bucketLimit int, maxSchema int32) storage.AppenderV2 {
|
||||
app = &timeLimitAppenderV2{
|
||||
|
|
@ -122,17 +124,18 @@ func (sl *scrapeLoopAppenderV2) append(b []byte, contentType string, ts time.Tim
|
|||
"err", err,
|
||||
)
|
||||
}
|
||||
|
||||
var (
|
||||
appErrs = appendErrors{}
|
||||
sampleLimitErr error
|
||||
bucketLimitErr error
|
||||
lset labels.Labels // Escapes to heap so hoisted out of loop.
|
||||
e exemplar.Exemplar // Escapes to heap so hoisted out of loop.
|
||||
lastMeta *metaEntry
|
||||
lastMFName []byte
|
||||
)
|
||||
|
||||
exemplars := make([]exemplar.Exemplar, 0, 1)
|
||||
// Below variables escape to heap, so they're hoisted out of loop.
|
||||
lset labels.Labels
|
||||
exemplars = make([]exemplar.Exemplar, exemplarsForParsing) // First sample is a buffer for parser fetches.
|
||||
)
|
||||
|
||||
// Take an appender with limits.
|
||||
app := appenderV2WithLimits(sl.AppenderV2, sl.sampleLimit, sl.bucketLimit, sl.maxSchema)
|
||||
|
|
@ -240,8 +243,7 @@ loop:
|
|||
}
|
||||
}
|
||||
|
||||
exemplars = exemplars[:0] // Reset and reuse the exemplar slice.
|
||||
|
||||
appOpts := storage.AOptions{}
|
||||
if seriesAlreadyScraped && parsedTimestamp == nil {
|
||||
err = storage.ErrDuplicateSampleForTimestamp
|
||||
} else {
|
||||
|
|
@ -259,8 +261,14 @@ loop:
|
|||
st = p.StartTimestamp()
|
||||
}
|
||||
|
||||
for hasExemplar := p.Exemplar(&e); hasExemplar; hasExemplar = p.Exemplar(&e) {
|
||||
if !e.HasTs {
|
||||
// Fetch exemplars from the parser, if any, while using exemplar slice.
|
||||
exemplars = exemplars[:exemplarsForParsing]
|
||||
for {
|
||||
exemplars[0] = exemplar.Exemplar{} // Always reset, before fetching.
|
||||
if !p.Exemplar(&(exemplars[0])) {
|
||||
break
|
||||
}
|
||||
if !exemplars[0].HasTs {
|
||||
if isHistogram {
|
||||
// We drop exemplars for native histograms if they don't have a timestamp.
|
||||
// Missing timestamps are deliberately not supported as we want to start
|
||||
|
|
@ -269,21 +277,18 @@ loop:
|
|||
// between repeated exemplars and new instances with the same values.
|
||||
// This is done silently without logs as it is not an error but out of spec.
|
||||
// This does not affect classic histograms so that behaviour is unchanged.
|
||||
e = exemplar.Exemplar{} // Reset for the next fetch.
|
||||
continue
|
||||
}
|
||||
e.Ts = t
|
||||
exemplars[0].Ts = t
|
||||
}
|
||||
exemplars = append(exemplars, e)
|
||||
e = exemplar.Exemplar{} // Reset for the next fetch.
|
||||
exemplars = append(exemplars, exemplars[0])
|
||||
}
|
||||
|
||||
// Prepare append call.
|
||||
appOpts := storage.AOptions{}
|
||||
if len(exemplars) > 0 {
|
||||
if len(exemplars) > exemplarsForParsing {
|
||||
// Sort so that checking for duplicates / out of order is more efficient during validation.
|
||||
slices.SortFunc(exemplars, exemplar.Compare)
|
||||
appOpts.Exemplars = exemplars
|
||||
appOpts.Exemplars = exemplars[exemplarsForParsing:]
|
||||
slices.SortFunc(appOpts.Exemplars, exemplar.Compare)
|
||||
}
|
||||
|
||||
// Metadata path mimicks the scrape appender V1 flow. Once we remove v2
|
||||
|
|
@ -314,7 +319,7 @@ loop:
|
|||
// Append sample to the storage.
|
||||
ref, err = app.Append(ref, lset, st, t, val, h, fh, appOpts)
|
||||
}
|
||||
sampleAdded, err = sl.checkAddError(met, exemplars, err, &sampleLimitErr, &bucketLimitErr, &appErrs)
|
||||
sampleAdded, err = sl.checkAddError(met, appOpts.Exemplars, err, &sampleLimitErr, &bucketLimitErr, &appErrs)
|
||||
if err != nil {
|
||||
if !errors.Is(err, storage.ErrNotFound) {
|
||||
sl.l.Debug("Unexpected error", "series", string(met), "err", err)
|
||||
|
|
|
|||
|
|
@ -1597,7 +1597,7 @@ func BenchmarkScrapeLoopAppend(b *testing.B) {
|
|||
// noopCompactAppendable is a bare minimum appender mock used to isolate scrape performance.
|
||||
type noopCompactAppendable struct{}
|
||||
|
||||
func (n *noopCompactAppendable) handleRef(ref storage.SeriesRef, l labels.Labels) storage.SeriesRef {
|
||||
func (*noopCompactAppendable) handleRef(ref storage.SeriesRef, l labels.Labels) storage.SeriesRef {
|
||||
if ref != 0 {
|
||||
return ref
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,22 +109,23 @@ type headAppenderV2 struct {
|
|||
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 (
|
||||
// Avoid shadowing err variables for reliability.
|
||||
valErr, appErr, partialErr error
|
||||
sampleMetricType = sampleMetricTypeFloat
|
||||
isStale bool
|
||||
appErr, partialErr error
|
||||
sampleMetricType = sampleMetricTypeFloat
|
||||
isStale bool
|
||||
)
|
||||
// Fail fast on incorrect histograms.
|
||||
|
||||
switch {
|
||||
case fh != nil:
|
||||
sampleMetricType = sampleMetricTypeHistogram
|
||||
valErr = fh.Validate()
|
||||
if err := fh.Validate(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
case h != nil:
|
||||
sampleMetricType = sampleMetricTypeHistogram
|
||||
valErr = h.Validate()
|
||||
}
|
||||
if valErr != nil {
|
||||
return 0, valErr
|
||||
if err := h.Validate(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// Fail fast if OOO is disabled and the sample is out of bounds.
|
||||
|
|
|
|||
Loading…
Reference in a new issue