Update prometheus/client_golang vendoring (#1099)

This is mostly required to fix a bug with histograms on 32bit platforms.
(Which might or might not be used in node_exporter. Just in case...)

Signed-off-by: beorn7 <beorn@soundcloud.com>
This commit is contained in:
Björn Rabenstein 2018-10-05 16:05:02 +02:00 committed by Ben Kochie
parent 01ec8c5c5c
commit bddf41d327
4 changed files with 60 additions and 41 deletions

View File

@ -40,7 +40,8 @@ type Collector interface {
// Collector may yield any Metric it sees fit in its Collect method. // Collector may yield any Metric it sees fit in its Collect method.
// //
// This method idempotently sends the same descriptors throughout the // This method idempotently sends the same descriptors throughout the
// lifetime of the Collector. // lifetime of the Collector. It may be called concurrently and
// therefore must be implemented in a concurrency safe way.
// //
// If a Collector encounters an error while executing this method, it // If a Collector encounters an error while executing this method, it
// must send an invalid descriptor (created with NewInvalidDesc) to // must send an invalid descriptor (created with NewInvalidDesc) to

View File

@ -187,6 +187,7 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
desc: desc, desc: desc,
upperBounds: opts.Buckets, upperBounds: opts.Buckets,
labelPairs: makeLabelPairs(desc, labelValues), labelPairs: makeLabelPairs(desc, labelValues),
counts: [2]*histogramCounts{&histogramCounts{}, &histogramCounts{}},
} }
for i, upperBound := range h.upperBounds { for i, upperBound := range h.upperBounds {
if i < len(h.upperBounds)-1 { if i < len(h.upperBounds)-1 {
@ -223,6 +224,21 @@ type histogramCounts struct {
} }
type histogram struct { type histogram struct {
// countAndHotIdx is a complicated one. For lock-free yet atomic
// observations, we need to save the total count of observations again,
// combined with the index of the currently-hot counts struct, so that
// we can perform the operation on both values atomically. The least
// significant bit defines the hot counts struct. The remaining 63 bits
// represent the total count of observations. This happens under the
// assumption that the 63bit count will never overflow. Rationale: An
// observations takes about 30ns. Let's assume it could happen in
// 10ns. Overflowing the counter will then take at least (2^63)*10ns,
// which is about 3000 years.
//
// This has to be first in the struct for 64bit alignment. See
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
countAndHotIdx uint64
selfCollector selfCollector
desc *Desc desc *Desc
writeMtx sync.Mutex // Only used in the Write method. writeMtx sync.Mutex // Only used in the Write method.
@ -230,23 +246,12 @@ type histogram struct {
upperBounds []float64 upperBounds []float64
// Two counts, one is "hot" for lock-free observations, the other is // Two counts, one is "hot" for lock-free observations, the other is
// "cold" for writing out a dto.Metric. // "cold" for writing out a dto.Metric. It has to be an array of
counts [2]histogramCounts // pointers to guarantee 64bit alignment of the histogramCounts, see
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG.
counts [2]*histogramCounts
hotIdx int // Index of currently-hot counts. Only used within Write. hotIdx int // Index of currently-hot counts. Only used within Write.
// This is a complicated one. For lock-free yet atomic observations, we
// need to save the total count of observations again, combined with the
// index of the currently-hot counts struct, so that we can perform the
// operation on both values atomically. The least significant bit
// defines the hot counts struct. The remaining 63 bits represent the
// total count of observations. This happens under the assumption that
// the 63bit count will never overflow. Rationale: An observations takes
// about 30ns. Let's assume it could happen in 10ns. Overflowing the
// counter will then take at least (2^63)*10ns, which is about 3000
// years.
countAndHotIdx uint64
labelPairs []*dto.LabelPair labelPairs []*dto.LabelPair
} }
@ -270,7 +275,7 @@ func (h *histogram) Observe(v float64) {
// 63 bits gets incremented by 1. At the same time, we get the new value // 63 bits gets incremented by 1. At the same time, we get the new value
// back, which we can use to find the currently-hot counts. // back, which we can use to find the currently-hot counts.
n := atomic.AddUint64(&h.countAndHotIdx, 2) n := atomic.AddUint64(&h.countAndHotIdx, 2)
hotCounts := &h.counts[n%2] hotCounts := h.counts[n%2]
if i < len(h.upperBounds) { if i < len(h.upperBounds) {
atomic.AddUint64(&hotCounts.buckets[i], 1) atomic.AddUint64(&hotCounts.buckets[i], 1)
@ -322,13 +327,13 @@ func (h *histogram) Write(out *dto.Metric) error {
if h.hotIdx == 0 { if h.hotIdx == 0 {
count = atomic.AddUint64(&h.countAndHotIdx, 1) >> 1 count = atomic.AddUint64(&h.countAndHotIdx, 1) >> 1
h.hotIdx = 1 h.hotIdx = 1
hotCounts = &h.counts[1] hotCounts = h.counts[1]
coldCounts = &h.counts[0] coldCounts = h.counts[0]
} else { } else {
count = atomic.AddUint64(&h.countAndHotIdx, ^uint64(0)) >> 1 // Decrement. count = atomic.AddUint64(&h.countAndHotIdx, ^uint64(0)) >> 1 // Decrement.
h.hotIdx = 0 h.hotIdx = 0
hotCounts = &h.counts[0] hotCounts = h.counts[0]
coldCounts = &h.counts[1] coldCounts = h.counts[1]
} }
// Now we have to wait for the now-declared-cold counts to actually cool // Now we have to wait for the now-declared-cold counts to actually cool

View File

@ -107,9 +107,6 @@ type Registerer interface {
// Collector, and for providing a Collector that will not cause // Collector, and for providing a Collector that will not cause
// inconsistent metrics on collection. (This would lead to scrape // inconsistent metrics on collection. (This would lead to scrape
// errors.) // errors.)
//
// It is in general not safe to register the same Collector multiple
// times concurrently.
Register(Collector) error Register(Collector) error
// MustRegister works like Register but registers any number of // MustRegister works like Register but registers any number of
// Collectors and panics upon the first registration that causes an // Collectors and panics upon the first registration that causes an
@ -273,7 +270,12 @@ func (r *Registry) Register(c Collector) error {
close(descChan) close(descChan)
}() }()
r.mtx.Lock() r.mtx.Lock()
defer r.mtx.Unlock() defer func() {
// Drain channel in case of premature return to not leak a goroutine.
for range descChan {
}
r.mtx.Unlock()
}()
// Conduct various tests... // Conduct various tests...
for desc := range descChan { for desc := range descChan {
@ -785,6 +787,8 @@ func checkMetricConsistency(
dtoMetric *dto.Metric, dtoMetric *dto.Metric,
metricHashes map[uint64]struct{}, metricHashes map[uint64]struct{},
) error { ) error {
name := metricFamily.GetName()
// Type consistency with metric family. // Type consistency with metric family.
if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil || if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil ||
metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil || metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil ||
@ -793,33 +797,42 @@ func checkMetricConsistency(
metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil { metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil {
return fmt.Errorf( return fmt.Errorf(
"collected metric %q { %s} is not a %s", "collected metric %q { %s} is not a %s",
metricFamily.GetName(), dtoMetric, metricFamily.GetType(), name, dtoMetric, metricFamily.GetType(),
) )
} }
previousLabelName := ""
for _, labelPair := range dtoMetric.GetLabel() { for _, labelPair := range dtoMetric.GetLabel() {
if !checkLabelName(labelPair.GetName()) { labelName := labelPair.GetName()
if labelName == previousLabelName {
return fmt.Errorf( return fmt.Errorf(
"collected metric %q { %s} has a label with an invalid name: %s", "collected metric %q { %s} has two or more labels with the same name: %s",
metricFamily.GetName(), dtoMetric, labelPair.GetName(), name, dtoMetric, labelName,
) )
} }
if dtoMetric.Summary != nil && labelPair.GetName() == quantileLabel { if !checkLabelName(labelName) {
return fmt.Errorf(
"collected metric %q { %s} has a label with an invalid name: %s",
name, dtoMetric, labelName,
)
}
if dtoMetric.Summary != nil && labelName == quantileLabel {
return fmt.Errorf( return fmt.Errorf(
"collected metric %q { %s} must not have an explicit %q label", "collected metric %q { %s} must not have an explicit %q label",
metricFamily.GetName(), dtoMetric, quantileLabel, name, dtoMetric, quantileLabel,
) )
} }
if !utf8.ValidString(labelPair.GetValue()) { if !utf8.ValidString(labelPair.GetValue()) {
return fmt.Errorf( return fmt.Errorf(
"collected metric %q { %s} has a label named %q whose value is not utf8: %#v", "collected metric %q { %s} has a label named %q whose value is not utf8: %#v",
metricFamily.GetName(), dtoMetric, labelPair.GetName(), labelPair.GetValue()) name, dtoMetric, labelName, labelPair.GetValue())
} }
previousLabelName = labelName
} }
// Is the metric unique (i.e. no other metric with the same name and the same labels)? // Is the metric unique (i.e. no other metric with the same name and the same labels)?
h := hashNew() h := hashNew()
h = hashAdd(h, metricFamily.GetName()) h = hashAdd(h, name)
h = hashAddByte(h, separatorByte) h = hashAddByte(h, separatorByte)
// Make sure label pairs are sorted. We depend on it for the consistency // Make sure label pairs are sorted. We depend on it for the consistency
// check. // check.
@ -833,7 +846,7 @@ func checkMetricConsistency(
if _, exists := metricHashes[h]; exists { if _, exists := metricHashes[h]; exists {
return fmt.Errorf( return fmt.Errorf(
"collected metric %q { %s} was collected before with the same name and label values", "collected metric %q { %s} was collected before with the same name and label values",
metricFamily.GetName(), dtoMetric, name, dtoMetric,
) )
} }
metricHashes[h] = struct{}{} metricHashes[h] = struct{}{}

14
vendor/vendor.json vendored
View File

@ -115,22 +115,22 @@
"revisionTime": "2017-09-01T18:29:50Z" "revisionTime": "2017-09-01T18:29:50Z"
}, },
{ {
"checksumSHA1": "lLvg5TpUtFbkyAoh+aI5T/nnpWw=", "checksumSHA1": "frS661rlSEZWE9CezHhnFioQK/I=",
"path": "github.com/prometheus/client_golang/prometheus", "path": "github.com/prometheus/client_golang/prometheus",
"revision": "e637cec7d9c8990247098639ebc6d43dd34ddd49", "revision": "0a8115f42e037a6e327f9a269a26ff6603fb8472",
"revisionTime": "2018-09-17T10:21:22Z" "revisionTime": "2018-10-01T17:40:01Z"
}, },
{ {
"checksumSHA1": "UBqhkyjCz47+S19MVTigxJ2VjVQ=", "checksumSHA1": "UBqhkyjCz47+S19MVTigxJ2VjVQ=",
"path": "github.com/prometheus/client_golang/prometheus/internal", "path": "github.com/prometheus/client_golang/prometheus/internal",
"revision": "e637cec7d9c8990247098639ebc6d43dd34ddd49", "revision": "0a8115f42e037a6e327f9a269a26ff6603fb8472",
"revisionTime": "2018-09-17T10:21:22Z" "revisionTime": "2018-10-01T17:40:01Z"
}, },
{ {
"checksumSHA1": "d5BiEvD8MrgpWQ6PQJUvawJsMak=", "checksumSHA1": "d5BiEvD8MrgpWQ6PQJUvawJsMak=",
"path": "github.com/prometheus/client_golang/prometheus/promhttp", "path": "github.com/prometheus/client_golang/prometheus/promhttp",
"revision": "e637cec7d9c8990247098639ebc6d43dd34ddd49", "revision": "0a8115f42e037a6e327f9a269a26ff6603fb8472",
"revisionTime": "2018-09-17T10:21:22Z" "revisionTime": "2018-10-01T17:40:01Z"
}, },
{ {
"checksumSHA1": "DvwvOlPNAgRntBzt3b3OSRMS2N4=", "checksumSHA1": "DvwvOlPNAgRntBzt3b3OSRMS2N4=",