diff --git a/prometheus/histogram.go b/prometheus/histogram.go index f88da707b..45f35177e 100644 --- a/prometheus/histogram.go +++ b/prometheus/histogram.go @@ -360,7 +360,7 @@ func (h *histogram) Write(out *dto.Metric) error { his.Bucket = buckets out.Histogram = his - out.Label = h.labelPairs + out.Label = copyLabelPairs(h.labelPairs) // Finally add all the cold counts to the new hot counts and reset the cold counts. atomic.AddUint64(&hotCounts.count, count) diff --git a/prometheus/registry.go b/prometheus/registry.go index f98c81a86..540759b01 100644 --- a/prometheus/registry.go +++ b/prometheus/registry.go @@ -872,7 +872,7 @@ func checkMetricConsistency( h = hashAddByte(h, separatorByte) // Make sure label pairs are sorted. We depend on it for the consistency // check. - sort.Sort(labelPairSorter(dtoMetric.Label)) + sort.Stable(labelPairSorter(dtoMetric.Label)) for _, lp := range dtoMetric.Label { h = hashAdd(h, lp.GetName()) h = hashAddByte(h, separatorByte) diff --git a/prometheus/registry_test.go b/prometheus/registry_test.go index a96cb10a6..5f0748242 100644 --- a/prometheus/registry_test.go +++ b/prometheus/registry_test.go @@ -791,7 +791,7 @@ func TestHistogramVecRegisterGatherConcurrency(t *testing.T) { Help: "This helps testing.", ConstLabels: prometheus.Labels{"foo": "bar"}, }, - []string{"one", "two", "three"}, + []string{"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen"}, ) labelValues = []string{"a", "b", "c", "alpha", "beta", "gamma", "aleph", "beth", "gimel"} quit = make(chan struct{}) @@ -810,6 +810,19 @@ func TestHistogramVecRegisterGatherConcurrency(t *testing.T) { labelValues[rand.Intn(len(labelValues))], labelValues[rand.Intn(len(labelValues))], labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], + labelValues[rand.Intn(len(labelValues))], ).Observe(obs) } } @@ -848,7 +861,7 @@ func TestHistogramVecRegisterGatherConcurrency(t *testing.T) { if len(g) != 1 { t.Error("Gathered unexpected number of metric families:", len(g)) } - if len(g[0].Metric[0].Label) != 4 { + if len(g[0].Metric[0].Label) != 17 { t.Error("Gathered unexpected number of label pairs:", len(g[0].Metric[0].Label)) } } @@ -869,7 +882,7 @@ func TestHistogramVecRegisterGatherConcurrency(t *testing.T) { go gather() go observe() - time.Sleep(time.Second) + time.Sleep(2 * time.Second) close(quit) wg.Wait() } diff --git a/prometheus/value.go b/prometheus/value.go index eb248f108..35d0ba790 100644 --- a/prometheus/value.go +++ b/prometheus/value.go @@ -125,7 +125,7 @@ func populateMetric( labelPairs []*dto.LabelPair, m *dto.Metric, ) error { - m.Label = labelPairs + m.Label = copyLabelPairs(labelPairs) switch t { case CounterValue: m.Counter = &dto.Counter{Value: proto.Float64(v)} @@ -160,3 +160,15 @@ func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair { sort.Sort(labelPairSorter(labelPairs)) return labelPairs } + +func copyLabelPairs(source []*dto.LabelPair) []*dto.LabelPair { + labelPairs := make([]*dto.LabelPair, 0, len(source)) + for _, pair := range source { + labelPairs = append(labelPairs, &dto.LabelPair{ + Name: pair.Name, + Value: pair.Value, + }) + } + // shouldn't need sorting, as it should already be sorted + return labelPairs +}