@cryptotaxi247 / kubo / commits / a8f0d3d41

Vendor github.com/codahale/metrics

Tommi Virtanen committed Apr 28, 2015 at 19:04 UTC a8f0d3d4112a2e02cd81889d2352f0ed2fa97b75
21 files changed +1798
Godeps/Godeps.json
+8
@@ -57,6 +57,14 @@
57 "ImportPath": "github.com/chuckpreslar/inflect",
58 "Rev": "423e3ac59c611e2d549527ab8c15fb99335d30ba"
59 },
60 + {
61 + "ImportPath": "github.com/codahale/hdrhistogram",
62 + "Rev": "5fd85ec0b4e2dd5d4158d257d943f2e586d86b62"
63 + },
64 + {
65 + "ImportPath": "github.com/codahale/metrics",
66 + "Rev": "7d3beb1b480077e77c08a6f6c65ea969f6e91420"
67 + },
68 {
69 "ImportPath": "github.com/coreos/go-semver/semver",
70 "Rev": "568e959cd89871e61434c1143528d9162da89ef2"
Godeps/_workspace/src/github.com/codahale/hdrhistogram/.travis.yml new
+9
@@ -0,0 +1,9 @@
1 +language: go
2 +go:
3 + - 1.3.3
4 +notifications:
5 + # See http://about.travis-ci.org/docs/user/build-configuration/ to learn more
6 + # about configuring notification recipients and more.
7 + email:
8 + recipients:
9 + - coda.hale@gmail.com
Godeps/_workspace/src/github.com/codahale/hdrhistogram/LICENSE new
+21
@@ -0,0 +1,21 @@
1 +The MIT License (MIT)
2 +
3 +Copyright (c) 2014 Coda Hale
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in
13 +all copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 +THE SOFTWARE.
Godeps/_workspace/src/github.com/codahale/hdrhistogram/README.md new
+15
@@ -0,0 +1,15 @@
1 +hdrhistogram
2 +============
3 +
4 +[![Build Status](https://travis-ci.org/codahale/hdrhistogram.png?branch=master)](https://travis-ci.org/codahale/hdrhistogram)
5 +
6 +A pure Go implementation of the [HDR Histogram](https://github.com/HdrHistogram/HdrHistogram).
7 +
8 +> A Histogram that supports recording and analyzing sampled data value counts
9 +> across a configurable integer value range with configurable value precision
10 +> within the range. Value precision is expressed as the number of significant
11 +> digits in the value recording, and provides control over value quantization
12 +> behavior across the value range and the subsequent value resolution at any
13 +> given level.
14 +
15 +For documentation, check [godoc](http://godoc.org/github.com/codahale/hdrhistogram).
Godeps/_workspace/src/github.com/codahale/hdrhistogram/hdr.go new
+513
@@ -0,0 +1,513 @@
1 +// Package hdrhistogram provides an implementation of Gil Tene's HDR Histogram
2 +// data structure. The HDR Histogram allows for fast and accurate analysis of
3 +// the extreme ranges of data with non-normal distributions, like latency.
4 +package hdrhistogram
5 +
6 +import (
7 + "fmt"
8 + "math"
9 +)
10 +
11 +// A Bracket is a part of a cumulative distribution.
12 +type Bracket struct {
13 + Quantile float64
14 + Count, ValueAt int64
15 +}
16 +
17 +// A Snapshot is an exported view of a Histogram, useful for serializing them.
18 +// A Histogram can be constructed from it by passing it to Import.
19 +type Snapshot struct {
20 + LowestTrackableValue int64
21 + HighestTrackableValue int64
22 + SignificantFigures int64
23 + Counts []int64
24 +}
25 +
26 +// A Histogram is a lossy data structure used to record the distribution of
27 +// non-normally distributed data (like latency) with a high degree of accuracy
28 +// and a bounded degree of precision.
29 +type Histogram struct {
30 + lowestTrackableValue int64
31 + highestTrackableValue int64
32 + unitMagnitude int64
33 + significantFigures int64
34 + subBucketHalfCountMagnitude int32
35 + subBucketHalfCount int32
36 + subBucketMask int64
37 + subBucketCount int32
38 + bucketCount int32
39 + countsLen int32
40 + totalCount int64
41 + counts []int64
42 +}
43 +
44 +// New returns a new Histogram instance capable of tracking values in the given
45 +// range and with the given amount of precision.
46 +func New(minValue, maxValue int64, sigfigs int) *Histogram {
47 + if sigfigs < 1 || 5 < sigfigs {
48 + panic(fmt.Errorf("sigfigs must be [1,5] (was %d)", sigfigs))
49 + }
50 +
51 + largestValueWithSingleUnitResolution := 2 * math.Pow10(sigfigs)
52 + subBucketCountMagnitude := int32(math.Ceil(math.Log2(float64(largestValueWithSingleUnitResolution))))
53 +
54 + subBucketHalfCountMagnitude := subBucketCountMagnitude
55 + if subBucketHalfCountMagnitude < 1 {
56 + subBucketHalfCountMagnitude = 1
57 + }
58 + subBucketHalfCountMagnitude--
59 +
60 + unitMagnitude := int32(math.Floor(math.Log2(float64(minValue))))
61 + if unitMagnitude < 0 {
62 + unitMagnitude = 0
63 + }
64 +
65 + subBucketCount := int32(math.Pow(2, float64(subBucketHalfCountMagnitude)+1))
66 +
67 + subBucketHalfCount := subBucketCount / 2
68 + subBucketMask := int64(subBucketCount-1) << uint(unitMagnitude)
69 +
70 + // determine exponent range needed to support the trackable value with no
71 + // overflow:
72 + smallestUntrackableValue := int64(subBucketCount) << uint(unitMagnitude)
73 + bucketsNeeded := int32(1)
74 + for smallestUntrackableValue < maxValue {
75 + smallestUntrackableValue <<= 1
76 + bucketsNeeded++
77 + }
78 +
79 + bucketCount := bucketsNeeded
80 + countsLen := (bucketCount + 1) * (subBucketCount / 2)
81 +
82 + return &Histogram{
83 + lowestTrackableValue: minValue,
84 + highestTrackableValue: maxValue,
85 + unitMagnitude: int64(unitMagnitude),
86 + significantFigures: int64(sigfigs),
87 + subBucketHalfCountMagnitude: subBucketHalfCountMagnitude,
88 + subBucketHalfCount: subBucketHalfCount,
89 + subBucketMask: subBucketMask,
90 + subBucketCount: subBucketCount,
91 + bucketCount: bucketCount,
92 + countsLen: countsLen,
93 + totalCount: 0,
94 + counts: make([]int64, countsLen),
95 + }
96 +}
97 +
98 +// ByteSize returns an estimate of the amount of memory allocated to the
99 +// histogram in bytes.
100 +//
101 +// N.B.: This does not take into account the overhead for slices, which are
102 +// small, constant, and specific to the compiler version.
103 +func (h *Histogram) ByteSize() int {
104 + return 6*8 + 5*4 + len(h.counts)*8
105 +}
106 +
107 +// Merge merges the data stored in the given histogram with the receiver,
108 +// returning the number of recorded values which had to be dropped.
109 +func (h *Histogram) Merge(from *Histogram) (dropped int64) {
110 + i := from.rIterator()
111 + for i.next() {
112 + v := i.valueFromIdx
113 + c := i.countAtIdx
114 +
115 + if h.RecordValues(v, c) != nil {
116 + dropped += c
117 + }
118 + }
119 +
120 + return
121 +}
122 +
123 +// TotalCount returns total number of values recorded.
124 +func (h *Histogram) TotalCount() int64 {
125 + return h.totalCount
126 +}
127 +
128 +// Max returns the approximate maximum recorded value.
129 +func (h *Histogram) Max() int64 {
130 + var max int64
131 + i := h.iterator()
132 + for i.next() {
133 + if i.countAtIdx != 0 {
134 + max = i.highestEquivalentValue
135 + }
136 + }
137 + return h.lowestEquivalentValue(max)
138 +}
139 +
140 +// Min returns the approximate minimum recorded value.
141 +func (h *Histogram) Min() int64 {
142 + var min int64
143 + i := h.iterator()
144 + for i.next() {
145 + if i.countAtIdx != 0 && min == 0 {
146 + min = i.highestEquivalentValue
147 + break
148 + }
149 + }
150 + return h.lowestEquivalentValue(min)
151 +}
152 +
153 +// Mean returns the approximate arithmetic mean of the recorded values.
154 +func (h *Histogram) Mean() float64 {
155 + var total int64
156 + i := h.iterator()
157 + for i.next() {
158 + if i.countAtIdx != 0 {
159 + total += i.countAtIdx * h.medianEquivalentValue(i.valueFromIdx)
160 + }
161 + }
162 + return float64(total) / float64(h.totalCount)
163 +}
164 +
165 +// StdDev returns the approximate standard deviation of the recorded values.
166 +func (h *Histogram) StdDev() float64 {
167 + mean := h.Mean()
168 + geometricDevTotal := 0.0
169 +
170 + i := h.iterator()
171 + for i.next() {
172 + if i.countAtIdx != 0 {
173 + dev := float64(h.medianEquivalentValue(i.valueFromIdx)) - mean
174 + geometricDevTotal += (dev * dev) * float64(i.countAtIdx)
175 + }
176 + }
177 +
178 + return math.Sqrt(geometricDevTotal / float64(h.totalCount))
179 +}
180 +
181 +// Reset deletes all recorded values and restores the histogram to its original
182 +// state.
183 +func (h *Histogram) Reset() {
184 + h.totalCount = 0
185 + for i := range h.counts {
186 + h.counts[i] = 0
187 + }
188 +}
189 +
190 +// RecordValue records the given value, returning an error if the value is out
191 +// of range.
192 +func (h *Histogram) RecordValue(v int64) error {
193 + return h.RecordValues(v, 1)
194 +}
195 +
196 +// RecordCorrectedValue records the given value, correcting for stalls in the
197 +// recording process. This only works for processes which are recording values
198 +// at an expected interval (e.g., doing jitter analysis). Processes which are
199 +// recording ad-hoc values (e.g., latency for incoming requests) can't take
200 +// advantage of this.
201 +func (h *Histogram) RecordCorrectedValue(v, expectedInterval int64) error {
202 + if err := h.RecordValue(v); err != nil {
203 + return err
204 + }
205 +
206 + if expectedInterval <= 0 || v <= expectedInterval {
207 + return nil
208 + }
209 +
210 + missingValue := v - expectedInterval
211 + for missingValue >= expectedInterval {
212 + if err := h.RecordValue(missingValue); err != nil {
213 + return err
214 + }
215 + missingValue -= expectedInterval
216 + }
217 +
218 + return nil
219 +}
220 +
221 +// RecordValues records n occurrences of the given value, returning an error if
222 +// the value is out of range.
223 +func (h *Histogram) RecordValues(v, n int64) error {
224 + idx := h.countsIndexFor(v)
225 + if idx < 0 || int(h.countsLen) <= idx {
226 + return fmt.Errorf("value %d is too large to be recorded", v)
227 + }
228 + h.counts[idx] += n
229 + h.totalCount += n
230 +
231 + return nil
232 +}
233 +
234 +// ValueAtQuantile returns the recorded value at the given quantile (0..100).
235 +func (h *Histogram) ValueAtQuantile(q float64) int64 {
236 + if q > 100 {
237 + q = 100
238 + }
239 +
240 + total := int64(0)
241 + countAtPercentile := int64(((q / 100) * float64(h.totalCount)) + 0.5)
242 +
243 + i := h.iterator()
244 + for i.next() {
245 + total += i.countAtIdx
246 + if total >= countAtPercentile {
247 + return h.highestEquivalentValue(i.valueFromIdx)
248 + }
249 + }
250 +
251 + return 0
252 +}
253 +
254 +// CumulativeDistribution returns an ordered list of brackets of the
255 +// distribution of recorded values.
256 +func (h *Histogram) CumulativeDistribution() []Bracket {
257 + var result []Bracket
258 +
259 + i := h.pIterator(1)
260 + for i.next() {
261 + result = append(result, Bracket{
262 + Quantile: i.percentile,
263 + Count: i.countToIdx,
264 + ValueAt: i.highestEquivalentValue,
265 + })
266 + }
267 +
268 + return result
269 +}
270 +
271 +// Equals returns true if the two Histograms are equivalent, false if not.
272 +func (h *Histogram) Equals(other *Histogram) bool {
273 + switch {
274 + case
275 + h.lowestTrackableValue != other.lowestTrackableValue,
276 + h.highestTrackableValue != other.highestTrackableValue,
277 + h.unitMagnitude != other.unitMagnitude,
278 + h.significantFigures != other.significantFigures,
279 + h.subBucketHalfCountMagnitude != other.subBucketHalfCountMagnitude,
280 + h.subBucketHalfCount != other.subBucketHalfCount,
281 + h.subBucketMask != other.subBucketMask,
282 + h.subBucketCount != other.subBucketCount,
283 + h.bucketCount != other.bucketCount,
284 + h.countsLen != other.countsLen,
285 + h.totalCount != other.totalCount:
286 + return false
287 + default:
288 + for i, c := range h.counts {
289 + if c != other.counts[i] {
290 + return false
291 + }
292 + }
293 + }
294 + return true
295 +}
296 +
297 +// Export returns a snapshot view of the Histogram. This can be later passed to
298 +// Import to construct a new Histogram with the same state.
299 +func (h *Histogram) Export() *Snapshot {
300 + return &Snapshot{
301 + LowestTrackableValue: h.lowestTrackableValue,
302 + HighestTrackableValue: h.highestTrackableValue,
303 + SignificantFigures: h.significantFigures,
304 + Counts: h.counts,
305 + }
306 +}
307 +
308 +// Import returns a new Histogram populated from the Snapshot data.
309 +func Import(s *Snapshot) *Histogram {
310 + h := New(s.LowestTrackableValue, s.HighestTrackableValue, int(s.SignificantFigures))
311 + h.counts = s.Counts
312 + totalCount := int64(0)
313 + for i := int32(0); i < h.countsLen; i++ {
314 + countAtIndex := h.counts[i]
315 + if countAtIndex > 0 {
316 + totalCount += countAtIndex
317 + }
318 + }
319 + h.totalCount = totalCount
320 + return h
321 +}
322 +
323 +func (h *Histogram) iterator() *iterator {
324 + return &iterator{
325 + h: h,
326 + subBucketIdx: -1,
327 + }
328 +}
329 +
330 +func (h *Histogram) rIterator() *rIterator {
331 + return &rIterator{
332 + iterator: iterator{
333 + h: h,
334 + subBucketIdx: -1,
335 + },
336 + }
337 +}
338 +
339 +func (h *Histogram) pIterator(ticksPerHalfDistance int32) *pIterator {
340 + return &pIterator{
341 + iterator: iterator{
342 + h: h,
343 + subBucketIdx: -1,
344 + },
345 + ticksPerHalfDistance: ticksPerHalfDistance,
346 + }
347 +}
348 +
349 +func (h *Histogram) sizeOfEquivalentValueRange(v int64) int64 {
350 + bucketIdx := h.getBucketIndex(v)
351 + subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
352 + adjustedBucket := bucketIdx
353 + if subBucketIdx >= h.subBucketCount {
354 + adjustedBucket++
355 + }
356 + return int64(1) << uint(h.unitMagnitude+int64(adjustedBucket))
357 +}
358 +
359 +func (h *Histogram) valueFromIndex(bucketIdx, subBucketIdx int32) int64 {
360 + return int64(subBucketIdx) << uint(int64(bucketIdx)+h.unitMagnitude)
361 +}
362 +
363 +func (h *Histogram) lowestEquivalentValue(v int64) int64 {
364 + bucketIdx := h.getBucketIndex(v)
365 + subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
366 + return h.valueFromIndex(bucketIdx, subBucketIdx)
367 +}
368 +
369 +func (h *Histogram) nextNonEquivalentValue(v int64) int64 {
370 + return h.lowestEquivalentValue(v) + h.sizeOfEquivalentValueRange(v)
371 +}
372 +
373 +func (h *Histogram) highestEquivalentValue(v int64) int64 {
374 + return h.nextNonEquivalentValue(v) - 1
375 +}
376 +
377 +func (h *Histogram) medianEquivalentValue(v int64) int64 {
378 + return h.lowestEquivalentValue(v) + (h.sizeOfEquivalentValueRange(v) >> 1)
379 +}
380 +
381 +func (h *Histogram) getCountAtIndex(bucketIdx, subBucketIdx int32) int64 {
382 + return h.counts[h.countsIndex(bucketIdx, subBucketIdx)]
383 +}
384 +
385 +func (h *Histogram) countsIndex(bucketIdx, subBucketIdx int32) int32 {
386 + bucketBaseIdx := (bucketIdx + 1) << uint(h.subBucketHalfCountMagnitude)
387 + offsetInBucket := subBucketIdx - h.subBucketHalfCount
388 + return bucketBaseIdx + offsetInBucket
389 +}
390 +
391 +func (h *Histogram) getBucketIndex(v int64) int32 {
392 + pow2Ceiling := bitLen(v | h.subBucketMask)
393 + return int32(pow2Ceiling - int64(h.unitMagnitude) -
394 + int64(h.subBucketHalfCountMagnitude+1))
395 +}
396 +
397 +func (h *Histogram) getSubBucketIdx(v int64, idx int32) int32 {
398 + return int32(v >> uint(int64(idx)+int64(h.unitMagnitude)))
399 +}
400 +
401 +func (h *Histogram) countsIndexFor(v int64) int {
402 + bucketIdx := h.getBucketIndex(v)
403 + subBucketIdx := h.getSubBucketIdx(v, bucketIdx)
404 + return int(h.countsIndex(bucketIdx, subBucketIdx))
405 +}
406 +
407 +type iterator struct {
408 + h *Histogram
409 + bucketIdx, subBucketIdx int32
410 + countAtIdx, countToIdx, valueFromIdx int64
411 + highestEquivalentValue int64
412 +}
413 +
414 +func (i *iterator) next() bool {
415 + if i.countToIdx >= i.h.totalCount {
416 + return false
417 + }
418 +
419 + // increment bucket
420 + i.subBucketIdx++
421 + if i.subBucketIdx >= i.h.subBucketCount {
422 + i.subBucketIdx = i.h.subBucketHalfCount
423 + i.bucketIdx++
424 + }
425 +
426 + if i.bucketIdx >= i.h.bucketCount {
427 + return false
428 + }
429 +
430 + i.countAtIdx = i.h.getCountAtIndex(i.bucketIdx, i.subBucketIdx)
431 + i.countToIdx += i.countAtIdx
432 + i.valueFromIdx = i.h.valueFromIndex(i.bucketIdx, i.subBucketIdx)
433 + i.highestEquivalentValue = i.h.highestEquivalentValue(i.valueFromIdx)
434 +
435 + return true
436 +}
437 +
438 +type rIterator struct {
439 + iterator
440 + countAddedThisStep int64
441 +}
442 +
443 +func (r *rIterator) next() bool {
444 + for r.iterator.next() {
445 + if r.countAtIdx != 0 {
446 + r.countAddedThisStep = r.countAtIdx
447 + return true
448 + }
449 + }
450 + return false
451 +}
452 +
453 +type pIterator struct {
454 + iterator
455 + seenLastValue bool
456 + ticksPerHalfDistance int32
457 + percentileToIteratorTo float64
458 + percentile float64
459 +}
460 +
461 +func (p *pIterator) next() bool {
462 + if !(p.countToIdx < p.h.totalCount) {
463 + if p.seenLastValue {
464 + return false
465 + }
466 +
467 + p.seenLastValue = true
468 + p.percentile = 100
469 +
470 + return true
471 + }
472 +
473 + if p.subBucketIdx == -1 && !p.iterator.next() {
474 + return false
475 + }
476 +
477 + var done = false
478 + for !done {
479 + currentPercentile := (100.0 * float64(p.countToIdx)) / float64(p.h.totalCount)
480 + if p.countAtIdx != 0 && p.percentileToIteratorTo <= currentPercentile {
481 + p.percentile = p.percentileToIteratorTo
482 + halfDistance := math.Trunc(math.Pow(2, math.Trunc(math.Log2(100.0/(100.0-p.percentileToIteratorTo)))+1))
483 + percentileReportingTicks := float64(p.ticksPerHalfDistance) * halfDistance
484 + p.percentileToIteratorTo += 100.0 / percentileReportingTicks
485 + return true
486 + }
487 + done = !p.iterator.next()
488 + }
489 +
490 + return true
491 +}
492 +
493 +func bitLen(x int64) (n int64) {
494 + for ; x >= 0x8000; x >>= 16 {
495 + n += 16
496 + }
497 + if x >= 0x80 {
498 + x >>= 8
499 + n += 8
500 + }
501 + if x >= 0x8 {
502 + x >>= 4
503 + n += 4
504 + }
505 + if x >= 0x2 {
506 + x >>= 2
507 + n += 2
508 + }
509 + if x >= 0x1 {
510 + n++
511 + }
512 + return
513 +}
Godeps/_workspace/src/github.com/codahale/hdrhistogram/hdr_test.go new
+333
@@ -0,0 +1,333 @@
1 +package hdrhistogram_test
2 +
3 +import (
4 + "reflect"
5 + "testing"
6 +
7 + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/hdrhistogram"
8 +)
9 +
10 +func TestHighSigFig(t *testing.T) {
11 + input := []int64{
12 + 459876, 669187, 711612, 816326, 931423, 1033197, 1131895, 2477317,
13 + 3964974, 12718782,
14 + }
15 +
16 + hist := hdrhistogram.New(459876, 12718782, 5)
17 + for _, sample := range input {
18 + hist.RecordValue(sample)
19 + }
20 +
21 + if v, want := hist.ValueAtQuantile(50), int64(1048575); v != want {
22 + t.Errorf("Median was %v, but expected %v", v, want)
23 + }
24 +}
25 +
26 +func TestValueAtQuantile(t *testing.T) {
27 + h := hdrhistogram.New(1, 10000000, 3)
28 +
29 + for i := 0; i < 1000000; i++ {
30 + if err := h.RecordValue(int64(i)); err != nil {
31 + t.Fatal(err)
32 + }
33 + }
34 +
35 + data := []struct {
36 + q float64
37 + v int64
38 + }{
39 + {q: 50, v: 500223},
40 + {q: 75, v: 750079},
41 + {q: 90, v: 900095},
42 + {q: 95, v: 950271},
43 + {q: 99, v: 990207},
44 + {q: 99.9, v: 999423},
45 + {q: 99.99, v: 999935},
46 + }
47 +
48 + for _, d := range data {
49 + if v := h.ValueAtQuantile(d.q); v != d.v {
50 + t.Errorf("P%v was %v, but expected %v", d.q, v, d.v)
51 + }
52 + }
53 +}
54 +
55 +func TestMean(t *testing.T) {
56 + h := hdrhistogram.New(1, 10000000, 3)
57 +
58 + for i := 0; i < 1000000; i++ {
59 + if err := h.RecordValue(int64(i)); err != nil {
60 + t.Fatal(err)
61 + }
62 + }
63 +
64 + if v, want := h.Mean(), 500000.013312; v != want {
65 + t.Errorf("Mean was %v, but expected %v", v, want)
66 + }
67 +}
68 +
69 +func TestStdDev(t *testing.T) {
70 + h := hdrhistogram.New(1, 10000000, 3)
71 +
72 + for i := 0; i < 1000000; i++ {
73 + if err := h.RecordValue(int64(i)); err != nil {
74 + t.Fatal(err)
75 + }
76 + }
77 +
78 + if v, want := h.StdDev(), 288675.1403682715; v != want {
79 + t.Errorf("StdDev was %v, but expected %v", v, want)
80 + }
81 +}
82 +
83 +func TestTotalCount(t *testing.T) {
84 + h := hdrhistogram.New(1, 10000000, 3)
85 +
86 + for i := 0; i < 1000000; i++ {
87 + if err := h.RecordValue(int64(i)); err != nil {
88 + t.Fatal(err)
89 + }
90 + if v, want := h.TotalCount(), int64(i+1); v != want {
91 + t.Errorf("TotalCount was %v, but expected %v", v, want)
92 + }
93 + }
94 +}
95 +
96 +func TestMax(t *testing.T) {
97 + h := hdrhistogram.New(1, 10000000, 3)
98 +
99 + for i := 0; i < 1000000; i++ {
100 + if err := h.RecordValue(int64(i)); err != nil {
101 + t.Fatal(err)
102 + }
103 + }
104 +
105 + if v, want := h.Max(), int64(999936); v != want {
106 + t.Errorf("Max was %v, but expected %v", v, want)
107 + }
108 +}
109 +
110 +func TestReset(t *testing.T) {
111 + h := hdrhistogram.New(1, 10000000, 3)
112 +
113 + for i := 0; i < 1000000; i++ {
114 + if err := h.RecordValue(int64(i)); err != nil {
115 + t.Fatal(err)
116 + }
117 + }
118 +
119 + h.Reset()
120 +
121 + if v, want := h.Max(), int64(0); v != want {
122 + t.Errorf("Max was %v, but expected %v", v, want)
123 + }
124 +}
125 +
126 +func TestMerge(t *testing.T) {
127 + h1 := hdrhistogram.New(1, 1000, 3)
128 + h2 := hdrhistogram.New(1, 1000, 3)
129 +
130 + for i := 0; i < 100; i++ {
131 + if err := h1.RecordValue(int64(i)); err != nil {
132 + t.Fatal(err)
133 + }
134 + }
135 +
136 + for i := 100; i < 200; i++ {
137 + if err := h2.RecordValue(int64(i)); err != nil {
138 + t.Fatal(err)
139 + }
140 + }
141 +
142 + h1.Merge(h2)
143 +
144 + if v, want := h1.ValueAtQuantile(50), int64(99); v != want {
145 + t.Errorf("Median was %v, but expected %v", v, want)
146 + }
147 +}
148 +
149 +func TestMin(t *testing.T) {
150 + h := hdrhistogram.New(1, 10000000, 3)
151 +
152 + for i := 0; i < 1000000; i++ {
153 + if err := h.RecordValue(int64(i)); err != nil {
154 + t.Fatal(err)
155 + }
156 + }
157 +
158 + if v, want := h.Min(), int64(0); v != want {
159 + t.Errorf("Min was %v, but expected %v", v, want)
160 + }
161 +}
162 +
163 +func TestByteSize(t *testing.T) {
164 + h := hdrhistogram.New(1, 100000, 3)
165 +
166 + if v, want := h.ByteSize(), 65604; v != want {
167 + t.Errorf("ByteSize was %v, but expected %d", v, want)
168 + }
169 +}
170 +
171 +func TestRecordCorrectedValue(t *testing.T) {
172 + h := hdrhistogram.New(1, 100000, 3)
173 +
174 + if err := h.RecordCorrectedValue(10, 100); err != nil {
175 + t.Fatal(err)
176 + }
177 +
178 + if v, want := h.ValueAtQuantile(75), int64(10); v != want {
179 + t.Errorf("Corrected value was %v, but expected %v", v, want)
180 + }
181 +}
182 +
183 +func TestRecordCorrectedValueStall(t *testing.T) {
184 + h := hdrhistogram.New(1, 100000, 3)
185 +
186 + if err := h.RecordCorrectedValue(1000, 100); err != nil {
187 + t.Fatal(err)
188 + }
189 +
190 + if v, want := h.ValueAtQuantile(75), int64(800); v != want {
191 + t.Errorf("Corrected value was %v, but expected %v", v, want)
192 + }
193 +}
194 +
195 +func TestCumulativeDistribution(t *testing.T) {
196 + h := hdrhistogram.New(1, 100000000, 3)
197 +
198 + for i := 0; i < 1000000; i++ {
199 + if err := h.RecordValue(int64(i)); err != nil {
200 + t.Fatal(err)
201 + }
202 + }
203 +
204 + actual := h.CumulativeDistribution()
205 + expected := []hdrhistogram.Bracket{
206 + hdrhistogram.Bracket{Quantile: 0, Count: 1, ValueAt: 0},
207 + hdrhistogram.Bracket{Quantile: 50, Count: 500224, ValueAt: 500223},
208 + hdrhistogram.Bracket{Quantile: 75, Count: 750080, ValueAt: 750079},
209 + hdrhistogram.Bracket{Quantile: 87.5, Count: 875008, ValueAt: 875007},
210 + hdrhistogram.Bracket{Quantile: 93.75, Count: 937984, ValueAt: 937983},
211 + hdrhistogram.Bracket{Quantile: 96.875, Count: 969216, ValueAt: 969215},
212 + hdrhistogram.Bracket{Quantile: 98.4375, Count: 984576, ValueAt: 984575},
213 + hdrhistogram.Bracket{Quantile: 99.21875, Count: 992256, ValueAt: 992255},
214 + hdrhistogram.Bracket{Quantile: 99.609375, Count: 996352, ValueAt: 996351},
215 + hdrhistogram.Bracket{Quantile: 99.8046875, Count: 998400, ValueAt: 998399},
216 + hdrhistogram.Bracket{Quantile: 99.90234375, Count: 999424, ValueAt: 999423},
217 + hdrhistogram.Bracket{Quantile: 99.951171875, Count: 999936, ValueAt: 999935},
218 + hdrhistogram.Bracket{Quantile: 99.9755859375, Count: 999936, ValueAt: 999935},
219 + hdrhistogram.Bracket{Quantile: 99.98779296875, Count: 999936, ValueAt: 999935},
220 + hdrhistogram.Bracket{Quantile: 99.993896484375, Count: 1000000, ValueAt: 1000447},
221 + hdrhistogram.Bracket{Quantile: 100, Count: 1000000, ValueAt: 1000447},
222 + }
223 +
224 + if !reflect.DeepEqual(actual, expected) {
225 + t.Errorf("CF was %#v, but expected %#v", actual, expected)
226 + }
227 +}
228 +
229 +func BenchmarkHistogramRecordValue(b *testing.B) {
230 + h := hdrhistogram.New(1, 10000000, 3)
231 + for i := 0; i < 1000000; i++ {
232 + if err := h.RecordValue(int64(i)); err != nil {
233 + b.Fatal(err)
234 + }
235 + }
236 + b.ResetTimer()
237 + b.ReportAllocs()
238 +
239 + for i := 0; i < b.N; i++ {
240 + h.RecordValue(100)
241 + }
242 +}
243 +
244 +func BenchmarkNew(b *testing.B) {
245 + b.ReportAllocs()
246 +
247 + for i := 0; i < b.N; i++ {
248 + hdrhistogram.New(1, 120000, 3) // this could track 1ms-2min
249 + }
250 +}
251 +
252 +func TestUnitMagnitudeOverflow(t *testing.T) {
253 + h := hdrhistogram.New(0, 200, 4)
254 + if err := h.RecordValue(11); err != nil {
255 + t.Fatal(err)
256 + }
257 +}
258 +
259 +func TestSubBucketMaskOverflow(t *testing.T) {
260 + hist := hdrhistogram.New(2e7, 1e8, 5)
261 + for _, sample := range [...]int64{1e8, 2e7, 3e7} {
262 + hist.RecordValue(sample)
263 + }
264 +
265 + for q, want := range map[float64]int64{
266 + 50: 33554431,
267 + 83.33: 33554431,
268 + 83.34: 100663295,
269 + 99: 100663295,
270 + } {
271 + if got := hist.ValueAtQuantile(q); got != want {
272 + t.Errorf("got %d for %fth percentile. want: %d", got, q, want)
273 + }
274 + }
275 +}
276 +
277 +func TestExportImport(t *testing.T) {
278 + min := int64(1)
279 + max := int64(10000000)
280 + sigfigs := 3
281 + h := hdrhistogram.New(min, max, sigfigs)
282 + for i := 0; i < 1000000; i++ {
283 + if err := h.RecordValue(int64(i)); err != nil {
284 + t.Fatal(err)
285 + }
286 + }
287 +
288 + s := h.Export()
289 +
290 + if v := s.LowestTrackableValue; v != min {
291 + t.Errorf("LowestTrackableValue was %v, but expected %v", v, min)
292 + }
293 +
294 + if v := s.HighestTrackableValue; v != max {
295 + t.Errorf("HighestTrackableValue was %v, but expected %v", v, max)
296 + }
297 +
298 + if v := int(s.SignificantFigures); v != sigfigs {
299 + t.Errorf("SignificantFigures was %v, but expected %v", v, sigfigs)
300 + }
301 +
302 + if imported := hdrhistogram.Import(s); !imported.Equals(h) {
303 + t.Error("Expected Histograms to be equivalent")
304 + }
305 +
306 +}
307 +
308 +func TestEquals(t *testing.T) {
309 + h1 := hdrhistogram.New(1, 10000000, 3)
310 + for i := 0; i < 1000000; i++ {
311 + if err := h1.RecordValue(int64(i)); err != nil {
312 + t.Fatal(err)
313 + }
314 + }
315 +
316 + h2 := hdrhistogram.New(1, 10000000, 3)
317 + for i := 0; i < 10000; i++ {
318 + if err := h1.RecordValue(int64(i)); err != nil {
319 + t.Fatal(err)
320 + }
321 + }
322 +
323 + if h1.Equals(h2) {
324 + t.Error("Expected Histograms to not be equivalent")
325 + }
326 +
327 + h1.Reset()
328 + h2.Reset()
329 +
330 + if !h1.Equals(h2) {
331 + t.Error("Expected Histograms to be equivalent")
332 + }
333 +}
Godeps/_workspace/src/github.com/codahale/hdrhistogram/window.go new
+45
@@ -0,0 +1,45 @@
1 +package hdrhistogram
2 +
3 +// A WindowedHistogram combines histograms to provide windowed statistics.
4 +type WindowedHistogram struct {
5 + idx int
6 + h []Histogram
7 + m *Histogram
8 +
9 + Current *Histogram
10 +}
11 +
12 +// NewWindowed creates a new WindowedHistogram with N underlying histograms with
13 +// the given parameters.
14 +func NewWindowed(n int, minValue, maxValue int64, sigfigs int) *WindowedHistogram {
15 + w := WindowedHistogram{
16 + idx: -1,
17 + h: make([]Histogram, n),
18 + m: New(minValue, maxValue, sigfigs),
19 + }
20 +
21 + for i := range w.h {
22 + w.h[i] = *New(minValue, maxValue, sigfigs)
23 + }
24 + w.Rotate()
25 +
26 + return &w
27 +}
28 +
29 +// Merge returns a histogram which includes the recorded values from all the
30 +// sections of the window.
31 +func (w *WindowedHistogram) Merge() *Histogram {
32 + w.m.Reset()
33 + for _, h := range w.h {
34 + w.m.Merge(&h)
35 + }
36 + return w.m
37 +}
38 +
39 +// Rotate resets the oldest histogram and rotates it to be used as the current
40 +// histogram.
41 +func (w *WindowedHistogram) Rotate() {
42 + w.idx++
43 + w.Current = &w.h[w.idx%len(w.h)]
44 + w.Current.Reset()
45 +}
Godeps/_workspace/src/github.com/codahale/hdrhistogram/window_test.go new
+64
@@ -0,0 +1,64 @@
1 +package hdrhistogram_test
2 +
3 +import (
4 + "testing"
5 +
6 + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/hdrhistogram"
7 +)
8 +
9 +func TestWindowedHistogram(t *testing.T) {
10 + w := hdrhistogram.NewWindowed(2, 1, 1000, 3)
11 +
12 + for i := 0; i < 100; i++ {
13 + w.Current.RecordValue(int64(i))
14 + }
15 + w.Rotate()
16 +
17 + for i := 100; i < 200; i++ {
18 + w.Current.RecordValue(int64(i))
19 + }
20 + w.Rotate()
21 +
22 + for i := 200; i < 300; i++ {
23 + w.Current.RecordValue(int64(i))
24 + }
25 +
26 + if v, want := w.Merge().ValueAtQuantile(50), int64(199); v != want {
27 + t.Errorf("Median was %v, but expected %v", v, want)
28 + }
29 +}
30 +
31 +func BenchmarkWindowedHistogramRecordAndRotate(b *testing.B) {
32 + w := hdrhistogram.NewWindowed(3, 1, 10000000, 3)
33 + b.ReportAllocs()
34 + b.ResetTimer()
35 +
36 + for i := 0; i < b.N; i++ {
37 + if err := w.Current.RecordValue(100); err != nil {
38 + b.Fatal(err)
39 + }
40 +
41 + if i%100000 == 1 {
42 + w.Rotate()
43 + }
44 + }
45 +}
46 +
47 +func BenchmarkWindowedHistogramMerge(b *testing.B) {
48 + w := hdrhistogram.NewWindowed(3, 1, 10000000, 3)
49 + for i := 0; i < 10000000; i++ {
50 + if err := w.Current.RecordValue(100); err != nil {
51 + b.Fatal(err)
52 + }
53 +
54 + if i%100000 == 1 {
55 + w.Rotate()
56 + }
57 + }
58 + b.ReportAllocs()
59 + b.ResetTimer()
60 +
61 + for i := 0; i < b.N; i++ {
62 + w.Merge()
63 + }
64 +}
Godeps/_workspace/src/github.com/codahale/metrics/.travis.yml new
+9
@@ -0,0 +1,9 @@
1 +language: go
2 +go:
3 + - 1.3.3
4 +notifications:
5 + # See http://about.travis-ci.org/docs/user/build-configuration/ to learn more
6 + # about configuring notification recipients and more.
7 + email:
8 + recipients:
9 + - coda.hale@gmail.com
Godeps/_workspace/src/github.com/codahale/metrics/LICENSE new
+21
@@ -0,0 +1,21 @@
1 +The MIT License (MIT)
2 +
3 +Copyright (c) 2014 Coda Hale
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in
13 +all copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 +THE SOFTWARE.
Godeps/_workspace/src/github.com/codahale/metrics/README.md new
+8
@@ -0,0 +1,8 @@
1 +metrics
2 +=======
3 +
4 +[![Build Status](https://travis-ci.org/codahale/metrics.png?branch=master)](https://travis-ci.org/codahale/metrics)
5 +
6 +A Go library which provides light-weight instrumentation for your application.
7 +
8 +For documentation, check [godoc](http://godoc.org/github.com/codahale/metrics).
Godeps/_workspace/src/github.com/codahale/metrics/metrics.go new
+329
@@ -0,0 +1,329 @@
1 +// Package metrics provides minimalist instrumentation for your applications in
2 +// the form of counters and gauges.
3 +//
4 +// Counters
5 +//
6 +// A counter is a monotonically-increasing, unsigned, 64-bit integer used to
7 +// represent the number of times an event has occurred. By tracking the deltas
8 +// between measurements of a counter over intervals of time, an aggregation
9 +// layer can derive rates, acceleration, etc.
10 +//
11 +// Gauges
12 +//
13 +// A gauge returns instantaneous measurements of something using signed, 64-bit
14 +// integers. This value does not need to be monotonic.
15 +//
16 +// Histograms
17 +//
18 +// A histogram tracks the distribution of a stream of values (e.g. the number of
19 +// milliseconds it takes to handle requests), adding gauges for the values at
20 +// meaningful quantiles: 50th, 75th, 90th, 95th, 99th, 99.9th.
21 +//
22 +// Reporting
23 +//
24 +// Measurements from counters and gauges are available as expvars. Your service
25 +// should return its expvars from an HTTP endpoint (i.e., /debug/vars) as a JSON
26 +// object.
27 +package metrics
28 +
29 +import (
30 + "expvar"
31 + "sync"
32 + "time"
33 +
34 + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/hdrhistogram"
35 +)
36 +
37 +// A Counter is a monotonically increasing unsigned integer.
38 +//
39 +// Use a counter to derive rates (e.g., record total number of requests, derive
40 +// requests per second).
41 +type Counter string
42 +
43 +// Add increments the counter by one.
44 +func (c Counter) Add() {
45 + c.AddN(1)
46 +}
47 +
48 +// AddN increments the counter by N.
49 +func (c Counter) AddN(delta uint64) {
50 + cm.Lock()
51 + counters[string(c)] += delta
52 + cm.Unlock()
53 +}
54 +
55 +// SetFunc sets the counter's value to the lazily-called return value of the
56 +// given function.
57 +func (c Counter) SetFunc(f func() uint64) {
58 + cm.Lock()
59 + defer cm.Unlock()
60 +
61 + counterFuncs[string(c)] = f
62 +}
63 +
64 +// SetBatchFunc sets the counter's value to the lazily-called return value of
65 +// the given function, with an additional initializer function for a related
66 +// batch of counters, all of which are keyed by an arbitrary value.
67 +func (c Counter) SetBatchFunc(key interface{}, init func(), f func() uint64) {
68 + cm.Lock()
69 + defer cm.Unlock()
70 +
71 + gm.Lock()
72 + defer gm.Unlock()
73 +
74 + counterFuncs[string(c)] = f
75 + if _, ok := inits[key]; !ok {
76 + inits[key] = init
77 + }
78 +}
79 +
80 +// Remove removes the given counter.
81 +func (c Counter) Remove() {
82 + cm.Lock()
83 + defer cm.Unlock()
84 +
85 + gm.Lock()
86 + defer gm.Unlock()
87 +
88 + delete(counters, string(c))
89 + delete(counterFuncs, string(c))
90 + delete(inits, string(c))
91 +}
92 +
93 +// A Gauge is an instantaneous measurement of a value.
94 +//
95 +// Use a gauge to track metrics which increase and decrease (e.g., amount of
96 +// free memory).
97 +type Gauge string
98 +
99 +// Set the gauge's value to the given value.
100 +func (g Gauge) Set(value int64) {
101 + gm.Lock()
102 + defer gm.Unlock()
103 +
104 + gauges[string(g)] = func() int64 {
105 + return value
106 + }
107 +}
108 +
109 +// SetFunc sets the gauge's value to the lazily-called return value of the given
110 +// function.
111 +func (g Gauge) SetFunc(f func() int64) {
112 + gm.Lock()
113 + defer gm.Unlock()
114 +
115 + gauges[string(g)] = f
116 +}
117 +
118 +// SetBatchFunc sets the gauge's value to the lazily-called return value of the
119 +// given function, with an additional initializer function for a related batch
120 +// of gauges, all of which are keyed by an arbitrary value.
121 +func (g Gauge) SetBatchFunc(key interface{}, init func(), f func() int64) {
122 + gm.Lock()
123 + defer gm.Unlock()
124 +
125 + gauges[string(g)] = f
126 + if _, ok := inits[key]; !ok {
127 + inits[key] = init
128 + }
129 +}
130 +
131 +// Remove removes the given gauge.
132 +func (g Gauge) Remove() {
133 + gm.Lock()
134 + defer gm.Unlock()
135 +
136 + delete(gauges, string(g))
137 + delete(inits, string(g))
138 +}
139 +
140 +// Reset removes all existing counters and gauges.
141 +func Reset() {
142 + cm.Lock()
143 + defer cm.Unlock()
144 +
145 + gm.Lock()
146 + defer gm.Unlock()
147 +
148 + hm.Lock()
149 + defer hm.Unlock()
150 +
151 + counters = make(map[string]uint64)
152 + counterFuncs = make(map[string]func() uint64)
153 + gauges = make(map[string]func() int64)
154 + histograms = make(map[string]*Histogram)
155 + inits = make(map[interface{}]func())
156 +}
157 +
158 +// Snapshot returns a copy of the values of all registered counters and gauges.
159 +func Snapshot() (c map[string]uint64, g map[string]int64) {
160 + cm.Lock()
161 + defer cm.Unlock()
162 +
163 + gm.Lock()
164 + defer gm.Unlock()
165 +
166 + hm.Lock()
167 + defer hm.Unlock()
168 +
169 + for _, init := range inits {
170 + init()
171 + }
172 +
173 + c = make(map[string]uint64, len(counters)+len(counterFuncs))
174 + for n, v := range counters {
175 + c[n] = v
176 + }
177 +
178 + for n, f := range counterFuncs {
179 + c[n] = f()
180 + }
181 +
182 + g = make(map[string]int64, len(gauges))
183 + for n, f := range gauges {
184 + g[n] = f()
185 + }
186 +
187 + return
188 +}
189 +
190 +// NewHistogram returns a windowed HDR histogram which drops data older than
191 +// five minutes. The returned histogram is safe to use from multiple goroutines.
192 +//
193 +// Use a histogram to track the distribution of a stream of values (e.g., the
194 +// latency associated with HTTP requests).
195 +func NewHistogram(name string, minValue, maxValue int64, sigfigs int) *Histogram {
196 + hm.Lock()
197 + defer hm.Unlock()
198 +
199 + if _, ok := histograms[name]; ok {
200 + panic(name + " already exists")
201 + }
202 +
203 + hist := &Histogram{
204 + name: name,
205 + hist: hdrhistogram.NewWindowed(5, minValue, maxValue, sigfigs),
206 + }
207 + histograms[name] = hist
208 +
209 + Gauge(name+".P50").SetBatchFunc(hname(name), hist.merge, hist.valueAt(50))
210 + Gauge(name+".P75").SetBatchFunc(hname(name), hist.merge, hist.valueAt(75))
211 + Gauge(name+".P90").SetBatchFunc(hname(name), hist.merge, hist.valueAt(90))
212 + Gauge(name+".P95").SetBatchFunc(hname(name), hist.merge, hist.valueAt(95))
213 + Gauge(name+".P99").SetBatchFunc(hname(name), hist.merge, hist.valueAt(99))
214 + Gauge(name+".P999").SetBatchFunc(hname(name), hist.merge, hist.valueAt(99.9))
215 +
216 + return hist
217 +}
218 +
219 +// Remove removes the given histogram.
220 +func (h *Histogram) Remove() {
221 +
222 + hm.Lock()
223 + defer hm.Unlock()
224 +
225 + Gauge(h.name + ".P50").Remove()
226 + Gauge(h.name + ".P75").Remove()
227 + Gauge(h.name + ".P90").Remove()
228 + Gauge(h.name + ".P95").Remove()
229 + Gauge(h.name + ".P99").Remove()
230 + Gauge(h.name + ".P999").Remove()
231 +
232 + delete(histograms, h.name)
233 +}
234 +
235 +type hname string // unexported to prevent collisions
236 +
237 +// A Histogram measures the distribution of a stream of values.
238 +type Histogram struct {
239 + name string
240 + hist *hdrhistogram.WindowedHistogram
241 + m *hdrhistogram.Histogram
242 + rw sync.RWMutex
243 +}
244 +
245 +// Name returns the name of the histogram
246 +func (h *Histogram) Name() string {
247 + return h.name
248 +}
249 +
250 +// RecordValue records the given value, or returns an error if the value is out
251 +// of range.
252 +// Returned error values are of type Error.
253 +func (h *Histogram) RecordValue(v int64) error {
254 + h.rw.Lock()
255 + defer h.rw.Unlock()
256 +
257 + err := h.hist.Current.RecordValue(v)
258 + if err != nil {
259 + return Error{h.name, err}
260 + }
261 + return nil
262 +}
263 +
264 +func (h *Histogram) rotate() {
265 + h.rw.Lock()
266 + defer h.rw.Unlock()
267 +
268 + h.hist.Rotate()
269 +}
270 +
271 +func (h *Histogram) merge() {
272 + h.rw.Lock()
273 + defer h.rw.Unlock()
274 +
275 + h.m = h.hist.Merge()
276 +}
277 +
278 +func (h *Histogram) valueAt(q float64) func() int64 {
279 + return func() int64 {
280 + h.rw.RLock()
281 + defer h.rw.RUnlock()
282 +
283 + if h.m == nil {
284 + return 0
285 + }
286 +
287 + return h.m.ValueAtQuantile(q)
288 + }
289 +}
290 +
291 +// Error describes an error and the name of the metric where it occurred.
292 +type Error struct {
293 + Metric string
294 + Err error
295 +}
296 +
297 +func (e Error) Error() string {
298 + return e.Metric + ": " + e.Err.Error()
299 +}
300 +
301 +var (
302 + counters = make(map[string]uint64)
303 + counterFuncs = make(map[string]func() uint64)
304 + gauges = make(map[string]func() int64)
305 + inits = make(map[interface{}]func())
306 + histograms = make(map[string]*Histogram)
307 +
308 + cm, gm, hm sync.Mutex
309 +)
310 +
311 +func init() {
312 + expvar.Publish("metrics", expvar.Func(func() interface{} {
313 + counters, gauges := Snapshot()
314 + return map[string]interface{}{
315 + "Counters": counters,
316 + "Gauges": gauges,
317 + }
318 + }))
319 +
320 + go func() {
321 + for _ = range time.NewTicker(1 * time.Minute).C {
322 + hm.Lock()
323 + for _, h := range histograms {
324 + h.rotate()
325 + }
326 + hm.Unlock()
327 + }
328 + }()
329 +}
Godeps/_workspace/src/github.com/codahale/metrics/metrics_test.go new
+217
@@ -0,0 +1,217 @@
1 +package metrics_test
2 +
3 +import (
4 + "testing"
5 +
6 + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
7 +)
8 +
9 +func TestCounter(t *testing.T) {
10 + metrics.Reset()
11 +
12 + metrics.Counter("whee").Add()
13 + metrics.Counter("whee").AddN(10)
14 +
15 + counters, _ := metrics.Snapshot()
16 + if v, want := counters["whee"], uint64(11); v != want {
17 + t.Errorf("Counter was %v, but expected %v", v, want)
18 + }
19 +}
20 +
21 +func TestCounterFunc(t *testing.T) {
22 + metrics.Reset()
23 +
24 + metrics.Counter("whee").SetFunc(func() uint64 {
25 + return 100
26 + })
27 +
28 + counters, _ := metrics.Snapshot()
29 + if v, want := counters["whee"], uint64(100); v != want {
30 + t.Errorf("Counter was %v, but expected %v", v, want)
31 + }
32 +}
33 +
34 +func TestCounterBatchFunc(t *testing.T) {
35 + metrics.Reset()
36 +
37 + var a, b uint64
38 +
39 + metrics.Counter("whee").SetBatchFunc(
40 + "yay",
41 + func() {
42 + a, b = 1, 2
43 + },
44 + func() uint64 {
45 + return a
46 + },
47 + )
48 +
49 + metrics.Counter("woo").SetBatchFunc(
50 + "yay",
51 + func() {
52 + a, b = 1, 2
53 + },
54 + func() uint64 {
55 + return b
56 + },
57 + )
58 +
59 + counters, _ := metrics.Snapshot()
60 + if v, want := counters["whee"], uint64(1); v != want {
61 + t.Errorf("Counter was %v, but expected %v", v, want)
62 + }
63 +
64 + if v, want := counters["woo"], uint64(2); v != want {
65 + t.Errorf("Counter was %v, but expected %v", v, want)
66 + }
67 +}
68 +
69 +func TestCounterRemove(t *testing.T) {
70 + metrics.Reset()
71 +
72 + metrics.Counter("whee").Add()
73 + metrics.Counter("whee").Remove()
74 +
75 + counters, _ := metrics.Snapshot()
76 + if v, ok := counters["whee"]; ok {
77 + t.Errorf("Counter was %v, but expected nothing", v)
78 + }
79 +}
80 +
81 +func TestGaugeValue(t *testing.T) {
82 + metrics.Reset()
83 +
84 + metrics.Gauge("whee").Set(-100)
85 +
86 + _, gauges := metrics.Snapshot()
87 + if v, want := gauges["whee"], int64(-100); v != want {
88 + t.Errorf("Gauge was %v, but expected %v", v, want)
89 + }
90 +}
91 +
92 +func TestGaugeFunc(t *testing.T) {
93 + metrics.Reset()
94 +
95 + metrics.Gauge("whee").SetFunc(func() int64 {
96 + return -100
97 + })
98 +
99 + _, gauges := metrics.Snapshot()
100 + if v, want := gauges["whee"], int64(-100); v != want {
101 + t.Errorf("Gauge was %v, but expected %v", v, want)
102 + }
103 +}
104 +
105 +func TestGaugeRemove(t *testing.T) {
106 + metrics.Reset()
107 +
108 + metrics.Gauge("whee").Set(1)
109 + metrics.Gauge("whee").Remove()
110 +
111 + _, gauges := metrics.Snapshot()
112 + if v, ok := gauges["whee"]; ok {
113 + t.Errorf("Gauge was %v, but expected nothing", v)
114 + }
115 +}
116 +
117 +func TestHistogram(t *testing.T) {
118 + metrics.Reset()
119 +
120 + h := metrics.NewHistogram("heyo", 1, 1000, 3)
121 + for i := 100; i > 0; i-- {
122 + for j := 0; j < i; j++ {
123 + h.RecordValue(int64(i))
124 + }
125 + }
126 +
127 + _, gauges := metrics.Snapshot()
128 +
129 + if v, want := gauges["heyo.P50"], int64(71); v != want {
130 + t.Errorf("P50 was %v, but expected %v", v, want)
131 + }
132 +
133 + if v, want := gauges["heyo.P75"], int64(87); v != want {
134 + t.Errorf("P75 was %v, but expected %v", v, want)
135 + }
136 +
137 + if v, want := gauges["heyo.P90"], int64(95); v != want {
138 + t.Errorf("P90 was %v, but expected %v", v, want)
139 + }
140 +
141 + if v, want := gauges["heyo.P95"], int64(98); v != want {
142 + t.Errorf("P95 was %v, but expected %v", v, want)
143 + }
144 +
145 + if v, want := gauges["heyo.P99"], int64(100); v != want {
146 + t.Errorf("P99 was %v, but expected %v", v, want)
147 + }
148 +
149 + if v, want := gauges["heyo.P999"], int64(100); v != want {
150 + t.Errorf("P999 was %v, but expected %v", v, want)
151 + }
152 +}
153 +
154 +func TestHistogramRemove(t *testing.T) {
155 + metrics.Reset()
156 +
157 + h := metrics.NewHistogram("heyo", 1, 1000, 3)
158 + h.Remove()
159 +
160 + _, gauges := metrics.Snapshot()
161 + if v, ok := gauges["heyo.P50"]; ok {
162 + t.Errorf("Gauge was %v, but expected nothing", v)
163 + }
164 +}
165 +
166 +func BenchmarkCounterAdd(b *testing.B) {
167 + metrics.Reset()
168 +
169 + b.ReportAllocs()
170 + b.ResetTimer()
171 +
172 + b.RunParallel(func(pb *testing.PB) {
173 + for pb.Next() {
174 + metrics.Counter("test1").Add()
175 + }
176 + })
177 +}
178 +
179 +func BenchmarkCounterAddN(b *testing.B) {
180 + metrics.Reset()
181 +
182 + b.ReportAllocs()
183 + b.ResetTimer()
184 +
185 + b.RunParallel(func(pb *testing.PB) {
186 + for pb.Next() {
187 + metrics.Counter("test2").AddN(100)
188 + }
189 + })
190 +}
191 +
192 +func BenchmarkGaugeSet(b *testing.B) {
193 + metrics.Reset()
194 +
195 + b.ReportAllocs()
196 + b.ResetTimer()
197 +
198 + b.RunParallel(func(pb *testing.PB) {
199 + for pb.Next() {
200 + metrics.Gauge("test2").Set(100)
201 + }
202 + })
203 +}
204 +
205 +func BenchmarkHistogramRecordValue(b *testing.B) {
206 + metrics.Reset()
207 + h := metrics.NewHistogram("hist", 1, 1000, 3)
208 +
209 + b.ReportAllocs()
210 + b.ResetTimer()
211 +
212 + b.RunParallel(func(pb *testing.PB) {
213 + for pb.Next() {
214 + h.RecordValue(100)
215 + }
216 + })
217 +}
Godeps/_workspace/src/github.com/codahale/metrics/runtime/doc.go new
+18
@@ -0,0 +1,18 @@
1 +// Package runtime registers gauges and counters for various operationally
2 +// important aspects of the Go runtime.
3 +//
4 +// To use, import this package:
5 +//
6 +// import _ "github.com/codahale/metrics/runtime"
7 +//
8 +// This registers the following gauges:
9 +//
10 +// FileDescriptors.Max
11 +// FileDescriptors.Used
12 +// Mem.NumGC
13 +// Mem.PauseTotalNs
14 +// Mem.LastGC
15 +// Mem.Alloc
16 +// Mem.HeapObjects
17 +// Goroutines.Num
18 +package runtime
Godeps/_workspace/src/github.com/codahale/metrics/runtime/fds.go new
+44
@@ -0,0 +1,44 @@
1 +// +build !windows
2 +
3 +package runtime
4 +
5 +import (
6 + "io/ioutil"
7 + "syscall"
8 +
9 + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
10 +)
11 +
12 +func getFDLimit() (uint64, error) {
13 + var rlimit syscall.Rlimit
14 + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil {
15 + return 0, err
16 + }
17 + return rlimit.Cur, nil
18 +}
19 +
20 +func getFDUsage() (uint64, error) {
21 + fds, err := ioutil.ReadDir("/proc/self/fd")
22 + if err != nil {
23 + return 0, err
24 + }
25 + return uint64(len(fds)), nil
26 +}
27 +
28 +func init() {
29 + metrics.Gauge("FileDescriptors.Max").SetFunc(func() int64 {
30 + v, err := getFDLimit()
31 + if err != nil {
32 + return 0
33 + }
34 + return int64(v)
35 + })
36 +
37 + metrics.Gauge("FileDescriptors.Used").SetFunc(func() int64 {
38 + v, err := getFDUsage()
39 + if err != nil {
40 + return 0
41 + }
42 + return int64(v)
43 + })
44 +}
Godeps/_workspace/src/github.com/codahale/metrics/runtime/fds_test.go new
+24
@@ -0,0 +1,24 @@
1 +// +build !windows
2 +
3 +package runtime
4 +
5 +import (
6 + "testing"
7 +
8 + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
9 +)
10 +
11 +func TestFdStats(t *testing.T) {
12 + _, gauges := metrics.Snapshot()
13 +
14 + expected := []string{
15 + "FileDescriptors.Max",
16 + "FileDescriptors.Used",
17 + }
18 +
19 + for _, name := range expected {
20 + if _, ok := gauges[name]; !ok {
21 + t.Errorf("Missing gauge %q", name)
22 + }
23 + }
24 +}
Godeps/_workspace/src/github.com/codahale/metrics/runtime/fds_windows.go new
+4
@@ -0,0 +1,4 @@
1 +package runtime
2 +
3 +func init() {
4 +}
Godeps/_workspace/src/github.com/codahale/metrics/runtime/goroutines.go new
+13
@@ -0,0 +1,13 @@
1 +package runtime
2 +
3 +import (
4 + "runtime"
5 +
6 + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
7 +)
8 +
9 +func init() {
10 + metrics.Gauge("Goroutines.Num").SetFunc(func() int64 {
11 + return int64(runtime.NumGoroutine())
12 + })
13 +}
Godeps/_workspace/src/github.com/codahale/metrics/runtime/goroutines_test.go new
+21
@@ -0,0 +1,21 @@
1 +package runtime
2 +
3 +import (
4 + "testing"
5 +
6 + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
7 +)
8 +
9 +func TestGoroutinesStats(t *testing.T) {
10 + _, gauges := metrics.Snapshot()
11 +
12 + expected := []string{
13 + "Goroutines.Num",
14 + }
15 +
16 + for _, name := range expected {
17 + if _, ok := gauges[name]; !ok {
18 + t.Errorf("Missing gauge %q", name)
19 + }
20 + }
21 +}
Godeps/_workspace/src/github.com/codahale/metrics/runtime/memstats.go new
+48
@@ -0,0 +1,48 @@
1 +package runtime
2 +
3 +import (
4 + "runtime"
5 +
6 + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
7 +)
8 +
9 +func init() {
10 + msg := &memStatGauges{}
11 +
12 + metrics.Counter("Mem.NumGC").SetBatchFunc(key{}, msg.init, msg.numGC)
13 + metrics.Counter("Mem.PauseTotalNs").SetBatchFunc(key{}, msg.init, msg.totalPause)
14 +
15 + metrics.Gauge("Mem.LastGC").SetBatchFunc(key{}, msg.init, msg.lastPause)
16 + metrics.Gauge("Mem.Alloc").SetBatchFunc(key{}, msg.init, msg.alloc)
17 + metrics.Gauge("Mem.HeapObjects").SetBatchFunc(key{}, msg.init, msg.objects)
18 +}
19 +
20 +type key struct{} // unexported to prevent collision
21 +
22 +type memStatGauges struct {
23 + stats runtime.MemStats
24 +}
25 +
26 +func (msg *memStatGauges) init() {
27 + runtime.ReadMemStats(&msg.stats)
28 +}
29 +
30 +func (msg *memStatGauges) numGC() uint64 {
31 + return uint64(msg.stats.NumGC)
32 +}
33 +
34 +func (msg *memStatGauges) totalPause() uint64 {
35 + return msg.stats.PauseTotalNs
36 +}
37 +
38 +func (msg *memStatGauges) lastPause() int64 {
39 + return int64(msg.stats.LastGC)
40 +}
41 +
42 +func (msg *memStatGauges) alloc() int64 {
43 + return int64(msg.stats.Alloc)
44 +}
45 +
46 +func (msg *memStatGauges) objects() int64 {
47 + return int64(msg.stats.HeapObjects)
48 +}
Godeps/_workspace/src/github.com/codahale/metrics/runtime/memstats_test.go new
+34
@@ -0,0 +1,34 @@
1 +package runtime
2 +
3 +import (
4 + "testing"
5 +
6 + "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/codahale/metrics"
7 +)
8 +
9 +func TestMemStats(t *testing.T) {
10 + counters, gauges := metrics.Snapshot()
11 +
12 + expectedCounters := []string{
13 + "Mem.NumGC",
14 + "Mem.PauseTotalNs",
15 + }
16 +
17 + expectedGauges := []string{
18 + "Mem.LastGC",
19 + "Mem.Alloc",
20 + "Mem.HeapObjects",
21 + }
22 +
23 + for _, name := range expectedCounters {
24 + if _, ok := counters[name]; !ok {
25 + t.Errorf("Missing counters %q", name)
26 + }
27 + }
28 +
29 + for _, name := range expectedGauges {
30 + if _, ok := gauges[name]; !ok {
31 + t.Errorf("Missing gauge %q", name)
32 + }
33 + }
34 +}