github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/x/histogram.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 // implied. See the License for the specific language governing 13 // permissions and limitations under the License. 14 15 package x 16 17 import ( 18 "sync" 19 "time" 20 21 "github.com/codahale/hdrhistogram" 22 ) 23 24 const ( 25 // The number of histograms to keep in rolling window. 26 histWrapNum = 2 27 ) 28 29 // A slidingHistogram is a wrapper around an 30 // hdrhistogram.WindowedHistogram. The caller must enforce proper 31 // synchronization. 32 type slidingHistogram struct { 33 windowed *hdrhistogram.WindowedHistogram 34 nextT time.Time 35 duration time.Duration 36 } 37 38 // newSlidingHistogram creates a new windowed HDRHistogram with the given 39 // parameters. Data is kept in the active window for approximately the given 40 // duration. See the documentation for hdrhistogram.WindowedHistogram for 41 // details. 42 func newSlidingHistogram(duration time.Duration, maxVal int64, sigFigs int) *slidingHistogram { 43 if duration <= 0 { 44 panic("cannot create a sliding histogram with nonpositive duration") 45 } 46 return &slidingHistogram{ 47 nextT: time.Now(), 48 duration: duration, 49 windowed: hdrhistogram.NewWindowed(histWrapNum, 0, maxVal, sigFigs), 50 } 51 } 52 53 func (h *slidingHistogram) tick() { 54 h.nextT = h.nextT.Add(h.duration / histWrapNum) 55 h.windowed.Rotate() 56 } 57 58 func (h *slidingHistogram) nextTick() time.Time { 59 return h.nextT 60 } 61 62 func (h *slidingHistogram) Current() *hdrhistogram.Histogram { 63 for h.nextTick().Before(time.Now()) { 64 h.tick() 65 } 66 return h.windowed.Merge() 67 } 68 69 func (h *slidingHistogram) RecordValue(v int64) error { 70 return h.windowed.Current.RecordValue(v) 71 } 72 73 // A Histogram collects observed values by keeping bucketed counts. For 74 // convenience, pb.y two sets of buckets are kept: A cumulative set (i.e. 75 // data is never evicted) and a windowed set (which keeps only recently 76 // collected samples). 77 // 78 // Top-level methods generally apply to the cumulative buckets; the windowed 79 // variant is exposed through the Windowed method. 80 type Histogram struct { 81 sync.Mutex 82 cumulative *hdrhistogram.Histogram 83 sliding *slidingHistogram 84 maxVal int64 85 } 86 87 // NewHistogram initializes a given Histogram. The contained windowed histogram 88 // rotates every 'duration'; both the windowed and the cumulative histogram 89 // track nonnegative values up to 'maxVal' with 'sigFigs' decimal points of 90 // precision. 91 func NewHistogram(duration time.Duration, maxVal int64, sigFigs int) *Histogram { 92 dHist := newSlidingHistogram(duration, maxVal, sigFigs) 93 h := &Histogram{} 94 h.cumulative = hdrhistogram.New(0, maxVal, sigFigs) 95 h.sliding = dHist 96 h.maxVal = maxVal 97 return h 98 } 99 100 // RecordValue adds the given value to the histogram. Recording a value in 101 // excess of the configured maximum value for that histogram results in 102 // recording the maximum value instead. 103 func (h *Histogram) RecordValue(v int64) { 104 h.Lock() 105 defer h.Unlock() 106 107 if h.sliding.RecordValue(v) != nil { 108 _ = h.sliding.RecordValue(h.maxVal) 109 } 110 if h.cumulative.RecordValue(v) != nil { 111 _ = h.cumulative.RecordValue(h.maxVal) 112 } 113 }