1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
package memmetrics
import (
"fmt"
"time"
"github.com/HdrHistogram/hdrhistogram-go"
"github.com/vulcand/oxy/v2/internal/holsterv4/clock"
)
// HDRHistogram is a tiny wrapper around github.com/HdrHistogram/hdrhistogram-go that provides convenience functions for measuring http latencies.
type HDRHistogram struct {
// lowest trackable value
low int64
// highest trackable value
high int64
// significant figures
sigfigs int
h *hdrhistogram.Histogram
}
// NewHDRHistogram creates a new HDRHistogram.
func NewHDRHistogram(low, high int64, sigfigs int) (h *HDRHistogram, err error) {
defer func() {
if msg := recover(); msg != nil {
err = fmt.Errorf("%s", msg)
}
}()
return &HDRHistogram{
low: low,
high: high,
sigfigs: sigfigs,
h: hdrhistogram.New(low, high, sigfigs),
}, nil
}
// Export exports a HDRHistogram.
func (h *HDRHistogram) Export() *HDRHistogram {
var hist *hdrhistogram.Histogram
if h.h != nil {
snapshot := h.h.Export()
hist = hdrhistogram.Import(snapshot)
}
return &HDRHistogram{low: h.low, high: h.high, sigfigs: h.sigfigs, h: hist}
}
// LatencyAtQuantile sets latency at quantile with microsecond precision.
func (h *HDRHistogram) LatencyAtQuantile(q float64) time.Duration {
return time.Duration(h.ValueAtQuantile(q)) * clock.Microsecond
}
// RecordLatencies Records latencies with microsecond precision.
func (h *HDRHistogram) RecordLatencies(d time.Duration, n int64) error {
return h.RecordValues(int64(d/clock.Microsecond), n)
}
// Reset resets a HDRHistogram.
func (h *HDRHistogram) Reset() {
h.h.Reset()
}
// ValueAtQuantile sets value at quantile.
func (h *HDRHistogram) ValueAtQuantile(q float64) int64 {
return h.h.ValueAtQuantile(q)
}
// RecordValues sets record values.
func (h *HDRHistogram) RecordValues(v, n int64) error {
return h.h.RecordValues(v, n)
}
// Merge merges a HDRHistogram.
func (h *HDRHistogram) Merge(other *HDRHistogram) error {
if other == nil {
return fmt.Errorf("other is nil")
}
h.h.Merge(other.h)
return nil
}
type rhOption func(r *RollingHDRHistogram) error
// RollingHDRHistogram holds multiple histograms and rotates every period.
// It provides resulting histogram as a result of a call of 'Merged' function.
type RollingHDRHistogram struct {
idx int
lastRoll clock.Time
period time.Duration
bucketCount int
low int64
high int64
sigfigs int
buckets []*HDRHistogram
}
// NewRollingHDRHistogram created a new RollingHDRHistogram.
func NewRollingHDRHistogram(low, high int64, sigfigs int, period time.Duration, bucketCount int, options ...rhOption) (*RollingHDRHistogram, error) {
rh := &RollingHDRHistogram{
bucketCount: bucketCount,
period: period,
low: low,
high: high,
sigfigs: sigfigs,
}
for _, o := range options {
if err := o(rh); err != nil {
return nil, err
}
}
buckets := make([]*HDRHistogram, rh.bucketCount)
for i := range buckets {
h, err := NewHDRHistogram(low, high, sigfigs)
if err != nil {
return nil, err
}
buckets[i] = h
}
rh.buckets = buckets
return rh, nil
}
// Export exports a RollingHDRHistogram.
func (r *RollingHDRHistogram) Export() *RollingHDRHistogram {
export := &RollingHDRHistogram{}
export.idx = r.idx
export.lastRoll = r.lastRoll
export.period = r.period
export.bucketCount = r.bucketCount
export.low = r.low
export.high = r.high
export.sigfigs = r.sigfigs
exportBuckets := make([]*HDRHistogram, len(r.buckets))
for i, hist := range r.buckets {
exportBuckets[i] = hist.Export()
}
export.buckets = exportBuckets
return export
}
// Append appends a RollingHDRHistogram.
func (r *RollingHDRHistogram) Append(o *RollingHDRHistogram) error {
if r.bucketCount != o.bucketCount || r.period != o.period || r.low != o.low || r.high != o.high || r.sigfigs != o.sigfigs {
return fmt.Errorf("can't merge")
}
for i := range r.buckets {
if err := r.buckets[i].Merge(o.buckets[i]); err != nil {
return err
}
}
return nil
}
// Reset resets a RollingHDRHistogram.
func (r *RollingHDRHistogram) Reset() {
r.idx = 0
r.lastRoll = clock.Now().UTC()
for _, b := range r.buckets {
b.Reset()
}
}
func (r *RollingHDRHistogram) rotate() {
r.idx = (r.idx + 1) % len(r.buckets)
r.buckets[r.idx].Reset()
}
// Merged gets merged histogram.
func (r *RollingHDRHistogram) Merged() (*HDRHistogram, error) {
m, err := NewHDRHistogram(r.low, r.high, r.sigfigs)
if err != nil {
return m, err
}
for _, h := range r.buckets {
if errMerge := m.Merge(h); errMerge != nil {
return nil, errMerge
}
}
return m, nil
}
func (r *RollingHDRHistogram) getHist() *HDRHistogram {
if clock.Now().UTC().Sub(r.lastRoll) >= r.period {
r.rotate()
r.lastRoll = clock.Now().UTC()
}
return r.buckets[r.idx]
}
// RecordLatencies sets records latencies.
func (r *RollingHDRHistogram) RecordLatencies(v time.Duration, n int64) error {
return r.getHist().RecordLatencies(v, n)
}
// RecordValues sets record values.
func (r *RollingHDRHistogram) RecordValues(v, n int64) error {
return r.getHist().RecordValues(v, n)
}
|