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
+[](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
+[](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
+}