Skip to content

Commit

Permalink
Add a Histogram method, and tests, for #40.
Browse files Browse the repository at this point in the history
  • Loading branch information
kurin committed Feb 28, 2018
1 parent 69ab10f commit 2062ab7
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 8 deletions.
52 changes: 44 additions & 8 deletions b2/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,21 @@ type StatusInfo struct {
MethodInfo *MethodInfo
}

// numBuckets is the number of buckets per histogram, corresponding to 0-1ms,
// numBins is the number of buckets per histogram, corresponding to 0-1ms,
// 1-3ms, 3-7ms, etc. Each bucket index i is 2^i ms wide, except of course the
// last one.
const numBuckets = 30
// last one. At 22 bins, the maximum finite bin width (the 21st, i=20) is
// about 17m.
const numBins = 22

func getBucket(d time.Duration) int {
func getBin(d time.Duration) int {
i := int(math.Log2(1 + float64(d/time.Millisecond)))
if i > numBuckets {
i = numBuckets
if i >= numBins {
i = numBins - 1
}
return i
}

type hist [numBuckets]int
type hist [numBins]int

func (h hist) add(o hist) hist {
var r hist
Expand Down Expand Up @@ -95,13 +96,14 @@ func (mi *MethodInfo) addCall(method string, d time.Duration, code int) {
defer mi.mu.Unlock()

mi.ensure(method)
mi.data[method][code] = mi.data[method][code].inc(getBucket(d))
mi.data[method][code] = mi.data[method][code].inc(getBin(d))
}

// Count returns the total number of method calls.
func (mi *MethodInfo) Count() int {
mi.mu.Lock()
defer mi.mu.Unlock()

var t int
for _, codes := range mi.data {
for _, h := range codes {
Expand All @@ -117,6 +119,7 @@ func (mi *MethodInfo) Count() int {
func (mi *MethodInfo) CountByMethod() map[string]int {
mi.mu.Lock()
defer mi.mu.Unlock()

t := make(map[string]int)
for method, codes := range mi.data {
for _, h := range codes {
Expand All @@ -128,6 +131,39 @@ func (mi *MethodInfo) CountByMethod() map[string]int {
return t
}

// CountByCode returns the number of calls per status code.
func (mi *MethodInfo) CountByCode() map[int]int {
mi.mu.Lock()
defer mi.mu.Unlock()

t := make(map[int]int)
for _, codes := range mi.data {
for code, h := range codes {
for i := range h {
t[code] += h[i]
}
}
}
return t
}

// Histogram returns a slice of bins for call latencies. The bin contains the
// number of requests that finished within 1ms, and the second 1-3ms, the third
// 3-7ms, etc. The width of the bin in index i is 2^i milliseconds wide, and
// the minimum time is (2^i)-1 milliseconds.
func (mi *MethodInfo) Histogram() []int {
mi.mu.Lock()
defer mi.mu.Unlock()

var r hist
for _, codes := range mi.data {
for _, h := range codes {
r = r.add(h)
}
}
return []int(r[:])
}

// WriterStatus reports the status for each writer.
type WriterStatus struct {
// Progress is a slice of completion ratios. The index of a ratio is its
Expand Down
77 changes: 77 additions & 0 deletions b2/monitor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2018, Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package b2

import (
"fmt"
"reflect"
"testing"
"time"
)

func TestHistogram(t *testing.T) {
table := []struct {
ds []time.Duration
want []int
}{
{
ds: []time.Duration{
1 * time.Microsecond,
1001 * time.Microsecond,
3001 * time.Microsecond,
7001 * time.Microsecond,
15001 * time.Microsecond,
},
want: []int{
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
},
},
{
ds: []time.Duration{
0,
1 * time.Microsecond,
999 * time.Microsecond,
1000 * time.Microsecond,
1001 * time.Microsecond,
2999 * time.Microsecond,
3000 * time.Microsecond,
3001 * time.Microsecond,
6999 * time.Microsecond,
7000 * time.Microsecond,
7001 * time.Microsecond,
14999 * time.Microsecond,
15000 * time.Microsecond,
15001 * time.Microsecond,
time.Hour,
},
want: []int{
3, 3, 3, 3, 2, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
},
},
}

for _, e := range table {
mi := &MethodInfo{}
for i, d := range e.ds {
mi.addCall(fmt.Sprintf("%d", i%3), d, i%4)
}
got := mi.Histogram()
if !reflect.DeepEqual(got, e.want) {
t.Errorf("Histogram(%v): got %v, want %v", e.ds, got, e.want)
}
}
}

0 comments on commit 2062ab7

Please sign in to comment.