add native histogram exemplar support
Signed-off-by: Ziqi Zhao <zhaoziqi9146@gmail.com>
This commit is contained in:
parent
7882668df7
commit
494ccce4f1
|
@ -472,6 +472,8 @@ type HistogramOpts struct {
|
||||||
NativeHistogramMaxBucketNumber uint32
|
NativeHistogramMaxBucketNumber uint32
|
||||||
NativeHistogramMinResetDuration time.Duration
|
NativeHistogramMinResetDuration time.Duration
|
||||||
NativeHistogramMaxZeroThreshold float64
|
NativeHistogramMaxZeroThreshold float64
|
||||||
|
NativeHistogramMaxExemplarCount uint32
|
||||||
|
NativeHistogramExemplarTTL time.Duration
|
||||||
|
|
||||||
// now is for testing purposes, by default it's time.Now.
|
// now is for testing purposes, by default it's time.Now.
|
||||||
now func() time.Time
|
now func() time.Time
|
||||||
|
@ -556,6 +558,7 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
||||||
h.nativeHistogramZeroThreshold = DefNativeHistogramZeroThreshold
|
h.nativeHistogramZeroThreshold = DefNativeHistogramZeroThreshold
|
||||||
} // Leave h.nativeHistogramZeroThreshold at 0 otherwise.
|
} // Leave h.nativeHistogramZeroThreshold at 0 otherwise.
|
||||||
h.nativeHistogramSchema = pickSchema(opts.NativeHistogramBucketFactor)
|
h.nativeHistogramSchema = pickSchema(opts.NativeHistogramBucketFactor)
|
||||||
|
h.nativeExemplars = newNativeExemplars(opts.NativeHistogramExemplarTTL, opts.NativeHistogramMaxExemplarCount)
|
||||||
}
|
}
|
||||||
for i, upperBound := range h.upperBounds {
|
for i, upperBound := range h.upperBounds {
|
||||||
if i < len(h.upperBounds)-1 {
|
if i < len(h.upperBounds)-1 {
|
||||||
|
@ -732,6 +735,8 @@ type histogram struct {
|
||||||
|
|
||||||
// afterFunc is for testing purposes, by default it's time.AfterFunc.
|
// afterFunc is for testing purposes, by default it's time.AfterFunc.
|
||||||
afterFunc func(time.Duration, func()) *time.Timer
|
afterFunc func(time.Duration, func()) *time.Timer
|
||||||
|
|
||||||
|
nativeExemplars nativeExemplars
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *histogram) Desc() *Desc {
|
func (h *histogram) Desc() *Desc {
|
||||||
|
@ -821,6 +826,8 @@ func (h *histogram) Write(out *dto.Metric) error {
|
||||||
Length: proto.Uint32(0),
|
Length: proto.Uint32(0),
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
his.Exemplars = append(his.Exemplars, h.nativeExemplars.exemplars...)
|
||||||
}
|
}
|
||||||
addAndResetCounts(hotCounts, coldCounts)
|
addAndResetCounts(hotCounts, coldCounts)
|
||||||
return nil
|
return nil
|
||||||
|
@ -1102,6 +1109,10 @@ func (h *histogram) updateExemplar(v float64, bucket int, l Labels) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
h.exemplars[bucket].Store(e)
|
h.exemplars[bucket].Store(e)
|
||||||
|
doSparse := h.nativeHistogramSchema > math.MinInt32 && !math.IsNaN(v)
|
||||||
|
if doSparse {
|
||||||
|
h.nativeExemplars.addExemplar(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HistogramVec is a Collector that bundles a set of Histograms that all share the
|
// HistogramVec is a Collector that bundles a set of Histograms that all share the
|
||||||
|
@ -1575,3 +1586,58 @@ func addAndResetCounts(hot, cold *histogramCounts) {
|
||||||
atomic.AddUint64(&hot.nativeHistogramZeroBucket, atomic.LoadUint64(&cold.nativeHistogramZeroBucket))
|
atomic.AddUint64(&hot.nativeHistogramZeroBucket, atomic.LoadUint64(&cold.nativeHistogramZeroBucket))
|
||||||
atomic.StoreUint64(&cold.nativeHistogramZeroBucket, 0)
|
atomic.StoreUint64(&cold.nativeHistogramZeroBucket, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type nativeExemplars struct {
|
||||||
|
nativeHistogramExemplarTTL time.Duration
|
||||||
|
nativeHistogramMaxExemplarCount uint32
|
||||||
|
|
||||||
|
exemplars []*dto.Exemplar
|
||||||
|
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNativeExemplars(ttl time.Duration, count uint32) nativeExemplars {
|
||||||
|
return nativeExemplars{
|
||||||
|
nativeHistogramExemplarTTL: ttl,
|
||||||
|
nativeHistogramMaxExemplarCount: count,
|
||||||
|
exemplars: make([]*dto.Exemplar, 0),
|
||||||
|
lock: sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *nativeExemplars) addExemplar(e *dto.Exemplar) {
|
||||||
|
n.lock.Lock()
|
||||||
|
defer n.lock.Unlock()
|
||||||
|
|
||||||
|
elogarithm := math.Log(e.GetValue())
|
||||||
|
if len(n.exemplars) == int(n.nativeHistogramMaxExemplarCount) {
|
||||||
|
// check if oldestIndex is beyond TTL,
|
||||||
|
// if so, find the oldest exemplar, and nearest exemplar
|
||||||
|
oldestTimestamp := time.Now()
|
||||||
|
oldestIndex := -1
|
||||||
|
nearestValue := -1.0
|
||||||
|
nearestIndex := -1
|
||||||
|
|
||||||
|
for i, exemplar := range n.exemplars {
|
||||||
|
if exemplar.Timestamp.AsTime().Before(oldestTimestamp) {
|
||||||
|
oldestTimestamp = exemplar.Timestamp.AsTime()
|
||||||
|
oldestIndex = i
|
||||||
|
}
|
||||||
|
logarithm := math.Log(exemplar.GetValue())
|
||||||
|
if nearestValue == -1 || math.Abs(elogarithm-logarithm) < nearestValue {
|
||||||
|
fmt.Printf("gap: %f", math.Abs(elogarithm-logarithm))
|
||||||
|
nearestValue = math.Abs(elogarithm - logarithm)
|
||||||
|
nearestIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldestIndex != -1 && time.Since(oldestTimestamp) > n.nativeHistogramExemplarTTL {
|
||||||
|
n.exemplars[oldestIndex] = e
|
||||||
|
} else {
|
||||||
|
n.exemplars[nearestIndex] = e
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n.exemplars = append(n.exemplars, e)
|
||||||
|
}
|
||||||
|
|
|
@ -1271,3 +1271,91 @@ func TestHistogramVecCreatedTimestampWithDeletes(t *testing.T) {
|
||||||
now = now.Add(1 * time.Hour)
|
now = now.Add(1 * time.Hour)
|
||||||
expectCTsForMetricVecValues(t, histogramVec.MetricVec, dto.MetricType_HISTOGRAM, expected)
|
expectCTsForMetricVecValues(t, histogramVec.MetricVec, dto.MetricType_HISTOGRAM, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNativeHistogramExemplar(t *testing.T) {
|
||||||
|
histogram := NewHistogram(HistogramOpts{
|
||||||
|
Name: "test",
|
||||||
|
Help: "test help",
|
||||||
|
Buckets: []float64{1, 2, 3, 4},
|
||||||
|
NativeHistogramBucketFactor: 1.1,
|
||||||
|
NativeHistogramMaxExemplarCount: 3,
|
||||||
|
NativeHistogramExemplarTTL: 10 * time.Second,
|
||||||
|
}).(*histogram)
|
||||||
|
|
||||||
|
// expectedExemplars := []*dto.Exemplar{
|
||||||
|
// {
|
||||||
|
// Label: []*dto.LabelPair{
|
||||||
|
// {Name: proto.String("id"), Value: proto.String("1")},
|
||||||
|
// },
|
||||||
|
// Value: proto.Float64(1),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Label: []*dto.LabelPair{
|
||||||
|
// {Name: proto.String("id"), Value: proto.String("2")},
|
||||||
|
// },
|
||||||
|
// Value: proto.Float64(3),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Label: []*dto.LabelPair{
|
||||||
|
// {Name: proto.String("id"), Value: proto.String("3")},
|
||||||
|
// },
|
||||||
|
// Value: proto.Float64(5),
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
|
||||||
|
histogram.ObserveWithExemplar(1, Labels{"id": "1"})
|
||||||
|
histogram.ObserveWithExemplar(3, Labels{"id": "1"})
|
||||||
|
histogram.ObserveWithExemplar(5, Labels{"id": "1"})
|
||||||
|
|
||||||
|
if len(histogram.nativeExemplars.exemplars) != 3 {
|
||||||
|
t.Errorf("the count of exemplars is not 3")
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedValues := map[float64]struct{}{
|
||||||
|
1: {},
|
||||||
|
3: {},
|
||||||
|
5: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range histogram.nativeExemplars.exemplars {
|
||||||
|
if _, ok := expectedValues[e.GetValue()]; !ok {
|
||||||
|
t.Errorf("the value is not in expected value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
histogram.ObserveWithExemplar(4, Labels{"id": "1"})
|
||||||
|
|
||||||
|
if len(histogram.nativeExemplars.exemplars) != 3 {
|
||||||
|
t.Errorf("the count of exemplars is not 3")
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedValues = map[float64]struct{}{
|
||||||
|
1: {},
|
||||||
|
3: {},
|
||||||
|
4: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range histogram.nativeExemplars.exemplars {
|
||||||
|
if _, ok := expectedValues[e.GetValue()]; !ok {
|
||||||
|
t.Errorf("the value is not in expected value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
histogram.ObserveWithExemplar(6, Labels{"id": "1"})
|
||||||
|
|
||||||
|
if len(histogram.nativeExemplars.exemplars) != 3 {
|
||||||
|
t.Errorf("the count of exemplars is not 3")
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedValues = map[float64]struct{}{
|
||||||
|
6: {},
|
||||||
|
3: {},
|
||||||
|
4: {},
|
||||||
|
}
|
||||||
|
for _, e := range histogram.nativeExemplars.exemplars {
|
||||||
|
if _, ok := expectedValues[e.GetValue()]; !ok {
|
||||||
|
t.Errorf("the value is not in expected value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue