labels: simplify call to get Labels from Builder

It took a `Labels` where the memory could be re-used, but in practice
this hardly ever benefitted. Especially after converting `relabel.Process`
to `relabel.ProcessBuilder`.

Comparing the parameter to `nil` was a bug; `EmptyLabels` is not `nil`
so the slice was reallocated multiple times by `append`.

Lastly `Builder.Labels()` now estimates that the final size will depend
on labels added and deleted.

Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
This commit is contained in:
Bryan Boreham 2023-03-22 15:46:02 +00:00
parent 5d90dc0627
commit b987afa7ef
17 changed files with 39 additions and 50 deletions

View file

@ -163,7 +163,7 @@ func (importer *ruleImporter) importRule(ctx context.Context, ruleExpr, ruleName
})
lb.Set(labels.MetricName, ruleName)
lbls := lb.Labels(labels.EmptyLabels())
lbls := lb.Labels()
for _, value := range sample.Values {
if err := app.add(ctx, lbls, timestamp.FromTime(value.Timestamp.Time()), float64(value.Value)); err != nil {

View file

@ -570,24 +570,14 @@ func contains(s []Label, n string) bool {
return false
}
// Labels returns the labels from the builder, adding them to res if non-nil.
// Argument res can be the same as b.base, if caller wants to overwrite that slice.
// Labels returns the labels from the builder.
// If no modifications were made, the original labels are returned.
func (b *Builder) Labels(res Labels) Labels {
func (b *Builder) Labels() Labels {
if len(b.del) == 0 && len(b.add) == 0 {
return b.base
}
if res == nil {
// In the general case, labels are removed, modified or moved
// rather than added.
res = make(Labels, 0, len(b.base))
} else {
res = res[:0]
}
// Justification that res can be the same slice as base: in this loop
// we move forward through base, and either skip an element or assign
// it to res at its current position or an earlier position.
res := make(Labels, 0, len(b.base)-len(b.del)+len(b.add))
for _, l := range b.base {
if slices.Contains(b.del, l.Name) || contains(b.add, l.Name) {
continue

View file

@ -158,7 +158,7 @@ func (ls Labels) MatchLabels(on bool, names ...string) Labels {
b.Del(MetricName)
b.Del(names...)
}
return b.Labels(EmptyLabels())
return b.Labels()
}
// Hash returns a hash value for the label set.
@ -624,10 +624,9 @@ func contains(s []Label, n string) bool {
return false
}
// Labels returns the labels from the builder, adding them to res if non-nil.
// Argument res can be the same as b.base, if caller wants to overwrite that slice.
// Labels returns the labels from the builder.
// If no modifications were made, the original labels are returned.
func (b *Builder) Labels(res Labels) Labels {
func (b *Builder) Labels() Labels {
if len(b.del) == 0 && len(b.add) == 0 {
return b.base
}
@ -637,7 +636,7 @@ func (b *Builder) Labels(res Labels) Labels {
a, d := 0, 0
bufSize := len(b.base.data) + labelsSize(b.add)
buf := make([]byte, 0, bufSize) // TODO: see if we can re-use the buffer from res.
buf := make([]byte, 0, bufSize)
for pos := 0; pos < len(b.base.data); {
oldPos := pos
var lName string

View file

@ -596,7 +596,7 @@ func TestBuilder(t *testing.T) {
b.Keep(tcase.keep...)
}
b.Del(tcase.del...)
require.Equal(t, tcase.want, b.Labels(EmptyLabels()))
require.Equal(t, tcase.want, b.Labels())
// Check what happens when we call Range and mutate the builder.
b.Range(func(l Label) {
@ -604,7 +604,7 @@ func TestBuilder(t *testing.T) {
b.Del(l.Name)
}
})
require.Equal(t, tcase.want.BytesWithoutLabels(nil, "aaa", "bbb"), b.Labels(tcase.base).Bytes(nil))
require.Equal(t, tcase.want.BytesWithoutLabels(nil, "aaa", "bbb"), b.Labels().Bytes(nil))
})
}
}
@ -669,7 +669,7 @@ func BenchmarkLabels_Hash(b *testing.B) {
// Label ~20B name, 50B value.
b.Set(fmt.Sprintf("abcdefghijabcdefghijabcdefghij%d", i), fmt.Sprintf("abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij%d", i))
}
return b.Labels(EmptyLabels())
return b.Labels()
}(),
},
{
@ -680,7 +680,7 @@ func BenchmarkLabels_Hash(b *testing.B) {
// Label ~50B name, 50B value.
b.Set(fmt.Sprintf("abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij%d", i), fmt.Sprintf("abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij%d", i))
}
return b.Labels(EmptyLabels())
return b.Labels()
}(),
},
{
@ -729,7 +729,7 @@ func BenchmarkBuilder(b *testing.B) {
for _, l := range m {
builder.Set(l.Name, l.Value)
}
l = builder.Labels(EmptyLabels())
l = builder.Labels()
}
require.Equal(b, 9, l.Len())
}

View file

@ -211,7 +211,7 @@ func Process(lbls labels.Labels, cfgs ...*Config) (ret labels.Labels, keep bool)
if !ProcessBuilder(lb, cfgs...) {
return labels.EmptyLabels(), false
}
return lb.Labels(lbls), true
return lb.Labels(), true
}
// ProcessBuilder is like Process, but the caller passes a labels.Builder

View file

@ -359,7 +359,7 @@ func (n *Manager) Send(alerts ...*Alert) {
}
})
a.Labels = lb.Labels(a.Labels)
a.Labels = lb.Labels()
}
alerts = n.relabelAlerts(alerts)

View file

@ -2193,7 +2193,7 @@ func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.V
}
}
ret := enh.lb.Labels(labels.EmptyLabels())
ret := enh.lb.Labels()
enh.resultMetric[str] = ret
return ret
}
@ -2233,7 +2233,7 @@ func (ev *evaluator) VectorscalarBinop(op parser.ItemType, lhs Vector, rhs Scala
}
func dropMetricName(l labels.Labels) labels.Labels {
return labels.NewBuilder(l).Del(labels.MetricName).Labels(labels.EmptyLabels())
return labels.NewBuilder(l).Del(labels.MetricName).Labels()
}
// scalarBinop evaluates a binary operation between two Scalars.
@ -2367,7 +2367,7 @@ func (ev *evaluator) aggregation(op parser.ItemType, grouping []string, without
if op == parser.COUNT_VALUES {
enh.resetBuilder(metric)
enh.lb.Set(valueLabel, strconv.FormatFloat(s.V, 'f', -1, 64))
metric = enh.lb.Labels(labels.EmptyLabels())
metric = enh.lb.Labels()
// We've changed the metric so we have to recompute the grouping key.
recomputeGroupingKey = true
@ -2389,10 +2389,10 @@ func (ev *evaluator) aggregation(op parser.ItemType, grouping []string, without
if without {
enh.lb.Del(grouping...)
enh.lb.Del(labels.MetricName)
m = enh.lb.Labels(labels.EmptyLabels())
m = enh.lb.Labels()
} else if len(grouping) > 0 {
enh.lb.Keep(grouping...)
m = enh.lb.Labels(labels.EmptyLabels())
m = enh.lb.Labels()
} else {
m = labels.EmptyLabels()
}

View file

@ -960,7 +960,7 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev
if !ok {
sample.Metric = labels.NewBuilder(sample.Metric).
Del(excludedLabels...).
Labels(labels.EmptyLabels())
Labels()
mb = &metricWithBuckets{sample.Metric, nil}
enh.signatureToMetricWithBuckets[string(enh.lblBuf)] = mb
@ -1080,7 +1080,7 @@ func funcLabelReplace(vals []parser.Value, args parser.Expressions, enh *EvalNod
if len(res) > 0 {
lb.Set(dst, string(res))
}
outMetric = lb.Labels(labels.EmptyLabels())
outMetric = lb.Labels()
enh.Dmn[h] = outMetric
}
}
@ -1148,7 +1148,7 @@ func funcLabelJoin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHe
lb.Set(dst, strval)
}
outMetric = lb.Labels(labels.EmptyLabels())
outMetric = lb.Labels()
enh.Dmn[h] = outMetric
}
@ -1414,7 +1414,7 @@ func createLabelsForAbsentFunction(expr parser.Expr) labels.Labels {
}
}
return b.Labels(labels.EmptyLabels())
return b.Labels()
}
func stringFromArg(e parser.Expr) string {

View file

@ -567,7 +567,7 @@ label_matcher : IDENTIFIER match_op STRING
*/
metric : metric_identifier label_set
{ b := labels.NewBuilder($2); b.Set(labels.MetricName, $1.Val); $$ = b.Labels(labels.EmptyLabels()) }
{ b := labels.NewBuilder($2); b.Set(labels.MetricName, $1.Val); $$ = b.Labels() }
| label_set
{$$ = $1}
;

View file

@ -1494,7 +1494,7 @@ yydefault:
{
b := labels.NewBuilder(yyDollar[2].labels)
b.Set(labels.MetricName, yyDollar[1].item.Val)
yyVAL.labels = b.Labels(labels.EmptyLabels())
yyVAL.labels = b.Labels()
}
case 96:
yyDollar = yyS[yypt-1 : yypt+1]

View file

@ -234,7 +234,7 @@ func (r *AlertingRule) sample(alert *Alert, ts time.Time) promql.Sample {
lb.Set(alertStateLabel, alert.State.String())
s := promql.Sample{
Metric: lb.Labels(labels.EmptyLabels()),
Metric: lb.Labels(),
Point: promql.Point{T: timestamp.FromTime(ts), V: 1},
}
return s
@ -252,7 +252,7 @@ func (r *AlertingRule) forStateSample(alert *Alert, ts time.Time, v float64) pro
lb.Set(labels.AlertName, r.name)
s := promql.Sample{
Metric: lb.Labels(labels.EmptyLabels()),
Metric: lb.Labels(),
Point: promql.Point{T: timestamp.FromTime(ts), V: v},
}
return s
@ -381,7 +381,7 @@ func (r *AlertingRule) Eval(ctx context.Context, ts time.Time, query QueryFunc,
})
annotations := sb.Labels()
lbs := lb.Labels(labels.EmptyLabels())
lbs := lb.Labels()
h := lbs.Hash()
resultFPs[h] = struct{}{}

View file

@ -93,7 +93,7 @@ func (rule *RecordingRule) Eval(ctx context.Context, ts time.Time, query QueryFu
lb.Set(l.Name, l.Value)
})
sample.Metric = lb.Labels(sample.Metric)
sample.Metric = lb.Labels()
}
// Check that the rule does not produce identical metrics after applying

View file

@ -692,7 +692,7 @@ func mutateSampleLabels(lset labels.Labels, target *Target, honor bool, rc []*re
}
}
res := lb.Labels(labels.EmptyLabels())
res := lb.Labels()
if len(rc) > 0 {
res, _ = relabel.Process(res, rc...)
@ -726,7 +726,7 @@ func mutateReportSampleLabels(lset labels.Labels, target *Target) labels.Labels
lb.Set(l.Name, l.Value)
})
return lb.Labels(labels.EmptyLabels())
return lb.Labels()
}
// appender returns an appender for ingested samples from the target.

View file

@ -380,7 +380,7 @@ func PopulateLabels(lb *labels.Builder, cfg *config.ScrapeConfig, noDefaultPort
}
}
preRelabelLabels := lb.Labels(labels.EmptyLabels())
preRelabelLabels := lb.Labels()
keep := relabel.ProcessBuilder(lb, cfg.RelabelConfigs...)
// Check if the target was dropped.
@ -476,7 +476,7 @@ func PopulateLabels(lb *labels.Builder, cfg *config.ScrapeConfig, noDefaultPort
lb.Set(model.InstanceLabel, addr)
}
res = lb.Labels(labels.EmptyLabels())
res = lb.Labels()
err = res.Validate(func(l labels.Label) error {
// Check label values are valid, drop the target if not.
if !model.LabelValue(l.Value).IsValid() {

View file

@ -140,7 +140,7 @@ func newTestTarget(targetURL string, deadline time.Duration, lbls labels.Labels)
lb.Set(model.AddressLabel, strings.TrimPrefix(targetURL, "http://"))
lb.Set(model.MetricsPathLabel, "/metrics")
return &Target{labels: lb.Labels(labels.EmptyLabels())}
return &Target{labels: lb.Labels()}
}
func TestNewHTTPBearerToken(t *testing.T) {

View file

@ -278,5 +278,5 @@ func (sf seriesFilter) Labels() labels.Labels {
b := labels.NewBuilder(sf.Series.Labels())
// todo: check if this is too inefficient.
b.Del(sf.toFilter...)
return b.Labels(labels.EmptyLabels())
return b.Labels()
}

View file

@ -1569,20 +1569,20 @@ func TestSparseHistogramSpaceSavings(t *testing.T) {
for it.Next() {
numOldSeriesPerHistogram++
b := it.At()
lbls := labels.NewBuilder(ah.baseLabels).Set("le", fmt.Sprintf("%.16f", b.Upper)).Labels(labels.EmptyLabels())
lbls := labels.NewBuilder(ah.baseLabels).Set("le", fmt.Sprintf("%.16f", b.Upper)).Labels()
refs[itIdx], err = oldApp.Append(refs[itIdx], lbls, ts, float64(b.Count))
require.NoError(t, err)
itIdx++
}
baseName := ah.baseLabels.Get(labels.MetricName)
// _count metric.
countLbls := labels.NewBuilder(ah.baseLabels).Set(labels.MetricName, baseName+"_count").Labels(labels.EmptyLabels())
countLbls := labels.NewBuilder(ah.baseLabels).Set(labels.MetricName, baseName+"_count").Labels()
_, err = oldApp.Append(0, countLbls, ts, float64(h.Count))
require.NoError(t, err)
numOldSeriesPerHistogram++
// _sum metric.
sumLbls := labels.NewBuilder(ah.baseLabels).Set(labels.MetricName, baseName+"_sum").Labels(labels.EmptyLabels())
sumLbls := labels.NewBuilder(ah.baseLabels).Set(labels.MetricName, baseName+"_sum").Labels()
_, err = oldApp.Append(0, sumLbls, ts, h.Sum)
require.NoError(t, err)
numOldSeriesPerHistogram++