New profiler and tests

This commit is contained in:
Carlos Salguero
2017-04-09 23:21:27 -03:00
parent 25efaef471
commit 64a5c6d8b6
5 changed files with 735 additions and 722 deletions

View File

@@ -0,0 +1,273 @@
package profiler
import (
"log"
"os"
"reflect"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/percona/percona-toolkit/src/go/lib/tutil"
"github.com/percona/percona-toolkit/src/go/mongolib/fingerprinter"
"github.com/percona/percona-toolkit/src/go/mongolib/proto"
"github.com/percona/percona-toolkit/src/go/pt-mongodb-query-digest/filter"
"github.com/percona/pmgo/pmgomock"
)
const (
samples = "/src/go/tests/"
)
type testVars struct {
RootPath string
}
var vars testVars
func TestMain(m *testing.M) {
var err error
if vars.RootPath, err = tutil.RootPath(); err != nil {
log.Printf("cannot get root path: %s", err.Error())
os.Exit(1)
}
os.Exit(m.Run())
}
func TestRegularIterator(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
docs := []proto.SystemProfile{}
err := tutil.LoadJson(vars.RootPath+samples+"profiler_docs.json", &docs)
if err != nil {
t.Fatalf("cannot load samples: %s", err.Error())
}
iter := pmgomock.NewMockIterManager(ctrl)
gomock.InOrder(
iter.EXPECT().Next(gomock.Any()).SetArg(0, docs[0]).Return(true),
iter.EXPECT().Timeout().Return(false),
iter.EXPECT().Next(gomock.Any()).SetArg(0, docs[1]).Return(true),
iter.EXPECT().Timeout().Return(false),
iter.EXPECT().Next(gomock.Any()).Return(false),
iter.EXPECT().Timeout().Return(false),
iter.EXPECT().Close(),
)
filters := []filter.Filter{}
fp := fingerprinter.NewFingerprinter(fingerprinter.DEFAULT_KEY_FILTERS)
prof := NewProfiler(iter, filters, nil, fp)
firstSeen, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:19.914-03:00")
lastSeen, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:20.214-03:00")
want := []QueryInfoAndCounters{
QueryInfoAndCounters{
ID: "c6466139b21c392acd0699e863b50d81",
Namespace: "samples.col1",
Operation: "query",
Query: map[string]interface{}{
"find": "col1",
"shardVersion": []interface{}{float64(0), "000000000000000000000000"},
},
Fingerprint: "find",
FirstSeen: firstSeen,
LastSeen: lastSeen,
TableScan: false,
Count: 2,
BlockedTime: Times(nil),
LockTime: Times(nil),
NReturned: []float64{50, 75},
NScanned: []float64{100, 75},
QueryTime: []float64{0, 1},
ResponseLength: []float64{1.06123e+06, 1.06123e+06},
},
}
prof.Start()
select {
case queries := <-prof.QueriesChan():
if !reflect.DeepEqual(queries, want) {
t.Errorf("invalid queries. \nGot: %#v,\nWant: %#v\n", queries, want)
}
case <-time.After(2 * time.Second):
t.Error("Didn't get any query")
}
}
func TestIteratorTimeout(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
docs := []proto.SystemProfile{}
err := tutil.LoadJson(vars.RootPath+samples+"profiler_docs.json", &docs)
if err != nil {
t.Fatalf("cannot load samples: %s", err.Error())
}
iter := pmgomock.NewMockIterManager(ctrl)
gomock.InOrder(
iter.EXPECT().Next(gomock.Any()).Return(true),
iter.EXPECT().Timeout().Return(true),
iter.EXPECT().Next(gomock.Any()).SetArg(0, docs[1]).Return(true),
iter.EXPECT().Timeout().Return(false),
iter.EXPECT().Next(gomock.Any()).Return(false),
iter.EXPECT().Timeout().Return(false),
// When there are no more docs, iterator will close
iter.EXPECT().Close(),
// And we are closing it again (to force the getData go-routine to end)
// at the profiler.Stop() method
iter.EXPECT().Close(),
)
filters := []filter.Filter{}
fp := fingerprinter.NewFingerprinter(fingerprinter.DEFAULT_KEY_FILTERS)
prof := NewProfiler(iter, filters, nil, fp)
firstSeen, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:19.914-03:00")
lastSeen, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:19.914-03:00")
want := []QueryInfoAndCounters{
QueryInfoAndCounters{
ID: "c6466139b21c392acd0699e863b50d81",
Namespace: "samples.col1",
Operation: "query",
Query: map[string]interface{}{
"find": "col1",
"shardVersion": []interface{}{float64(0), "000000000000000000000000"},
},
Fingerprint: "find",
FirstSeen: firstSeen,
LastSeen: lastSeen,
TableScan: false,
Count: 1,
BlockedTime: Times(nil),
LockTime: Times(nil),
NReturned: []float64{75},
NScanned: []float64{75},
QueryTime: []float64{1},
ResponseLength: []float64{1.06123e+06},
},
}
prof.Start()
gotTimeout := false
// Get a timeout
select {
case <-prof.TimeoutsChan():
gotTimeout = true
case <-prof.QueriesChan():
t.Error("Got queries before timeout")
case <-time.After(2 * time.Second):
t.Error("Timeout checking timeout")
}
if !gotTimeout {
t.Error("Didn't get a timeout")
}
// After the first document returned a timeout, we should still receive the second document
select {
case queries := <-prof.QueriesChan():
if !reflect.DeepEqual(queries, want) {
t.Errorf("invalid queries. \nGot: %#v,\nWant: %#v\n", queries, want)
}
case <-time.After(2 * time.Second):
t.Error("Didn't get any query after 2 seconds")
}
prof.Stop()
}
func TestTailIterator(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
docs := []proto.SystemProfile{}
err := tutil.LoadJson(vars.RootPath+samples+"profiler_docs.json", &docs)
if err != nil {
t.Fatalf("cannot load samples: %s", err.Error())
}
sleep := func(param interface{}) {
time.Sleep(1500 * time.Millisecond)
}
iter := pmgomock.NewMockIterManager(ctrl)
gomock.InOrder(
iter.EXPECT().Next(gomock.Any()).SetArg(0, docs[0]).Return(true),
iter.EXPECT().Timeout().Return(false),
// A Tail iterator will wait if the are no available docs.
// Do a 1500 ms sleep before returning the second doc to simulate a tail wait
// and to let the ticker ticks
iter.EXPECT().Next(gomock.Any()).Do(sleep).SetArg(0, docs[1]).Return(true),
iter.EXPECT().Timeout().Return(false),
iter.EXPECT().Next(gomock.Any()).Return(false),
iter.EXPECT().Timeout().Return(false),
iter.EXPECT().Close(),
)
filters := []filter.Filter{}
ticker := time.NewTicker(time.Second)
fp := fingerprinter.NewFingerprinter(fingerprinter.DEFAULT_KEY_FILTERS)
prof := NewProfiler(iter, filters, ticker.C, fp)
firstSeen, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:20.214-03:00")
lastSeen, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:20.214-03:00")
firstSeen2, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:19.914-03:00")
lastSeen2, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:19.914-03:00")
want := []QueryInfoAndCounters{
QueryInfoAndCounters{
ID: "c6466139b21c392acd0699e863b50d81",
Namespace: "samples.col1",
Operation: "query",
Query: map[string]interface{}{
"find": "col1",
"shardVersion": []interface{}{float64(0), "000000000000000000000000"},
},
Fingerprint: "find",
FirstSeen: firstSeen,
LastSeen: lastSeen,
TableScan: false,
Count: 1,
BlockedTime: Times(nil),
LockTime: Times(nil),
NReturned: []float64{50},
NScanned: []float64{100},
QueryTime: []float64{0},
ResponseLength: []float64{1.06123e+06},
},
QueryInfoAndCounters{
ID: "c6466139b21c392acd0699e863b50d81",
Namespace: "samples.col1",
Operation: "query",
Query: map[string]interface{}{
"find": "col1",
"shardVersion": []interface{}{float64(0), "000000000000000000000000"},
},
Fingerprint: "find",
FirstSeen: firstSeen2,
LastSeen: lastSeen2,
TableScan: false,
Count: 1,
BlockedTime: Times(nil),
LockTime: Times(nil),
NReturned: []float64{75},
NScanned: []float64{75},
QueryTime: []float64{1},
ResponseLength: []float64{1.06123e+06},
},
}
prof.Start()
index := 0
// Since the mocked iterator has a Sleep(1500 ms) between Next methods calls,
// we are going to have two ticker ticks and on every tick it will return one document.
for index < 2 {
select {
case queries := <-prof.QueriesChan():
if !reflect.DeepEqual(queries, []QueryInfoAndCounters{want[index]}) {
t.Errorf("invalid queries. \nGot: %#v,\nWant: %#v\n", queries, want)
}
index++
}
}
}