mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
model: String method for histogram.Histogram
This includes a regular bucket iterator and a string method for histogram.Bucket. Signed-off-by: beorn7 <beorn@grafana.com>
This commit is contained in:
parent
8f92c90897
commit
f1065e44a4
|
@ -14,7 +14,9 @@
|
||||||
package histogram
|
package histogram
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Histogram encodes a sparse, high-resolution histogram. See the design
|
// Histogram encodes a sparse, high-resolution histogram. See the design
|
||||||
|
@ -65,7 +67,7 @@ type Span struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a deep copy of the Histogram.
|
// Copy returns a deep copy of the Histogram.
|
||||||
func (h Histogram) Copy() Histogram {
|
func (h Histogram) Copy() *Histogram {
|
||||||
c := h
|
c := h
|
||||||
|
|
||||||
if h.PositiveSpans != nil {
|
if h.PositiveSpans != nil {
|
||||||
|
@ -85,7 +87,61 @@ func (h Histogram) Copy() Histogram {
|
||||||
copy(c.NegativeBuckets, h.NegativeBuckets)
|
copy(c.NegativeBuckets, h.NegativeBuckets)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the Histogram.
|
||||||
|
func (h Histogram) String() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
fmt.Fprintf(&sb, "{count:%d, sum:%g", h.Count, h.Sum)
|
||||||
|
|
||||||
|
var nBuckets []Bucket
|
||||||
|
for it := h.NegativeBucketIterator(); it.Next(); {
|
||||||
|
bucket := it.At()
|
||||||
|
if bucket.Count != 0 {
|
||||||
|
nBuckets = append(nBuckets, it.At())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := len(nBuckets) - 1; i >= 0; i-- {
|
||||||
|
fmt.Fprintf(&sb, ", %s", nBuckets[i].String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.ZeroCount != 0 {
|
||||||
|
fmt.Fprintf(&sb, ", %s", h.ZeroBucket().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
for it := h.PositiveBucketIterator(); it.Next(); {
|
||||||
|
bucket := it.At()
|
||||||
|
if bucket.Count != 0 {
|
||||||
|
fmt.Fprintf(&sb, ", %s", bucket.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.WriteRune('}')
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZeroBucket returns the zero bucket.
|
||||||
|
func (h Histogram) ZeroBucket() Bucket {
|
||||||
|
return Bucket{
|
||||||
|
Lower: -h.ZeroThreshold,
|
||||||
|
Upper: h.ZeroThreshold,
|
||||||
|
LowerInclusive: true,
|
||||||
|
UpperInclusive: true,
|
||||||
|
Count: h.ZeroCount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PositiveBucketIterator returns a BucketIterator to iterate over all positive
|
||||||
|
// buckets in ascending order (starting next to the zero bucket and going up).
|
||||||
|
func (h Histogram) PositiveBucketIterator() BucketIterator {
|
||||||
|
return newRegularBucketIterator(&h, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NegativeBucketIterator returns a BucketIterator to iterate over all negative
|
||||||
|
// buckets in descending order (starting next to the zero bucket and going down).
|
||||||
|
func (h Histogram) NegativeBucketIterator() BucketIterator {
|
||||||
|
return newRegularBucketIterator(&h, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CumulativeBucketIterator returns a BucketIterator to iterate over a
|
// CumulativeBucketIterator returns a BucketIterator to iterate over a
|
||||||
|
@ -96,7 +152,7 @@ func (h Histogram) CumulativeBucketIterator() BucketIterator {
|
||||||
if len(h.NegativeBuckets) > 0 {
|
if len(h.NegativeBuckets) > 0 {
|
||||||
panic("CumulativeIterator called on Histogram with negative buckets")
|
panic("CumulativeIterator called on Histogram with negative buckets")
|
||||||
}
|
}
|
||||||
return &cumulativeBucketIterator{h: h, posSpansIdx: -1}
|
return &cumulativeBucketIterator{h: &h, posSpansIdx: -1}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BucketIterator iterates over the buckets of a Histogram, returning decoded
|
// BucketIterator iterates over the buckets of a Histogram, returning decoded
|
||||||
|
@ -106,26 +162,126 @@ type BucketIterator interface {
|
||||||
Next() bool
|
Next() bool
|
||||||
// At returns the current bucket.
|
// At returns the current bucket.
|
||||||
At() Bucket
|
At() Bucket
|
||||||
// Err returns the current error. It should be used only after iterator is
|
|
||||||
// exhausted, that is `Next` or `Seek` returns false.
|
|
||||||
Err() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bucket represents a bucket (currently only a cumulative one with an upper
|
// Bucket represents a bucket with lower and upper limit and the count of
|
||||||
// inclusive bound and a cumulative count).
|
// samples in the bucket. It also specifies if each limit is inclusive or
|
||||||
|
// not. (Mathematically, inclusive limits create a closed interval, and
|
||||||
|
// non-inclusive limits an open interval.)
|
||||||
|
//
|
||||||
|
// To represent cumulative buckets, Lower is set to -Inf, and the Count is then
|
||||||
|
// cumulative (including the counts of all buckets for smaller values).
|
||||||
type Bucket struct {
|
type Bucket struct {
|
||||||
Upper float64
|
Lower, Upper float64
|
||||||
Count uint64
|
LowerInclusive, UpperInclusive bool
|
||||||
|
Count uint64
|
||||||
|
Index int32 // Index within schema. To easily compare buckets that share the same schema.
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation, using the usual mathematical notation
|
||||||
|
// of '['/']' for inclusive bounds and '('/')' for non-inclusive bounds.
|
||||||
|
func (b Bucket) String() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
if b.LowerInclusive {
|
||||||
|
sb.WriteRune('[')
|
||||||
|
} else {
|
||||||
|
sb.WriteRune('(')
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&sb, "%g,%g", b.Lower, b.Upper)
|
||||||
|
if b.UpperInclusive {
|
||||||
|
sb.WriteRune(']')
|
||||||
|
} else {
|
||||||
|
sb.WriteRune(')')
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&sb, ":%d", b.Count)
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type regularBucketIterator struct {
|
||||||
|
schema int32
|
||||||
|
spans []Span
|
||||||
|
buckets []int64
|
||||||
|
|
||||||
|
positive bool // Whether this is for positive buckets.
|
||||||
|
|
||||||
|
spansIdx int // Current span within spans slice.
|
||||||
|
idxInSpan uint32 // Index in the current span. 0 <= idxInSpan < span.Length.
|
||||||
|
bucketsIdx int // Current bucket within buckets slice.
|
||||||
|
|
||||||
|
currCount int64 // Count in the current bucket.
|
||||||
|
currIdx int32 // The actual bucket index.
|
||||||
|
currLower, currUpper float64 // Limits of the current bucket.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRegularBucketIterator(h *Histogram, positive bool) *regularBucketIterator {
|
||||||
|
r := ®ularBucketIterator{schema: h.Schema, positive: positive}
|
||||||
|
if positive {
|
||||||
|
r.spans = h.PositiveSpans
|
||||||
|
r.buckets = h.PositiveBuckets
|
||||||
|
} else {
|
||||||
|
r.spans = h.NegativeSpans
|
||||||
|
r.buckets = h.NegativeBuckets
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *regularBucketIterator) Next() bool {
|
||||||
|
if r.spansIdx >= len(r.spans) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
span := r.spans[r.spansIdx]
|
||||||
|
// Seed currIdx for the first bucket.
|
||||||
|
if r.bucketsIdx == 0 {
|
||||||
|
r.currIdx = span.Offset
|
||||||
|
} else {
|
||||||
|
r.currIdx++
|
||||||
|
}
|
||||||
|
for r.idxInSpan >= span.Length {
|
||||||
|
// We have exhausted the current span and have to find a new
|
||||||
|
// one. We'll even handle pathologic spans of length 0.
|
||||||
|
r.idxInSpan = 0
|
||||||
|
r.spansIdx++
|
||||||
|
if r.spansIdx >= len(r.spans) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
span = r.spans[r.spansIdx]
|
||||||
|
r.currIdx += span.Offset
|
||||||
|
}
|
||||||
|
|
||||||
|
r.currCount += r.buckets[r.bucketsIdx]
|
||||||
|
if r.positive {
|
||||||
|
r.currUpper = getBound(r.currIdx, r.schema)
|
||||||
|
r.currLower = getBound(r.currIdx-1, r.schema)
|
||||||
|
} else {
|
||||||
|
r.currLower = -getBound(r.currIdx, r.schema)
|
||||||
|
r.currUpper = -getBound(r.currIdx-1, r.schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.idxInSpan++
|
||||||
|
r.bucketsIdx++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *regularBucketIterator) At() Bucket {
|
||||||
|
return Bucket{
|
||||||
|
Count: uint64(r.currCount),
|
||||||
|
Lower: r.currLower,
|
||||||
|
Upper: r.currUpper,
|
||||||
|
LowerInclusive: r.currLower < 0,
|
||||||
|
UpperInclusive: r.currUpper > 0,
|
||||||
|
Index: r.currIdx,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type cumulativeBucketIterator struct {
|
type cumulativeBucketIterator struct {
|
||||||
h Histogram
|
h *Histogram
|
||||||
|
|
||||||
posSpansIdx int // Index in h.PositiveSpans we are in. -1 means 0 bucket.
|
posSpansIdx int // Index in h.PositiveSpans we are in. -1 means 0 bucket.
|
||||||
posBucketsIdx int // Index in h.PositiveBuckets.
|
posBucketsIdx int // Index in h.PositiveBuckets.
|
||||||
idxInSpan uint32 // Index in the current span. 0 <= idxInSpan < span.Length.
|
idxInSpan uint32 // Index in the current span. 0 <= idxInSpan < span.Length.
|
||||||
|
|
||||||
initialised bool
|
initialized bool
|
||||||
currIdx int32 // The actual bucket index after decoding from spans.
|
currIdx int32 // The actual bucket index after decoding from spans.
|
||||||
currUpper float64 // The upper boundary of the current bucket.
|
currUpper float64 // The upper boundary of the current bucket.
|
||||||
currCount int64 // Current non-cumulative count for the current bucket. Does not apply for empty bucket.
|
currCount int64 // Current non-cumulative count for the current bucket. Does not apply for empty bucket.
|
||||||
|
@ -158,24 +314,24 @@ func (c *cumulativeBucketIterator) Next() bool {
|
||||||
|
|
||||||
if c.emptyBucketCount > 0 {
|
if c.emptyBucketCount > 0 {
|
||||||
// We are traversing through empty buckets at the moment.
|
// We are traversing through empty buckets at the moment.
|
||||||
c.currUpper = getUpper(c.currIdx, c.h.Schema)
|
c.currUpper = getBound(c.currIdx, c.h.Schema)
|
||||||
c.currIdx++
|
c.currIdx++
|
||||||
c.emptyBucketCount--
|
c.emptyBucketCount--
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
span := c.h.PositiveSpans[c.posSpansIdx]
|
span := c.h.PositiveSpans[c.posSpansIdx]
|
||||||
if c.posSpansIdx == 0 && !c.initialised {
|
if c.posSpansIdx == 0 && !c.initialized {
|
||||||
// Initialising.
|
// Initialising.
|
||||||
c.currIdx = span.Offset
|
c.currIdx = span.Offset
|
||||||
// The first bucket is absolute value and not a delta with Zero bucket.
|
// The first bucket is an absolute value and not a delta with Zero bucket.
|
||||||
c.currCount = 0
|
c.currCount = 0
|
||||||
c.initialised = true
|
c.initialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
c.currCount += c.h.PositiveBuckets[c.posBucketsIdx]
|
c.currCount += c.h.PositiveBuckets[c.posBucketsIdx]
|
||||||
c.currCumulativeCount += uint64(c.currCount)
|
c.currCumulativeCount += uint64(c.currCount)
|
||||||
c.currUpper = getUpper(c.currIdx, c.h.Schema)
|
c.currUpper = getBound(c.currIdx, c.h.Schema)
|
||||||
|
|
||||||
c.posBucketsIdx++
|
c.posBucketsIdx++
|
||||||
c.idxInSpan++
|
c.idxInSpan++
|
||||||
|
@ -191,15 +347,19 @@ func (c *cumulativeBucketIterator) Next() bool {
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cumulativeBucketIterator) At() Bucket {
|
func (c *cumulativeBucketIterator) At() Bucket {
|
||||||
return Bucket{
|
return Bucket{
|
||||||
Upper: c.currUpper,
|
Upper: c.currUpper,
|
||||||
Count: c.currCumulativeCount,
|
Lower: math.Inf(-1),
|
||||||
|
UpperInclusive: true,
|
||||||
|
LowerInclusive: true,
|
||||||
|
Count: c.currCumulativeCount,
|
||||||
|
Index: c.currIdx - 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (c *cumulativeBucketIterator) Err() error { return nil }
|
|
||||||
|
|
||||||
func getUpper(idx, schema int32) float64 {
|
func getBound(idx, schema int32) float64 {
|
||||||
if schema < 0 {
|
if schema < 0 {
|
||||||
return math.Ldexp(1, int(idx)<<(-schema))
|
return math.Ldexp(1, int(idx)<<(-schema))
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,72 @@ package histogram
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestHistogramString(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
histogram Histogram
|
||||||
|
expectedString string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
histogram: Histogram{
|
||||||
|
Schema: 0,
|
||||||
|
},
|
||||||
|
expectedString: "{count:0, sum:0}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
histogram: Histogram{
|
||||||
|
Schema: 0,
|
||||||
|
Count: 9,
|
||||||
|
Sum: -3.1415,
|
||||||
|
ZeroCount: 12,
|
||||||
|
ZeroThreshold: 0.001,
|
||||||
|
NegativeSpans: []Span{
|
||||||
|
{Offset: 0, Length: 5},
|
||||||
|
{Offset: 1, Length: 1},
|
||||||
|
},
|
||||||
|
NegativeBuckets: []int64{1, 2, -2, 1, -1, 0},
|
||||||
|
},
|
||||||
|
expectedString: "{count:9, sum:-3.1415, [-64,-32):1, [-16,-8):1, [-8,-4):2, [-4,-2):1, [-2,-1):3, [-1,-0.5):1, [-0.001,0.001]:12}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
histogram: Histogram{
|
||||||
|
Schema: 0,
|
||||||
|
Count: 19,
|
||||||
|
Sum: 2.7,
|
||||||
|
PositiveSpans: []Span{
|
||||||
|
{Offset: 0, Length: 4},
|
||||||
|
{Offset: 0, Length: 0},
|
||||||
|
{Offset: 0, Length: 3},
|
||||||
|
},
|
||||||
|
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0, 0},
|
||||||
|
NegativeSpans: []Span{
|
||||||
|
{Offset: 0, Length: 5},
|
||||||
|
{Offset: 1, Length: 0},
|
||||||
|
{Offset: 0, Length: 1},
|
||||||
|
},
|
||||||
|
NegativeBuckets: []int64{1, 2, -2, 1, -1, 0},
|
||||||
|
},
|
||||||
|
expectedString: "{count:19, sum:2.7, [-64,-32):1, [-16,-8):1, [-8,-4):2, [-4,-2):1, [-2,-1):3, [-1,-0.5):1, (0.5,1]:1, (1,2]:3, (2,4]:1, (4,8]:2, (8,16]:1, (16,32]:1, (32,64]:1}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range cases {
|
||||||
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||||
|
actualString := c.histogram.String()
|
||||||
|
require.Equal(t, c.expectedString, actualString)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCumulativeBucketIterator(t *testing.T) {
|
func TestCumulativeBucketIterator(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
histogram Histogram
|
histogram Histogram
|
||||||
expectedCumulativeBuckets []Bucket
|
expectedBuckets []Bucket
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
histogram: Histogram{
|
histogram: Histogram{
|
||||||
|
@ -34,14 +91,14 @@ func TestCumulativeBucketIterator(t *testing.T) {
|
||||||
},
|
},
|
||||||
PositiveBuckets: []int64{1, 1, -1, 0},
|
PositiveBuckets: []int64{1, 1, -1, 0},
|
||||||
},
|
},
|
||||||
expectedCumulativeBuckets: []Bucket{
|
expectedBuckets: []Bucket{
|
||||||
{Upper: 1, Count: 1},
|
{Lower: math.Inf(-1), Upper: 1, Count: 1, LowerInclusive: true, UpperInclusive: true, Index: 0},
|
||||||
{Upper: 2, Count: 3},
|
{Lower: math.Inf(-1), Upper: 2, Count: 3, LowerInclusive: true, UpperInclusive: true, Index: 1},
|
||||||
|
|
||||||
{Upper: 4, Count: 3},
|
{Lower: math.Inf(-1), Upper: 4, Count: 3, LowerInclusive: true, UpperInclusive: true, Index: 2},
|
||||||
|
|
||||||
{Upper: 8, Count: 4},
|
{Lower: math.Inf(-1), Upper: 8, Count: 4, LowerInclusive: true, UpperInclusive: true, Index: 3},
|
||||||
{Upper: 16, Count: 5},
|
{Lower: math.Inf(-1), Upper: 16, Count: 5, LowerInclusive: true, UpperInclusive: true, Index: 4},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -53,16 +110,16 @@ func TestCumulativeBucketIterator(t *testing.T) {
|
||||||
},
|
},
|
||||||
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0},
|
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0},
|
||||||
},
|
},
|
||||||
expectedCumulativeBuckets: []Bucket{
|
expectedBuckets: []Bucket{
|
||||||
{Upper: 1, Count: 1},
|
{Lower: math.Inf(-1), Upper: 1, Count: 1, LowerInclusive: true, UpperInclusive: true, Index: 0},
|
||||||
{Upper: 2, Count: 4},
|
{Lower: math.Inf(-1), Upper: 2, Count: 4, LowerInclusive: true, UpperInclusive: true, Index: 1},
|
||||||
{Upper: 4, Count: 5},
|
{Lower: math.Inf(-1), Upper: 4, Count: 5, LowerInclusive: true, UpperInclusive: true, Index: 2},
|
||||||
{Upper: 8, Count: 7},
|
{Lower: math.Inf(-1), Upper: 8, Count: 7, LowerInclusive: true, UpperInclusive: true, Index: 3},
|
||||||
|
|
||||||
{Upper: 16, Count: 8},
|
{Lower: math.Inf(-1), Upper: 16, Count: 8, LowerInclusive: true, UpperInclusive: true, Index: 4},
|
||||||
|
|
||||||
{Upper: 32, Count: 8},
|
{Lower: math.Inf(-1), Upper: 32, Count: 8, LowerInclusive: true, UpperInclusive: true, Index: 5},
|
||||||
{Upper: 64, Count: 9},
|
{Lower: math.Inf(-1), Upper: 64, Count: 9, LowerInclusive: true, UpperInclusive: true, Index: 6},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -73,14 +130,14 @@ func TestCumulativeBucketIterator(t *testing.T) {
|
||||||
},
|
},
|
||||||
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0, 0},
|
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0, 0},
|
||||||
},
|
},
|
||||||
expectedCumulativeBuckets: []Bucket{
|
expectedBuckets: []Bucket{
|
||||||
{Upper: 1, Count: 1},
|
{Lower: math.Inf(-1), Upper: 1, Count: 1, LowerInclusive: true, UpperInclusive: true, Index: 0},
|
||||||
{Upper: 2, Count: 4},
|
{Lower: math.Inf(-1), Upper: 2, Count: 4, LowerInclusive: true, UpperInclusive: true, Index: 1},
|
||||||
{Upper: 4, Count: 5},
|
{Lower: math.Inf(-1), Upper: 4, Count: 5, LowerInclusive: true, UpperInclusive: true, Index: 2},
|
||||||
{Upper: 8, Count: 7},
|
{Lower: math.Inf(-1), Upper: 8, Count: 7, LowerInclusive: true, UpperInclusive: true, Index: 3},
|
||||||
{Upper: 16, Count: 8},
|
{Lower: math.Inf(-1), Upper: 16, Count: 8, LowerInclusive: true, UpperInclusive: true, Index: 4},
|
||||||
{Upper: 32, Count: 9},
|
{Lower: math.Inf(-1), Upper: 32, Count: 9, LowerInclusive: true, UpperInclusive: true, Index: 5},
|
||||||
{Upper: 64, Count: 10},
|
{Lower: math.Inf(-1), Upper: 64, Count: 10, LowerInclusive: true, UpperInclusive: true, Index: 6},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -93,22 +150,22 @@ func TestCumulativeBucketIterator(t *testing.T) {
|
||||||
},
|
},
|
||||||
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0, 3},
|
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0, 3},
|
||||||
},
|
},
|
||||||
expectedCumulativeBuckets: []Bucket{
|
expectedBuckets: []Bucket{
|
||||||
{Upper: 0.6484197773255048, Count: 1}, // -5
|
{Lower: math.Inf(-1), Upper: 0.6484197773255048, Count: 1, LowerInclusive: true, UpperInclusive: true, Index: -5},
|
||||||
{Upper: 0.7071067811865475, Count: 4}, // -4
|
{Lower: math.Inf(-1), Upper: 0.7071067811865475, Count: 4, LowerInclusive: true, UpperInclusive: true, Index: -4},
|
||||||
|
|
||||||
{Upper: 0.7711054127039704, Count: 4}, // -3
|
{Lower: math.Inf(-1), Upper: 0.7711054127039704, Count: 4, LowerInclusive: true, UpperInclusive: true, Index: -3},
|
||||||
{Upper: 0.8408964152537144, Count: 4}, // -2
|
{Lower: math.Inf(-1), Upper: 0.8408964152537144, Count: 4, LowerInclusive: true, UpperInclusive: true, Index: -2},
|
||||||
|
|
||||||
{Upper: 0.9170040432046711, Count: 5}, // -1
|
{Lower: math.Inf(-1), Upper: 0.9170040432046711, Count: 5, LowerInclusive: true, UpperInclusive: true, Index: -1},
|
||||||
{Upper: 1, Count: 7}, // 1
|
{Lower: math.Inf(-1), Upper: 1, Count: 7, LowerInclusive: true, UpperInclusive: true, Index: 0},
|
||||||
{Upper: 1.0905077326652577, Count: 8}, // 0
|
{Lower: math.Inf(-1), Upper: 1.0905077326652577, Count: 8, LowerInclusive: true, UpperInclusive: true, Index: 1},
|
||||||
|
|
||||||
{Upper: 1.189207115002721, Count: 8}, // 1
|
{Lower: math.Inf(-1), Upper: 1.189207115002721, Count: 8, LowerInclusive: true, UpperInclusive: true, Index: 2},
|
||||||
{Upper: 1.2968395546510096, Count: 8}, // 2
|
{Lower: math.Inf(-1), Upper: 1.2968395546510096, Count: 8, LowerInclusive: true, UpperInclusive: true, Index: 3},
|
||||||
|
|
||||||
{Upper: 1.414213562373095, Count: 9}, // 3
|
{Lower: math.Inf(-1), Upper: 1.414213562373095, Count: 9, LowerInclusive: true, UpperInclusive: true, Index: 4},
|
||||||
{Upper: 1.5422108254079407, Count: 13}, // 4
|
{Lower: math.Inf(-1), Upper: 1.5422108254079407, Count: 13, LowerInclusive: true, UpperInclusive: true, Index: 5},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -120,17 +177,17 @@ func TestCumulativeBucketIterator(t *testing.T) {
|
||||||
},
|
},
|
||||||
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0},
|
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0},
|
||||||
},
|
},
|
||||||
expectedCumulativeBuckets: []Bucket{
|
expectedBuckets: []Bucket{
|
||||||
{Upper: 0.00390625, Count: 1}, // -2
|
{Lower: math.Inf(-1), Upper: 0.00390625, Count: 1, LowerInclusive: true, UpperInclusive: true, Index: -2},
|
||||||
{Upper: 0.0625, Count: 4}, // -1
|
{Lower: math.Inf(-1), Upper: 0.0625, Count: 4, LowerInclusive: true, UpperInclusive: true, Index: -1},
|
||||||
{Upper: 1, Count: 5}, // 0
|
{Lower: math.Inf(-1), Upper: 1, Count: 5, LowerInclusive: true, UpperInclusive: true, Index: 0},
|
||||||
{Upper: 16, Count: 7}, // 1
|
{Lower: math.Inf(-1), Upper: 16, Count: 7, LowerInclusive: true, UpperInclusive: true, Index: 1},
|
||||||
|
|
||||||
{Upper: 256, Count: 7}, // 2
|
{Lower: math.Inf(-1), Upper: 256, Count: 7, LowerInclusive: true, UpperInclusive: true, Index: 2},
|
||||||
{Upper: 4096, Count: 7}, // 3
|
{Lower: math.Inf(-1), Upper: 4096, Count: 7, LowerInclusive: true, UpperInclusive: true, Index: 3},
|
||||||
|
|
||||||
{Upper: 65536, Count: 8}, // 4
|
{Lower: math.Inf(-1), Upper: 65536, Count: 8, LowerInclusive: true, UpperInclusive: true, Index: 4},
|
||||||
{Upper: 1048576, Count: 9}, // 5
|
{Lower: math.Inf(-1), Upper: 1048576, Count: 9, LowerInclusive: true, UpperInclusive: true, Index: 5},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -141,12 +198,12 @@ func TestCumulativeBucketIterator(t *testing.T) {
|
||||||
},
|
},
|
||||||
PositiveBuckets: []int64{1, 2, -2, 1, -1},
|
PositiveBuckets: []int64{1, 2, -2, 1, -1},
|
||||||
},
|
},
|
||||||
expectedCumulativeBuckets: []Bucket{
|
expectedBuckets: []Bucket{
|
||||||
{Upper: 0.0625, Count: 1}, // -2
|
{Lower: math.Inf(-1), Upper: 0.0625, Count: 1, LowerInclusive: true, UpperInclusive: true, Index: -2},
|
||||||
{Upper: 0.25, Count: 4}, // -1
|
{Lower: math.Inf(-1), Upper: 0.25, Count: 4, LowerInclusive: true, UpperInclusive: true, Index: -1},
|
||||||
{Upper: 1, Count: 5}, // 0
|
{Lower: math.Inf(-1), Upper: 1, Count: 5, LowerInclusive: true, UpperInclusive: true, Index: 0},
|
||||||
{Upper: 4, Count: 7}, // 1
|
{Lower: math.Inf(-1), Upper: 4, Count: 7, LowerInclusive: true, UpperInclusive: true, Index: 1},
|
||||||
{Upper: 16, Count: 8}, // 2
|
{Lower: math.Inf(-1), Upper: 16, Count: 8, LowerInclusive: true, UpperInclusive: true, Index: 2},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -154,12 +211,177 @@ func TestCumulativeBucketIterator(t *testing.T) {
|
||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||||
it := c.histogram.CumulativeBucketIterator()
|
it := c.histogram.CumulativeBucketIterator()
|
||||||
actualBuckets := make([]Bucket, 0, len(c.expectedCumulativeBuckets))
|
actualBuckets := make([]Bucket, 0, len(c.expectedBuckets))
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
actualBuckets = append(actualBuckets, it.At())
|
actualBuckets = append(actualBuckets, it.At())
|
||||||
}
|
}
|
||||||
require.NoError(t, it.Err())
|
require.Equal(t, c.expectedBuckets, actualBuckets)
|
||||||
require.Equal(t, c.expectedCumulativeBuckets, actualBuckets)
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegularBucketIterator(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
histogram Histogram
|
||||||
|
expectedPositiveBuckets []Bucket
|
||||||
|
expectedNegativeBuckets []Bucket
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
histogram: Histogram{
|
||||||
|
Schema: 0,
|
||||||
|
},
|
||||||
|
expectedPositiveBuckets: []Bucket{},
|
||||||
|
expectedNegativeBuckets: []Bucket{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
histogram: Histogram{
|
||||||
|
Schema: 0,
|
||||||
|
PositiveSpans: []Span{
|
||||||
|
{Offset: 0, Length: 2},
|
||||||
|
{Offset: 1, Length: 2},
|
||||||
|
},
|
||||||
|
PositiveBuckets: []int64{1, 1, -1, 0},
|
||||||
|
},
|
||||||
|
expectedPositiveBuckets: []Bucket{
|
||||||
|
{Lower: 0.5, Upper: 1, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 0},
|
||||||
|
{Lower: 1, Upper: 2, Count: 2, LowerInclusive: false, UpperInclusive: true, Index: 1},
|
||||||
|
|
||||||
|
{Lower: 4, Upper: 8, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 3},
|
||||||
|
{Lower: 8, Upper: 16, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 4},
|
||||||
|
},
|
||||||
|
expectedNegativeBuckets: []Bucket{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
histogram: Histogram{
|
||||||
|
Schema: 0,
|
||||||
|
NegativeSpans: []Span{
|
||||||
|
{Offset: 0, Length: 5},
|
||||||
|
{Offset: 1, Length: 1},
|
||||||
|
},
|
||||||
|
NegativeBuckets: []int64{1, 2, -2, 1, -1, 0},
|
||||||
|
},
|
||||||
|
expectedPositiveBuckets: []Bucket{},
|
||||||
|
expectedNegativeBuckets: []Bucket{
|
||||||
|
{Lower: -1, Upper: -0.5, Count: 1, LowerInclusive: true, UpperInclusive: false, Index: 0},
|
||||||
|
{Lower: -2, Upper: -1, Count: 3, LowerInclusive: true, UpperInclusive: false, Index: 1},
|
||||||
|
{Lower: -4, Upper: -2, Count: 1, LowerInclusive: true, UpperInclusive: false, Index: 2},
|
||||||
|
{Lower: -8, Upper: -4, Count: 2, LowerInclusive: true, UpperInclusive: false, Index: 3},
|
||||||
|
{Lower: -16, Upper: -8, Count: 1, LowerInclusive: true, UpperInclusive: false, Index: 4},
|
||||||
|
|
||||||
|
{Lower: -64, Upper: -32, Count: 1, LowerInclusive: true, UpperInclusive: false, Index: 6},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
histogram: Histogram{
|
||||||
|
Schema: 0,
|
||||||
|
PositiveSpans: []Span{
|
||||||
|
{Offset: 0, Length: 4},
|
||||||
|
{Offset: 0, Length: 0},
|
||||||
|
{Offset: 0, Length: 3},
|
||||||
|
},
|
||||||
|
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0, 0},
|
||||||
|
NegativeSpans: []Span{
|
||||||
|
{Offset: 0, Length: 5},
|
||||||
|
{Offset: 1, Length: 0},
|
||||||
|
{Offset: 0, Length: 1},
|
||||||
|
},
|
||||||
|
NegativeBuckets: []int64{1, 2, -2, 1, -1, 0},
|
||||||
|
},
|
||||||
|
expectedPositiveBuckets: []Bucket{
|
||||||
|
{Lower: 0.5, Upper: 1, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 0},
|
||||||
|
{Lower: 1, Upper: 2, Count: 3, LowerInclusive: false, UpperInclusive: true, Index: 1},
|
||||||
|
{Lower: 2, Upper: 4, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 2},
|
||||||
|
{Lower: 4, Upper: 8, Count: 2, LowerInclusive: false, UpperInclusive: true, Index: 3},
|
||||||
|
{Lower: 8, Upper: 16, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 4},
|
||||||
|
{Lower: 16, Upper: 32, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 5},
|
||||||
|
{Lower: 32, Upper: 64, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 6},
|
||||||
|
},
|
||||||
|
expectedNegativeBuckets: []Bucket{
|
||||||
|
{Lower: -1, Upper: -0.5, Count: 1, LowerInclusive: true, UpperInclusive: false, Index: 0},
|
||||||
|
{Lower: -2, Upper: -1, Count: 3, LowerInclusive: true, UpperInclusive: false, Index: 1},
|
||||||
|
{Lower: -4, Upper: -2, Count: 1, LowerInclusive: true, UpperInclusive: false, Index: 2},
|
||||||
|
{Lower: -8, Upper: -4, Count: 2, LowerInclusive: true, UpperInclusive: false, Index: 3},
|
||||||
|
{Lower: -16, Upper: -8, Count: 1, LowerInclusive: true, UpperInclusive: false, Index: 4},
|
||||||
|
|
||||||
|
{Lower: -64, Upper: -32, Count: 1, LowerInclusive: true, UpperInclusive: false, Index: 6},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
histogram: Histogram{
|
||||||
|
Schema: 3,
|
||||||
|
PositiveSpans: []Span{
|
||||||
|
{Offset: -5, Length: 2}, // -5 -4
|
||||||
|
{Offset: 2, Length: 3}, // -1 0 1
|
||||||
|
{Offset: 2, Length: 2}, // 4 5
|
||||||
|
},
|
||||||
|
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0, 3},
|
||||||
|
},
|
||||||
|
expectedPositiveBuckets: []Bucket{
|
||||||
|
{Lower: 0.5946035575013605, Upper: 0.6484197773255048, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: -5},
|
||||||
|
{Lower: 0.6484197773255048, Upper: 0.7071067811865475, Count: 3, LowerInclusive: false, UpperInclusive: true, Index: -4},
|
||||||
|
|
||||||
|
{Lower: 0.8408964152537144, Upper: 0.9170040432046711, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: -1},
|
||||||
|
{Lower: 0.9170040432046711, Upper: 1, Count: 2, LowerInclusive: false, UpperInclusive: true, Index: 0},
|
||||||
|
{Lower: 1, Upper: 1.0905077326652577, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 1},
|
||||||
|
|
||||||
|
{Lower: 1.2968395546510096, Upper: 1.414213562373095, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 4},
|
||||||
|
{Lower: 1.414213562373095, Upper: 1.5422108254079407, Count: 4, LowerInclusive: false, UpperInclusive: true, Index: 5},
|
||||||
|
},
|
||||||
|
expectedNegativeBuckets: []Bucket{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
histogram: Histogram{
|
||||||
|
Schema: -2,
|
||||||
|
PositiveSpans: []Span{
|
||||||
|
{Offset: -2, Length: 4}, // -2 -1 0 1
|
||||||
|
{Offset: 2, Length: 2}, // 4 5
|
||||||
|
},
|
||||||
|
PositiveBuckets: []int64{1, 2, -2, 1, -1, 0},
|
||||||
|
},
|
||||||
|
expectedPositiveBuckets: []Bucket{
|
||||||
|
{Lower: 0.000244140625, Upper: 0.00390625, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: -2},
|
||||||
|
{Lower: 0.00390625, Upper: 0.0625, Count: 3, LowerInclusive: false, UpperInclusive: true, Index: -1},
|
||||||
|
{Lower: 0.0625, Upper: 1, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 0},
|
||||||
|
{Lower: 1, Upper: 16, Count: 2, LowerInclusive: false, UpperInclusive: true, Index: 1},
|
||||||
|
|
||||||
|
{Lower: 4096, Upper: 65536, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 4},
|
||||||
|
{Lower: 65536, Upper: 1048576, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 5},
|
||||||
|
},
|
||||||
|
expectedNegativeBuckets: []Bucket{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
histogram: Histogram{
|
||||||
|
Schema: -1,
|
||||||
|
PositiveSpans: []Span{
|
||||||
|
{Offset: -2, Length: 5}, // -2 -1 0 1 2
|
||||||
|
},
|
||||||
|
PositiveBuckets: []int64{1, 2, -2, 1, -1},
|
||||||
|
},
|
||||||
|
expectedPositiveBuckets: []Bucket{
|
||||||
|
{Lower: 0.015625, Upper: 0.0625, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: -2},
|
||||||
|
{Lower: 0.0625, Upper: 0.25, Count: 3, LowerInclusive: false, UpperInclusive: true, Index: -1},
|
||||||
|
{Lower: 0.25, Upper: 1, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 0},
|
||||||
|
{Lower: 1, Upper: 4, Count: 2, LowerInclusive: false, UpperInclusive: true, Index: 1},
|
||||||
|
{Lower: 4, Upper: 16, Count: 1, LowerInclusive: false, UpperInclusive: true, Index: 2},
|
||||||
|
},
|
||||||
|
expectedNegativeBuckets: []Bucket{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range cases {
|
||||||
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||||
|
it := c.histogram.PositiveBucketIterator()
|
||||||
|
actualPositiveBuckets := make([]Bucket, 0, len(c.expectedPositiveBuckets))
|
||||||
|
for it.Next() {
|
||||||
|
actualPositiveBuckets = append(actualPositiveBuckets, it.At())
|
||||||
|
}
|
||||||
|
require.Equal(t, c.expectedPositiveBuckets, actualPositiveBuckets)
|
||||||
|
it = c.histogram.NegativeBucketIterator()
|
||||||
|
actualNegativeBuckets := make([]Bucket, 0, len(c.expectedNegativeBuckets))
|
||||||
|
for it.Next() {
|
||||||
|
actualNegativeBuckets = append(actualNegativeBuckets, it.At())
|
||||||
|
}
|
||||||
|
require.Equal(t, c.expectedNegativeBuckets, actualNegativeBuckets)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2460,4 +2460,5 @@ func TestSparseHistogramRate(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
res := qry.Exec(test.Context())
|
res := qry.Exec(test.Context())
|
||||||
require.NoError(t, res.Err)
|
require.NoError(t, res.Err)
|
||||||
|
fmt.Println(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1533,7 +1533,6 @@ func TestSparseHistogramSpaceSavings(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
itIdx++
|
itIdx++
|
||||||
}
|
}
|
||||||
require.NoError(t, it.Err())
|
|
||||||
// _count metric.
|
// _count metric.
|
||||||
countLbls := ah.baseLabels.Copy()
|
countLbls := ah.baseLabels.Copy()
|
||||||
countLbls[0].Value = countLbls[0].Value + "_count"
|
countLbls[0].Value = countLbls[0].Value + "_count"
|
||||||
|
|
Loading…
Reference in a new issue