metrics/generic: make VividCortex/gohistogram safe
Peter Bourgon
7 years ago
120 | 120 |
type Histogram struct {
|
121 | 121 |
Name string
|
122 | 122 |
lvs lv.LabelValues
|
123 | |
h gohistogram.Histogram
|
|
123 |
h *safeHistogram
|
124 | 124 |
}
|
125 | 125 |
|
126 | 126 |
// NewHistogram returns a numeric histogram based on VividCortex/gohistogram. A
|
|
128 | 128 |
func NewHistogram(name string, buckets int) *Histogram {
|
129 | 129 |
return &Histogram{
|
130 | 130 |
Name: name,
|
131 | |
h: gohistogram.NewHistogram(buckets),
|
|
131 |
h: &safeHistogram{Histogram: gohistogram.NewHistogram(buckets)},
|
132 | 132 |
}
|
133 | 133 |
}
|
134 | 134 |
|
|
142 | 142 |
|
143 | 143 |
// Observe implements Histogram.
|
144 | 144 |
func (h *Histogram) Observe(value float64) {
|
|
145 |
h.h.Lock()
|
|
146 |
defer h.h.Unlock()
|
145 | 147 |
h.h.Add(value)
|
146 | 148 |
}
|
147 | 149 |
|
148 | 150 |
// Quantile returns the value of the quantile q, 0.0 < q < 1.0.
|
149 | 151 |
func (h *Histogram) Quantile(q float64) float64 {
|
|
152 |
h.h.RLock()
|
|
153 |
defer h.h.RUnlock()
|
150 | 154 |
return h.h.Quantile(q)
|
151 | 155 |
}
|
152 | 156 |
|
|
158 | 162 |
// Print writes a string representation of the histogram to the passed writer.
|
159 | 163 |
// Useful for printing to a terminal.
|
160 | 164 |
func (h *Histogram) Print(w io.Writer) {
|
|
165 |
h.h.RLock()
|
|
166 |
defer h.h.RUnlock()
|
161 | 167 |
fmt.Fprintf(w, h.h.String())
|
|
168 |
}
|
|
169 |
|
|
170 |
// safeHistogram exists as gohistogram.Histogram is not goroutine-safe.
|
|
171 |
type safeHistogram struct {
|
|
172 |
sync.RWMutex
|
|
173 |
gohistogram.Histogram
|
162 | 174 |
}
|
163 | 175 |
|
164 | 176 |
// Bucket is a range in a histogram which aggregates observations.
|
6 | 6 |
import (
|
7 | 7 |
"math"
|
8 | 8 |
"math/rand"
|
|
9 |
"sync"
|
9 | 10 |
"testing"
|
10 | 11 |
|
11 | 12 |
"github.com/go-kit/kit/metrics/generic"
|
|
51 | 52 |
}
|
52 | 53 |
}
|
53 | 54 |
|
|
55 |
func TestIssue424(t *testing.T) {
|
|
56 |
var (
|
|
57 |
histogram = generic.NewHistogram("dont_panic", 50)
|
|
58 |
concurrency = 100
|
|
59 |
operations = 1000
|
|
60 |
wg sync.WaitGroup
|
|
61 |
)
|
|
62 |
|
|
63 |
wg.Add(concurrency)
|
|
64 |
for i := 0; i < concurrency; i++ {
|
|
65 |
go func() {
|
|
66 |
defer wg.Done()
|
|
67 |
for j := 0; j < operations; j++ {
|
|
68 |
histogram.Observe(float64(j))
|
|
69 |
histogram.Observe(histogram.Quantile(0.5))
|
|
70 |
}
|
|
71 |
}()
|
|
72 |
}
|
|
73 |
wg.Wait()
|
|
74 |
}
|
|
75 |
|
54 | 76 |
func TestSimpleHistogram(t *testing.T) {
|
55 | 77 |
histogram := generic.NewSimpleHistogram().With("label", "simple_histogram").(*generic.SimpleHistogram)
|
56 | 78 |
var (
|