diff --git a/src/go/lib/tutil/util.go b/src/go/lib/tutil/util.go index 4acda744..3a4caa7e 100644 --- a/src/go/lib/tutil/util.go +++ b/src/go/lib/tutil/util.go @@ -8,6 +8,10 @@ import ( "strings" ) +const ( + updateSamplesEnvVar = "UPDATE_SAMPLES" +) + func RootPath() (string, error) { out, err := exec.Command("git", "rev-parse", "--show-toplevel").Output() if err != nil { @@ -42,14 +46,21 @@ func LoadJson(filename string, destination interface{}) error { func WriteJson(filename string, data interface{}) error { - buf, err := json.Marshal(data) + buf, err := json.MarshalIndent(data, "", " ") if err != nil { return err } - err = ioutil.WriteFile(filename, buf, 0) + err = ioutil.WriteFile(filename, buf, 777) if err != nil { return err } return nil } + +func ShouldUpdateSamples() bool { + if os.Getenv(updateSamplesEnvVar) != "" { + return true + } + return false +} diff --git a/src/go/mongolib/profiler/profiler.go b/src/go/mongolib/profiler/profiler.go index bebfd3d9..9181a4cd 100644 --- a/src/go/mongolib/profiler/profiler.go +++ b/src/go/mongolib/profiler/profiler.go @@ -50,21 +50,27 @@ type Profiler interface { GetLastError() error QueriesChan() chan []QueryInfoAndCounters TimeoutsChan() <-chan time.Time + ProcessDoc(proto.SystemProfile, map[StatsGroupKey]*QueryInfoAndCounters) error Start() Stop() } type Profile struct { - filters []filter.Filter - iterator pmgo.IterManager - ticker <-chan time.Time - queriesChan chan []QueryInfoAndCounters - stopChan chan bool - docsChan chan proto.SystemProfile - timeoutsChan chan time.Time + filters []filter.Filter + iterator pmgo.IterManager + ticker <-chan time.Time + queriesChan chan []QueryInfoAndCounters + stopChan chan bool + docsChan chan proto.SystemProfile + timeoutsChan chan time.Time + // For the moment ProcessDoc is exportable to it could be called from the "outside" + // For that reason, we need a mutex to make it thread safe. In the future this func + // will be unexported + countersMapLock sync.Mutex queriesInfoAndCounters map[StatsGroupKey]*QueryInfoAndCounters keyFilters []string fingerprinter fingerprinter.Fingerprinter + lock sync.Mutex running bool lastError error stopWaitGroup sync.WaitGroup @@ -142,6 +148,8 @@ func (p *Profile) QueriesChan() chan []QueryInfoAndCounters { } func (p *Profile) Start() { + p.lock.Lock() + defer p.lock.Unlock() if !p.running { p.running = true p.stopChan = make(chan bool) @@ -150,15 +158,15 @@ func (p *Profile) Start() { } func (p *Profile) Stop() { + p.lock.Lock() + defer p.lock.Unlock() if p.running { select { case p.stopChan <- true: default: } - close(p.timeoutsChan) // Wait for getData to receive the stop signal p.stopWaitGroup.Wait() - p.iterator.Close() } } @@ -180,6 +188,7 @@ MAIN_GETDATA_LOOP: p.queriesChan <- mapToArray(p.queriesInfoAndCounters) p.queriesInfoAndCounters = make(map[StatsGroupKey]*QueryInfoAndCounters) // Reset stats case <-p.stopChan: + // Close the iterator to break the loop on getDocs p.iterator.Close() break MAIN_GETDATA_LOOP } @@ -212,10 +221,7 @@ func (p *Profile) getDocs() { } } p.queriesChan <- mapToArray(p.queriesInfoAndCounters) - select { - case p.stopChan <- true: - default: - } + p.Stop() } func (p *Profile) ProcessDoc(doc proto.SystemProfile, stats map[StatsGroupKey]*QueryInfoAndCounters) error { @@ -226,12 +232,15 @@ func (p *Profile) ProcessDoc(doc proto.SystemProfile, stats map[StatsGroupKey]*Q } var s *QueryInfoAndCounters var ok bool + p.countersMapLock.Lock() + defer p.countersMapLock.Unlock() + key := StatsGroupKey{ Operation: doc.Op, Fingerprint: fp, Namespace: doc.Ns, } - if s, ok = p.queriesInfoAndCounters[key]; !ok { + if s, ok = stats[key]; !ok { realQuery, _ := util.GetQueryField(doc.Query) s = &QueryInfoAndCounters{ ID: fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s", key)))), @@ -241,7 +250,7 @@ func (p *Profile) ProcessDoc(doc proto.SystemProfile, stats map[StatsGroupKey]*Q TableScan: false, Query: realQuery, } - p.queriesInfoAndCounters[key] = s + stats[key] = s } s.Count++ s.NScanned = append(s.NScanned, float64(doc.DocsExamined)) diff --git a/src/go/mongolib/profiler/profiler_test.go b/src/go/mongolib/profiler/profiler_test.go index 22cfc819..fd709c03 100644 --- a/src/go/mongolib/profiler/profiler_test.go +++ b/src/go/mongolib/profiler/profiler_test.go @@ -1,6 +1,7 @@ package profiler import ( + "fmt" "log" "os" "reflect" @@ -25,6 +26,11 @@ type testVars struct { var vars testVars +func parseDate(dateStr string) time.Time { + date, _ := time.Parse(time.RFC3339Nano, dateStr) + return date +} + func TestMain(m *testing.M) { var err error if vars.RootPath, err = tutil.RootPath(); err != nil { @@ -58,8 +64,8 @@ func TestRegularIterator(t *testing.T) { 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") + firstSeen, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:19.914+00:00") + lastSeen, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:20.214+00:00") want := []QueryInfoAndCounters{ QueryInfoAndCounters{ ID: "c6466139b21c392acd0699e863b50d81", @@ -113,17 +119,14 @@ func TestIteratorTimeout(t *testing.T) { 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") + firstSeen, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:19.914+00:00") + lastSeen, _ := time.Parse(time.RFC3339Nano, "2017-04-01T23:01:19.914+00:00") want := []QueryInfoAndCounters{ QueryInfoAndCounters{ ID: "c6466139b21c392acd0699e863b50d81", @@ -196,7 +199,7 @@ func TestTailIterator(t *testing.T) { 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 + // and to let the ticker tick 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), @@ -209,12 +212,6 @@ func TestTailIterator(t *testing.T) { 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", @@ -225,8 +222,8 @@ func TestTailIterator(t *testing.T) { "shardVersion": []interface{}{float64(0), "000000000000000000000000"}, }, Fingerprint: "find", - FirstSeen: firstSeen, - LastSeen: lastSeen, + FirstSeen: parseDate("2017-04-01T23:01:20.214+00:00"), + LastSeen: parseDate("2017-04-01T23:01:20.214+00:00"), TableScan: false, Count: 1, BlockedTime: Times(nil), @@ -245,8 +242,8 @@ func TestTailIterator(t *testing.T) { "shardVersion": []interface{}{float64(0), "000000000000000000000000"}, }, Fingerprint: "find", - FirstSeen: firstSeen2, - LastSeen: lastSeen2, + FirstSeen: parseDate("2017-04-01T23:01:19.914+00:00"), + LastSeen: parseDate("2017-04-01T23:01:19.914+00:00"), TableScan: false, Count: 1, BlockedTime: Times(nil), @@ -271,3 +268,202 @@ func TestTailIterator(t *testing.T) { } } } + +func TestCalcStats(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + docs := []proto.SystemProfile{} + err := tutil.LoadJson(vars.RootPath+samples+"profiler_docs_stats.json", &docs) + if err != nil { + t.Fatalf("cannot load samples: %s", err.Error()) + } + + want := []QueryStats{} + err = tutil.LoadJson(vars.RootPath+samples+"profiler_docs_stats.want.json", &want) + if err != nil { + t.Fatalf("cannot load expected results: %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()).SetArg(0, docs[2]).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) + + prof.Start() + select { + case queries := <-prof.QueriesChan(): + stats := CalcQueriesStats(queries, 1) + if os.Getenv("UPDATE_SAMPLES") != "" { + tutil.WriteJson(vars.RootPath+samples+"profiler_docs_stats.want.json", stats) + } + if !reflect.DeepEqual(stats, want) { + t.Errorf("Invalid stats.\nGot:%#v\nWant: %#v\n", stats, want) + } + case <-time.After(2 * time.Second): + t.Error("Didn't get any query") + } +} + +func TestCalcTotalStats(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + docs := []proto.SystemProfile{} + err := tutil.LoadJson(vars.RootPath+samples+"profiler_docs_stats.json", &docs) + if err != nil { + t.Fatalf("cannot load samples: %s", err.Error()) + } + + want := QueryStats{} + err = tutil.LoadJson(vars.RootPath+samples+"profiler_docs_total_stats.want.json", &want) + if err != nil && !tutil.ShouldUpdateSamples() { + t.Fatalf("cannot load expected results: %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()).SetArg(0, docs[2]).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) + + prof.Start() + select { + case queries := <-prof.QueriesChan(): + stats := CalcTotalQueriesStats(queries, 1) + if os.Getenv("UPDATE_SAMPLES") != "" { + fmt.Println("Updating samples: " + vars.RootPath + samples + "profiler_docs_total_stats.want.json") + err := tutil.WriteJson(vars.RootPath+samples+"profiler_docs_total_stats.want.json", stats) + if err != nil { + fmt.Printf("cannot update samples: %s", err.Error()) + } + } + if !reflect.DeepEqual(stats, want) { + t.Errorf("Invalid stats.\nGot:%#v\nWant: %#v\n", stats, want) + } + case <-time.After(2 * time.Second): + t.Error("Didn't get any query") + } +} + +func TestCalcTotalCounters(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + docs := []proto.SystemProfile{} + err := tutil.LoadJson(vars.RootPath+samples+"profiler_docs_stats.json", &docs) + if err != nil { + t.Fatalf("cannot load samples: %s", err.Error()) + } + + want := totalCounters{} + err = tutil.LoadJson(vars.RootPath+samples+"profiler_docs_total_counters.want.json", &want) + if err != nil && !tutil.ShouldUpdateSamples() { + t.Fatalf("cannot load expected results: %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()).SetArg(0, docs[2]).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) + + prof.Start() + select { + case queries := <-prof.QueriesChan(): + counters := calcTotalCounters(queries) + if tutil.ShouldUpdateSamples() { + fmt.Println("Updating samples: " + vars.RootPath + samples + "profiler_docs_total_counters.want.json") + err := tutil.WriteJson(vars.RootPath+samples+"profiler_docs_total_counters.want.json", counters) + if err != nil { + fmt.Printf("cannot update samples: %s", err.Error()) + } + } + if !reflect.DeepEqual(counters, want) { + t.Errorf("Invalid counters.\nGot:%#v\nWant: %#v\n", counters, want) + } + case <-time.After(2 * time.Second): + t.Error("Didn't get any query") + } +} + +func TestProcessDoc(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + docs := []proto.SystemProfile{} + err := tutil.LoadJson(vars.RootPath+samples+"profiler_docs_stats.json", &docs) + if err != nil { + t.Fatalf("cannot load samples: %s", err.Error()) + } + + iter := pmgomock.NewMockIterManager(ctrl) + filters := []filter.Filter{} + fp := fingerprinter.NewFingerprinter(fingerprinter.DEFAULT_KEY_FILTERS) + prof := NewProfiler(iter, filters, nil, fp) + + var stats = make(map[StatsGroupKey]*QueryInfoAndCounters) + + err = prof.ProcessDoc(docs[1], stats) + if err != nil { + t.Errorf("Error processing doc: %s\n", err.Error()) + } + statsKey := StatsGroupKey{Operation: "query", Fingerprint: "s2", Namespace: "samples.col1"} + statsVal := &QueryInfoAndCounters{ + ID: "84e09ef6a3dc35f472df05fa98eee7d3", + Namespace: "samples.col1", + Operation: "query", + Query: map[string]interface{}{"s2": map[string]interface{}{"$gte": "41991", "$lt": "33754"}}, + Fingerprint: "s2", + FirstSeen: parseDate("2017-04-10T13:15:53.532-03:00"), + LastSeen: parseDate("2017-04-10T13:15:53.532-03:00"), + TableScan: false, + Count: 1, + BlockedTime: nil, + LockTime: nil, + NReturned: []float64{0}, + NScanned: []float64{10000}, + QueryTime: []float64{7}, + ResponseLength: []float64{215}, + } + + want := map[StatsGroupKey]*QueryInfoAndCounters{statsKey: statsVal} + + if !reflect.DeepEqual(stats, want) { + t.Errorf("Error in ProcessDoc.\nGot:%#v\nWant: %#v\n", stats, want) + } +} diff --git a/src/go/pt-mongodb-query-digest/main.go b/src/go/pt-mongodb-query-digest/main.go index b078a82c..10e982ca 100644 --- a/src/go/pt-mongodb-query-digest/main.go +++ b/src/go/pt-mongodb-query-digest/main.go @@ -140,6 +140,7 @@ func main() { queries := <-prof.QueriesChan() uptime := uptime(session) + queriesStats := profiler.CalcQueriesStats(queries, uptime) sortedQueryStats := sortQueries(queriesStats, opts.OrderBy) diff --git a/src/go/tests/profiler_docs.json b/src/go/tests/profiler_docs.json index b03f038e..b4cf06e9 100644 --- a/src/go/tests/profiler_docs.json +++ b/src/go/tests/profiler_docs.json @@ -76,7 +76,7 @@ ] }, "ResponseLength": 1061230, - "Ts": "2017-04-01T23:01:20.214-03:00", + "Ts": "2017-04-01T23:01:20.214+00:00", "User": "", "WriteConflicts": 0 }, @@ -157,7 +157,7 @@ ] }, "ResponseLength": 1061230, - "Ts": "2017-04-01T23:01:19.914-03:00", + "Ts": "2017-04-01T23:01:19.914+00:00", "User": "", "WriteConflicts": 0 } diff --git a/src/go/tests/profiler_docs_stats.json b/src/go/tests/profiler_docs_stats.json new file mode 100644 index 00000000..9abff90c --- /dev/null +++ b/src/go/tests/profiler_docs_stats.json @@ -0,0 +1,265 @@ +[ + { + "AllUsers": [], + "Client": "127.0.0.1", + "CursorExhausted": false, + "DocsExamined": 75, + "ExecStats": { + "Advanced": 75, + "ExecutionTimeMillisEstimate": 10, + "InputStage": { + "Advanced": 0, + "Direction": "", + "DocsExamined": 0, + "ExecutionTimeMillisEstimate": 0, + "Filter": { + "Date": { + "Eq": "" + } + }, + "Invalidates": 0, + "IsEOF": 0, + "NReturned": 0, + "NeedTime": 0, + "NeedYield": 0, + "RestoreState": 0, + "SaveState": 0, + "Stage": "", + "Works": 0 + }, + "Invalidates": 0, + "IsEOF": 0, + "LimitAmount": 0, + "NReturned": 75, + "NeedTime": 1, + "NeedYield": 0, + "RestoreState": 0, + "SaveState": 1, + "Stage": "COLLSCAN", + "Works": 76 + }, + "KeyUpdates": 0, + "KeysExamined": 0, + "Locks": { + "Collection": { + "AcquireCount": { + "R": 0 + } + }, + "Database": { + "AcquireCount": { + "R": 1 + } + }, + "Global": { + "AcquireCount": { + "R": 2 + } + }, + "MMAPV1Journal": { + "AcquireCount": { + "R": 0 + } + } + }, + "Millis": 0, + "Nreturned": 75, + "Ns": "samples.col1", + "NumYield": 0, + "Op": "query", + "Protocol": "op_command", + "Query": { + "find": "col1", + "shardVersion": [ + 0, + "000000000000000000000000" + ] + }, + "ResponseLength": 1061230, + "Ts": "2017-04-10T13:16:23.29-03:00", + "User": "", + "WriteConflicts": 0 + }, + { + "AllUsers": [], + "Client": "127.0.0.1", + "CursorExhausted": true, + "DocsExamined": 10000, + "ExecStats": { + "Advanced": 0, + "ExecutionTimeMillisEstimate": 10, + "InputStage": { + "Advanced": 0, + "Direction": "", + "DocsExamined": 0, + "ExecutionTimeMillisEstimate": 0, + "Filter": { + "Date": { + "Eq": "" + } + }, + "Invalidates": 0, + "IsEOF": 0, + "NReturned": 0, + "NeedTime": 0, + "NeedYield": 0, + "RestoreState": 0, + "SaveState": 0, + "Stage": "", + "Works": 0 + }, + "Invalidates": 0, + "IsEOF": 1, + "LimitAmount": 0, + "NReturned": 0, + "NeedTime": 10001, + "NeedYield": 0, + "RestoreState": 78, + "SaveState": 78, + "Stage": "COLLSCAN", + "Works": 10002 + }, + "KeyUpdates": 0, + "KeysExamined": 0, + "Locks": { + "Collection": { + "AcquireCount": { + "R": 0 + } + }, + "Database": { + "AcquireCount": { + "R": 79 + } + }, + "Global": { + "AcquireCount": { + "R": 158 + } + }, + "MMAPV1Journal": { + "AcquireCount": { + "R": 0 + } + } + }, + "Millis": 7, + "Nreturned": 0, + "Ns": "samples.col1", + "NumYield": 78, + "Op": "query", + "Protocol": "op_command", + "Query": { + "filter": { + "s2": { + "$gte": "41991", + "$lt": "33754" + } + }, + "find": "col1", + "shardVersion": [ + 0, + "000000000000000000000000" + ] + }, + "ResponseLength": 215, + "Ts": "2017-04-10T13:15:53.532-03:00", + "User": "", + "WriteConflicts": 0 + }, + { + "AllUsers": [], + "Client": "127.0.0.1", + "CursorExhausted": true, + "DocsExamined": 0, + "ExecStats": { + "Advanced": 0, + "ExecutionTimeMillisEstimate": 0, + "InputStage": { + "Advanced": 0, + "Direction": "", + "DocsExamined": 0, + "ExecutionTimeMillisEstimate": 0, + "Filter": { + "Date": { + "Eq": "" + } + }, + "Invalidates": 0, + "IsEOF": 1, + "NReturned": 0, + "NeedTime": 1, + "NeedYield": 0, + "RestoreState": 0, + "SaveState": 0, + "Stage": "SORT_KEY_GENERATOR", + "Works": 2 + }, + "Invalidates": 0, + "IsEOF": 1, + "LimitAmount": 0, + "NReturned": 0, + "NeedTime": 1, + "NeedYield": 0, + "RestoreState": 0, + "SaveState": 0, + "Stage": "PROJECTION", + "Works": 2 + }, + "KeyUpdates": 0, + "KeysExamined": 0, + "Locks": { + "Collection": { + "AcquireCount": { + "R": 0 + } + }, + "Database": { + "AcquireCount": { + "R": 1 + } + }, + "Global": { + "AcquireCount": { + "R": 2 + } + }, + "MMAPV1Journal": { + "AcquireCount": { + "R": 0 + } + } + }, + "Millis": 0, + "Nreturned": 0, + "Ns": "samples.col1", + "NumYield": 0, + "Op": "query", + "Protocol": "op_command", + "Query": { + "filter": { + "user_id": { + "$gte": 3384024924, + "$lt": 195092007 + } + }, + "find": "col1", + "projection": { + "$sortKey": { + "$meta": "sortKey" + } + }, + "shardVersion": [ + 0, + "000000000000000000000000" + ], + "sort": { + "user_id": 1 + } + }, + "ResponseLength": 215, + "Ts": "2017-04-10T13:15:53.524-03:00", + "User": "", + "WriteConflicts": 0 + } +] diff --git a/src/go/tests/profiler_docs_stats.want.json b/src/go/tests/profiler_docs_stats.want.json new file mode 100644 index 00000000..fc203be0 --- /dev/null +++ b/src/go/tests/profiler_docs_stats.want.json @@ -0,0 +1,161 @@ +[ + { + "ID": "c6466139b21c392acd0699e863b50d81", + "Namespace": "samples.col1", + "Operation": "query", + "Query": "{\"find\":\"col1\",\"shardVersion\":[0,\"000000000000000000000000\"]}", + "Fingerprint": "find", + "FirstSeen": "2017-04-10T13:16:23.29-03:00", + "LastSeen": "2017-04-10T13:16:23.29-03:00", + "Count": 1, + "QPS": 1, + "Rank": 0, + "Ratio": 1, + "QueryTime": { + "Pct": 0, + "Total": 0, + "Min": 0, + "Max": 0, + "Avg": 0, + "Pct95": 0, + "StdDev": 0, + "Median": 0 + }, + "ResponseLength": { + "Pct": 0.9995949739087844, + "Total": 1061230, + "Min": 1061230, + "Max": 1061230, + "Avg": 1061230, + "Pct95": 1061230, + "StdDev": 0, + "Median": 1061230 + }, + "Returned": { + "Pct": 100, + "Total": 75, + "Min": 75, + "Max": 75, + "Avg": 75, + "Pct95": 75, + "StdDev": 0, + "Median": 75 + }, + "Scanned": { + "Pct": 0.7444168734491315, + "Total": 75, + "Min": 75, + "Max": 75, + "Avg": 75, + "Pct95": 75, + "StdDev": 0, + "Median": 75 + } + }, + { + "ID": "84e09ef6a3dc35f472df05fa98eee7d3", + "Namespace": "samples.col1", + "Operation": "query", + "Query": "{\"s2\":{\"$gte\":\"41991\",\"$lt\":\"33754\"}}", + "Fingerprint": "s2", + "FirstSeen": "2017-04-10T13:15:53.532-03:00", + "LastSeen": "2017-04-10T13:15:53.532-03:00", + "Count": 1, + "QPS": 1, + "Rank": 0, + "Ratio": 0, + "QueryTime": { + "Pct": 100, + "Total": 7, + "Min": 7, + "Max": 7, + "Avg": 7, + "Pct95": 7, + "StdDev": 0, + "Median": 7 + }, + "ResponseLength": { + "Pct": 0.00020251304560782172, + "Total": 215, + "Min": 215, + "Max": 215, + "Avg": 215, + "Pct95": 215, + "StdDev": 0, + "Median": 215 + }, + "Returned": { + "Pct": 0, + "Total": 0, + "Min": 0, + "Max": 0, + "Avg": 0, + "Pct95": 0, + "StdDev": 0, + "Median": 0 + }, + "Scanned": { + "Pct": 99.25558312655087, + "Total": 10000, + "Min": 10000, + "Max": 10000, + "Avg": 10000, + "Pct95": 10000, + "StdDev": 0, + "Median": 10000 + } + }, + { + "ID": "69e3b2f5f0aefcec868c0fa5ec8cebe5", + "Namespace": "samples.col1", + "Operation": "query", + "Query": "{\"user_id\":{\"$gte\":3384024924,\"$lt\":195092007}}", + "Fingerprint": "user_id", + "FirstSeen": "2017-04-10T13:15:53.524-03:00", + "LastSeen": "2017-04-10T13:15:53.524-03:00", + "Count": 1, + "QPS": 1, + "Rank": 0, + "Ratio": 0, + "QueryTime": { + "Pct": 0, + "Total": 0, + "Min": 0, + "Max": 0, + "Avg": 0, + "Pct95": 0, + "StdDev": 0, + "Median": 0 + }, + "ResponseLength": { + "Pct": 0.00020251304560782172, + "Total": 215, + "Min": 215, + "Max": 215, + "Avg": 215, + "Pct95": 215, + "StdDev": 0, + "Median": 215 + }, + "Returned": { + "Pct": 0, + "Total": 0, + "Min": 0, + "Max": 0, + "Avg": 0, + "Pct95": 0, + "StdDev": 0, + "Median": 0 + }, + "Scanned": { + "Pct": 0, + "Total": 0, + "Min": 0, + "Max": 0, + "Avg": 0, + "Pct95": 0, + "StdDev": 0, + "Median": 0 + } + } +] \ No newline at end of file diff --git a/src/go/tests/profiler_docs_total_counters.want.json b/src/go/tests/profiler_docs_total_counters.want.json new file mode 100644 index 00000000..33c710e4 --- /dev/null +++ b/src/go/tests/profiler_docs_total_counters.want.json @@ -0,0 +1,7 @@ +{ + "Count": 3, + "Scanned": 10075, + "Returned": 75, + "QueryTime": 7, + "Bytes": 1061660 +} \ No newline at end of file diff --git a/src/go/tests/profiler_docs_total_stats.want.json b/src/go/tests/profiler_docs_total_stats.want.json new file mode 100755 index 00000000..39bc4c5c --- /dev/null +++ b/src/go/tests/profiler_docs_total_stats.want.json @@ -0,0 +1,53 @@ +{ + "ID": "", + "Namespace": "", + "Operation": "", + "Query": "null", + "Fingerprint": "", + "FirstSeen": "0001-01-01T00:00:00Z", + "LastSeen": "0001-01-01T00:00:00Z", + "Count": 0, + "QPS": 0, + "Rank": 0, + "Ratio": 134.33333333333334, + "QueryTime": { + "Pct": 100, + "Total": 7, + "Min": 0, + "Max": 7, + "Avg": 2.3333333333333335, + "Pct95": 7, + "StdDev": 3.2998316455372216, + "Median": 0 + }, + "ResponseLength": { + "Pct": 1, + "Total": 1061660, + "Min": 215, + "Max": 1061230, + "Avg": 353886.6666666667, + "Pct95": 1061230, + "StdDev": 500167.26762709644, + "Median": 215 + }, + "Returned": { + "Pct": 100, + "Total": 75, + "Min": 0, + "Max": 75, + "Avg": 25, + "Pct95": 75, + "StdDev": 35.35533905932738, + "Median": 0 + }, + "Scanned": { + "Pct": 100, + "Total": 10075, + "Min": 0, + "Max": 10000, + "Avg": 3358.3333333333335, + "Pct95": 10000, + "StdDev": 4696.46734850308, + "Median": 75 + } +} \ No newline at end of file