metrics: simplify interfaces, convert tests to table-driven

This commit is contained in:
Martin Holst Swende 2023-09-04 12:33:26 +02:00
parent 55c3396aac
commit 0295992985
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
5 changed files with 52 additions and 123 deletions

View file

@ -1,15 +1,7 @@
package metrics
type HistogramSnapshot interface {
Count() int64
Max() int64
Mean() float64
Min() int64
Percentile(float64) float64
Percentiles([]float64) []float64
StdDev() float64
Sum() int64
Variance() float64
SampleSnapshot
}
// Histograms calculate distribution statistics from a series of int64 values.
@ -56,52 +48,6 @@ func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram {
return c
}
// histogramSnapshot is a read-only copy of another Histogram.
type histogramSnapshot struct {
sample *sampleSnapshot
}
// Count returns the number of samples recorded at the time the snapshot was
// taken.
func (h *histogramSnapshot) Count() int64 { return h.sample.Count() }
// Max returns the maximum value in the sample at the time the snapshot was
// taken.
func (h *histogramSnapshot) Max() int64 { return h.sample.Max() }
// Mean returns the mean of the values in the sample at the time the snapshot
// was taken.
func (h *histogramSnapshot) Mean() float64 { return h.sample.Mean() }
// Min returns the minimum value in the sample at the time the snapshot was
// taken.
func (h *histogramSnapshot) Min() int64 { return h.sample.Min() }
// Percentile returns an arbitrary percentile of values in the sample at the
// time the snapshot was taken.
func (h *histogramSnapshot) Percentile(p float64) float64 {
return h.sample.Percentile(p)
}
// Percentiles returns a slice of arbitrary percentiles of values in the sample
// at the time the snapshot was taken.
func (h *histogramSnapshot) Percentiles(ps []float64) []float64 {
return h.sample.Percentiles(ps)
}
// Snapshot returns the snapshot.
func (h *histogramSnapshot) Snapshot() HistogramSnapshot { return h }
// StdDev returns the standard deviation of the values in the sample at the
// time the snapshot was taken.
func (h *histogramSnapshot) StdDev() float64 { return h.sample.StdDev() }
// Sum returns the sum in the sample at the time the snapshot was taken.
func (h *histogramSnapshot) Sum() int64 { return h.sample.Sum() }
// Variance returns the variance of inputs at the time the snapshot was taken.
func (h *histogramSnapshot) Variance() float64 { return h.sample.Variance() }
// NilHistogram is a no-op Histogram.
type NilHistogram struct{}
@ -120,7 +66,7 @@ func (h *StandardHistogram) Clear() { h.sample.Clear() }
// Snapshot returns a read-only copy of the histogram.
func (h *StandardHistogram) Snapshot() HistogramSnapshot {
return &histogramSnapshot{sample: h.sample.Snapshot().(*sampleSnapshot)}
return h.sample.Snapshot()
}
// Update samples a new value.

View file

@ -78,6 +78,11 @@ func (h *runtimeHistogramSnapshot) Count() int64 {
return count
}
// Size returns the size of the sample at the time the snapshot was taken.
func (h *runtimeHistogramSnapshot) Size() int {
return len(h.Counts)
}
// Mean returns an approximation of the mean.
func (h *runtimeHistogramSnapshot) Mean() float64 {
if len(h.Counts) == 0 {

View file

@ -21,7 +21,6 @@ type SampleSnapshot interface {
Size() int
StdDev() float64
Sum() int64
Values() []int64
Variance() float64
}

View file

@ -87,69 +87,43 @@ func BenchmarkUniformSample1028(b *testing.B) {
benchmarkSample(b, NewUniformSample(1028))
}
func TestExpDecaySample10(t *testing.T) {
sw := NewExpDecaySample(100, 0.99)
for i := 0; i < 10; i++ {
sw.Update(int64(i))
}
s := sw.Snapshot()
if size := s.Count(); size != 10 {
t.Errorf("s.Count(): 10 != %v\n", size)
}
if size := s.Size(); size != 10 {
t.Errorf("s.Size(): 10 != %v\n", size)
}
if l := len(s.Values()); l != 10 {
t.Errorf("len(s.Values()): 10 != %v\n", l)
}
for _, v := range s.Values() {
if v > 10 || v < 0 {
t.Errorf("out of range [0, 10): %v\n", v)
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func TestExpDecaySample100(t *testing.T) {
sw := NewExpDecaySample(1000, 0.01)
for i := 0; i < 100; i++ {
sw.Update(int64(i))
func TestExpDecaySample(t *testing.T) {
for _, tc := range []struct {
reservoirSize int
alpha float64
updates int
}{
{100, 0.99, 10},
{1000, 0.01, 100},
{100, 0.99, 1000},
} {
sample := NewExpDecaySample(tc.reservoirSize, tc.alpha)
for i := 0; i < tc.updates; i++ {
sample.Update(int64(i))
}
s := sw.Snapshot()
if size := s.Count(); size != 100 {
t.Errorf("s.Count(): 100 != %v\n", size)
snap := sample.Snapshot()
if have, want := int(snap.Count()), tc.updates; have != want {
t.Errorf("have %d want %d", have, want)
}
if size := s.Size(); size != 100 {
t.Errorf("s.Size(): 100 != %v\n", size)
if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want {
t.Errorf("have %d want %d", have, want)
}
if l := len(s.Values()); l != 100 {
t.Errorf("len(s.Values()): 100 != %v\n", l)
values := snap.(*sampleSnapshot).values
if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want {
t.Errorf("have %d want %d", have, want)
}
for _, v := range s.Values() {
if v > 100 || v < 0 {
t.Errorf("out of range [0, 100): %v\n", v)
for _, v := range values {
if v > int64(tc.updates) || v < 0 {
t.Errorf("out of range [0, %d): %v", tc.updates, v)
}
}
}
func TestExpDecaySample1000(t *testing.T) {
sw := NewExpDecaySample(100, 0.99)
for i := 0; i < 1000; i++ {
sw.Update(int64(i))
}
s := sw.Snapshot()
if size := s.Count(); size != 1000 {
t.Errorf("s.Count(): 1000 != %v\n", size)
}
if size := s.Size(); size != 100 {
t.Errorf("s.Size(): 100 != %v\n", size)
}
if l := len(s.Values()); l != 100 {
t.Errorf("len(s.Values()): 100 != %v\n", l)
}
for _, v := range s.Values() {
if v > 1000 || v < 0 {
t.Errorf("out of range [0, 1000): %v\n", v)
}
}
}
@ -167,7 +141,7 @@ func TestExpDecaySampleNanosecondRegression(t *testing.T) {
sw.Update(20)
}
s := sw.Snapshot()
v := s.Values()
v := s.(*sampleSnapshot).values
avg := float64(0)
for i := 0; i < len(v); i++ {
avg += float64(v[i])
@ -221,10 +195,12 @@ func TestUniformSample(t *testing.T) {
if size := s.Size(); size != 100 {
t.Errorf("s.Size(): 100 != %v\n", size)
}
if l := len(s.Values()); l != 100 {
values := s.(*sampleSnapshot).values
if l := len(values); l != 100 {
t.Errorf("len(s.Values()): 100 != %v\n", l)
}
for _, v := range s.Values() {
for _, v := range values {
if v > 1000 || v < 0 {
t.Errorf("out of range [0, 100): %v\n", v)
}
@ -238,7 +214,7 @@ func TestUniformSampleIncludesTail(t *testing.T) {
sw.Update(int64(i))
}
s := sw.Snapshot()
v := s.Values()
v := s.(*sampleSnapshot).values
sum := 0
exp := (max - 1) * max / 2
for i := 0; i < len(v); i++ {

View file

@ -89,8 +89,8 @@ func (t *StandardTimer) Snapshot() TimerSnapshot {
t.mutex.Lock()
defer t.mutex.Unlock()
return &timerSnapshot{
histogram: t.histogram.Snapshot().(*histogramSnapshot),
meter: t.meter.Snapshot().(*meterSnapshot),
histogram: t.histogram.Snapshot(),
meter: t.meter.Snapshot(),
}
}
@ -124,8 +124,8 @@ func (t *StandardTimer) UpdateSince(ts time.Time) {
// timerSnapshot is a read-only copy of another Timer.
type timerSnapshot struct {
histogram *histogramSnapshot
meter *meterSnapshot
histogram HistogramSnapshot
meter MeterSnapshot
}
// Count returns the number of events recorded at the time the snapshot was
@ -135,6 +135,9 @@ func (t *timerSnapshot) Count() int64 { return t.histogram.Count() }
// Max returns the maximum value at the time the snapshot was taken.
func (t *timerSnapshot) Max() int64 { return t.histogram.Max() }
// Size returns the size of the sample at the time the snapshot was taken.
func (t *timerSnapshot) Size() int { return t.histogram.Size() }
// Mean returns the mean value at the time the snapshot was taken.
func (t *timerSnapshot) Mean() float64 { return t.histogram.Mean() }