2018-05-08 09:38:12 +00:00
|
|
|
package metrics
|
2018-05-01 13:39:41 +00:00
|
|
|
|
|
|
|
import (
|
2019-03-26 01:55:07 +00:00
|
|
|
"fmt"
|
2020-08-27 12:10:58 +00:00
|
|
|
"strconv"
|
2018-05-01 13:39:41 +00:00
|
|
|
"testing"
|
2018-05-07 06:50:03 +00:00
|
|
|
"time"
|
2018-05-01 13:39:41 +00:00
|
|
|
|
|
|
|
"github.com/ipfs/ipfs-cluster/api"
|
|
|
|
)
|
|
|
|
|
2019-02-27 17:04:35 +00:00
|
|
|
func makeMetric(value string) *api.Metric {
|
|
|
|
metr := &api.Metric{
|
|
|
|
Name: "test",
|
|
|
|
Peer: "peer1",
|
|
|
|
Value: value,
|
|
|
|
Valid: true,
|
|
|
|
}
|
|
|
|
metr.SetTTL(5 * time.Second)
|
|
|
|
return metr
|
|
|
|
}
|
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
func TestNewWindow(t *testing.T) {
|
|
|
|
w := NewWindow(10)
|
|
|
|
w.window.Next()
|
|
|
|
}
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-26 01:55:07 +00:00
|
|
|
func TestWindow_Race(t *testing.T) {
|
|
|
|
t.SkipNow()
|
|
|
|
w := NewWindow(DefaultWindowCap)
|
|
|
|
start := make(chan struct{})
|
|
|
|
done := make(chan struct{})
|
|
|
|
log := make(chan string, 100)
|
|
|
|
|
|
|
|
// go routine to add metrics at regular interval
|
|
|
|
addTicker := time.NewTicker(10 * time.Millisecond)
|
|
|
|
go func() {
|
|
|
|
var i int
|
|
|
|
<-start
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-addTicker.C:
|
|
|
|
if i >= 25 {
|
|
|
|
i = 0
|
|
|
|
}
|
|
|
|
time.Sleep(time.Duration(i) * time.Millisecond)
|
|
|
|
w.Add(makeMetric("1"))
|
|
|
|
i++
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// go routine to query latest at regular interval
|
|
|
|
latestTicker := time.NewTicker(20 * time.Millisecond)
|
|
|
|
go func() {
|
|
|
|
<-start
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-latestTicker.C:
|
|
|
|
// l, _ := w.Latest()
|
|
|
|
w.Latest()
|
|
|
|
// log <- fmt.Sprintf("latest: %v", l)
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// go routine to query all at regular interval
|
|
|
|
allTicker := time.NewTicker(30 * time.Millisecond)
|
|
|
|
go func() {
|
|
|
|
<-start
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-allTicker.C:
|
|
|
|
w.All()
|
|
|
|
// log <- fmt.Sprintf("all: %v", w.All())
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// go routine to query distribution at regular interval
|
|
|
|
distributionTicker := time.NewTicker(100 * time.Millisecond)
|
|
|
|
go func() {
|
|
|
|
<-start
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-distributionTicker.C:
|
|
|
|
log <- fmt.Sprintf("dist: %v", w.Distribution())
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
<-start
|
2020-04-14 17:58:00 +00:00
|
|
|
<-done
|
|
|
|
for s := range log {
|
|
|
|
fmt.Println(s)
|
2019-03-26 01:55:07 +00:00
|
|
|
}
|
2020-04-14 17:58:00 +00:00
|
|
|
close(done)
|
2019-03-26 01:55:07 +00:00
|
|
|
}()
|
|
|
|
|
|
|
|
close(start)
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
done <- struct{}{}
|
|
|
|
<-done
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWindow_Add(t *testing.T) {
|
|
|
|
t.Run("add single value", func(t *testing.T) {
|
|
|
|
mw := NewWindow(4)
|
|
|
|
want := makeMetric("1")
|
|
|
|
mw.Add(want)
|
|
|
|
|
|
|
|
mw.wMu.RLock()
|
|
|
|
prevRing := mw.window.Prev()
|
|
|
|
got, ok := prevRing.Value.(*api.Metric)
|
|
|
|
mw.wMu.RUnlock()
|
|
|
|
if !ok {
|
|
|
|
t.Error("value in window isn't an *api.Metric")
|
|
|
|
}
|
|
|
|
|
|
|
|
if got != want {
|
|
|
|
t.Errorf("got = %v, want = %v", got, want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
func BenchmarkWindow_Add(b *testing.B) {
|
|
|
|
b.Run("window size 10", func(b *testing.B) {
|
|
|
|
mw := NewWindow(10)
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
})
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
b.Run("window size 25", func(b *testing.B) {
|
|
|
|
mw := NewWindow(25)
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
})
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
b.Run("window size 1000", func(b *testing.B) {
|
|
|
|
mw := NewWindow(1000)
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
func TestWindow_Latest(t *testing.T) {
|
|
|
|
t.Run("no metrics error", func(t *testing.T) {
|
|
|
|
mw := NewWindow(4)
|
|
|
|
_, err := mw.Latest()
|
|
|
|
if err != ErrNoMetrics {
|
|
|
|
t.Error("expected ErrNoMetrics")
|
|
|
|
}
|
|
|
|
})
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
t.Run("single latest value", func(t *testing.T) {
|
|
|
|
mw := NewWindow(4)
|
|
|
|
mw.Add(makeMetric("1"))
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
metr, err := mw.Latest()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if metr.Value != "1" {
|
|
|
|
t.Error("expected different value")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkWindow_Latest(b *testing.B) {
|
|
|
|
b.Run("window size 10", func(b *testing.B) {
|
|
|
|
mw := NewWindow(10)
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
b.Run("window size 25", func(b *testing.B) {
|
|
|
|
mw := NewWindow(25)
|
|
|
|
for i := 0; i < 25; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
b.Run("window size 1000", func(b *testing.B) {
|
|
|
|
mw := NewWindow(1000)
|
|
|
|
for i := 0; i < 1000; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWindow_All(t *testing.T) {
|
|
|
|
t.Run("empty window", func(t *testing.T) {
|
|
|
|
mw := NewWindow(4)
|
|
|
|
if len(mw.All()) != 0 {
|
|
|
|
t.Error("expected 0 metrics")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("half capacity", func(t *testing.T) {
|
|
|
|
mw := NewWindow(4)
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
mw.Add(makeMetric("2"))
|
|
|
|
|
|
|
|
all := mw.All()
|
|
|
|
if len(all) != 2 {
|
|
|
|
t.Fatalf("should only be storing 2 metrics: got: %d", len(all))
|
|
|
|
}
|
|
|
|
|
|
|
|
if all[0].Value != "2" {
|
|
|
|
t.Error("newest metric should be first")
|
|
|
|
}
|
|
|
|
|
|
|
|
if all[1].Value != "1" {
|
|
|
|
t.Error("older metric should be second")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("full capacity", func(t *testing.T) {
|
|
|
|
mw := NewWindow(4)
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
mw.Add(makeMetric("2"))
|
|
|
|
mw.Add(makeMetric("3"))
|
|
|
|
mw.Add(makeMetric("4"))
|
|
|
|
|
|
|
|
all := mw.All()
|
|
|
|
if len(all) != 4 {
|
|
|
|
t.Fatalf("should only be storing 4 metrics: got: %d", len(all))
|
|
|
|
}
|
|
|
|
|
|
|
|
if all[len(all)-1].Value != "1" {
|
|
|
|
t.Error("oldest metric should be 1")
|
|
|
|
}
|
|
|
|
})
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
t.Run("over flow capacity", func(t *testing.T) {
|
|
|
|
mw := NewWindow(4)
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
mw.Add(makeMetric("2"))
|
|
|
|
mw.Add(makeMetric("3"))
|
|
|
|
mw.Add(makeMetric("4"))
|
|
|
|
mw.Add(makeMetric("5"))
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
all := mw.All()
|
|
|
|
if len(all) != 4 {
|
|
|
|
t.Fatalf("should only be storing 4 metrics: got: %d", len(all))
|
|
|
|
}
|
|
|
|
|
|
|
|
if all[len(all)-1].Value != "2" {
|
|
|
|
t.Error("oldest metric should be 2")
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWindow_AddParallel(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
mw := NewWindow(10)
|
|
|
|
|
|
|
|
t.Run("parallel adder 1", func(t *testing.T) {
|
|
|
|
for i := 0; i < 100; i++ {
|
|
|
|
mw.Add(makeMetric("adder 1"))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("parallel adder 2", func(t *testing.T) {
|
|
|
|
for i := 0; i < 100; i++ {
|
|
|
|
mw.Add(makeMetric("adder 2"))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkWindow_All(b *testing.B) {
|
|
|
|
b.Run("window size 10", func(b *testing.B) {
|
|
|
|
mw := NewWindow(10)
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.All()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
b.Run("window size 25", func(b *testing.B) {
|
|
|
|
mw := NewWindow(25)
|
|
|
|
for i := 0; i < 25; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.All()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
b.Run("window size 1000", func(b *testing.B) {
|
|
|
|
mw := NewWindow(1000)
|
|
|
|
for i := 0; i < 1000; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.All()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWindow_Distribution(t *testing.T) {
|
|
|
|
var tests = []struct {
|
|
|
|
name string
|
2019-03-21 10:42:56 +00:00
|
|
|
heartbeats []float64
|
|
|
|
want []float64
|
2019-03-08 02:10:51 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
"even 1 sec distribution",
|
2019-03-21 10:42:56 +00:00
|
|
|
[]float64{1, 1, 1, 1},
|
|
|
|
[]float64{1, 1, 1, 1},
|
2019-03-08 02:10:51 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"increasing latency distribution",
|
2019-03-21 10:42:56 +00:00
|
|
|
[]float64{1, 1, 2, 2, 3, 3, 4},
|
|
|
|
[]float64{4, 3, 3, 2, 2, 1, 1},
|
2019-03-08 02:10:51 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"random latency distribution",
|
2019-03-21 10:42:56 +00:00
|
|
|
[]float64{4, 1, 3, 9, 7, 8, 11, 18},
|
|
|
|
[]float64{18, 11, 8, 7, 9, 3, 1, 4},
|
2019-03-08 02:10:51 +00:00
|
|
|
},
|
2018-05-01 13:39:41 +00:00
|
|
|
}
|
2019-03-08 02:10:51 +00:00
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
mw := NewWindow(len(tt.heartbeats) + 1)
|
|
|
|
for i, v := range tt.heartbeats {
|
2020-08-27 12:10:58 +00:00
|
|
|
mw.Add(makeMetric(strconv.Itoa(int(v * 10))))
|
2019-03-26 01:55:07 +00:00
|
|
|
// time.Sleep on the 1s of milliseconds level is
|
|
|
|
// susceptible to scheduler variance. Hence we
|
|
|
|
// multiple the input by 10 and this combined with
|
|
|
|
// truncating the result to just seconds, we should
|
|
|
|
// get stable distribution of timings between
|
|
|
|
// window.Adds.
|
|
|
|
time.Sleep(time.Duration(v*10) * time.Millisecond)
|
2019-03-08 02:10:51 +00:00
|
|
|
if i == len(tt.heartbeats)-1 {
|
|
|
|
mw.Add(makeMetric("last"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
got := mw.Distribution()
|
2018-05-01 13:39:41 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
if len(got) != len(tt.want) {
|
|
|
|
t.Errorf("want len: %v, got len: %v", len(tt.want), len(got))
|
|
|
|
}
|
|
|
|
|
2019-03-21 10:42:56 +00:00
|
|
|
var gotseconds []float64
|
2019-03-08 02:10:51 +00:00
|
|
|
for _, v := range got {
|
|
|
|
// truncate nanoseconds to seconds for testing purposes
|
2019-03-21 10:42:56 +00:00
|
|
|
// also truncate decimal places by converting to int and then back
|
|
|
|
gotseconds = append(gotseconds, float64(int64(v/10000000)))
|
2019-03-08 02:10:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, s := range gotseconds {
|
|
|
|
if s != tt.want[i] {
|
|
|
|
t.Fatalf("want: %v, got: %v", tt.want, gotseconds)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2018-05-01 13:39:41 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-08 02:10:51 +00:00
|
|
|
|
|
|
|
func BenchmarkWindow_Distribution(b *testing.B) {
|
|
|
|
b.Run("window size 10", func(b *testing.B) {
|
|
|
|
mw := NewWindow(10)
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.Distribution()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
b.Run("window size 25", func(b *testing.B) {
|
|
|
|
mw := NewWindow(25)
|
|
|
|
for i := 0; i < 25; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.Distribution()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
b.Run("window size 1000", func(b *testing.B) {
|
|
|
|
mw := NewWindow(1000)
|
|
|
|
for i := 0; i < 1000; i++ {
|
|
|
|
mw.Add(makeMetric("1"))
|
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
mw.Distribution()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|