mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-13 14:39:28 +00:00
add errors; add mutex; fix some old tests
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/montanaflynn/stats"
|
"github.com/montanaflynn/stats"
|
||||||
@@ -12,6 +13,25 @@ import (
|
|||||||
"github.com/percona/percona-toolkit/src/go/mongolib/util"
|
"github.com/percona/percona-toolkit/src/go/mongolib/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type StatsError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *StatsError) Error() string {
|
||||||
|
if e == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("stats error: %s", e.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *StatsError) Parent() error {
|
||||||
|
return e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatsFingerprintError StatsError
|
||||||
|
type StatsGetQueryFieldError StatsError
|
||||||
|
|
||||||
// New creates new instance of stats with given fingerprinter
|
// New creates new instance of stats with given fingerprinter
|
||||||
func New(fingerprinter fingerprinter.Fingerprinter) *Stats {
|
func New(fingerprinter fingerprinter.Fingerprinter) *Stats {
|
||||||
s := &Stats{
|
s := &Stats{
|
||||||
@@ -29,10 +49,14 @@ type Stats struct {
|
|||||||
|
|
||||||
// internal
|
// internal
|
||||||
queryInfoAndCounters map[GroupKey]*QueryInfoAndCounters
|
queryInfoAndCounters map[GroupKey]*QueryInfoAndCounters
|
||||||
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset clears the collection of statistics
|
// Reset clears the collection of statistics
|
||||||
func (s *Stats) Reset() {
|
func (s *Stats) Reset() {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
s.queryInfoAndCounters = make(map[GroupKey]*QueryInfoAndCounters)
|
s.queryInfoAndCounters = make(map[GroupKey]*QueryInfoAndCounters)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +64,7 @@ func (s *Stats) Reset() {
|
|||||||
func (s *Stats) Add(doc proto.SystemProfile) error {
|
func (s *Stats) Add(doc proto.SystemProfile) error {
|
||||||
fp, err := s.fingerprinter.Fingerprint(doc.Query)
|
fp, err := s.fingerprinter.Fingerprint(doc.Query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot get fingerprint: %s", err)
|
return &StatsFingerprintError{err}
|
||||||
}
|
}
|
||||||
var qiac *QueryInfoAndCounters
|
var qiac *QueryInfoAndCounters
|
||||||
var ok bool
|
var ok bool
|
||||||
@@ -50,8 +74,11 @@ func (s *Stats) Add(doc proto.SystemProfile) error {
|
|||||||
Fingerprint: fp,
|
Fingerprint: fp,
|
||||||
Namespace: doc.Ns,
|
Namespace: doc.Ns,
|
||||||
}
|
}
|
||||||
if qiac, ok = s.queryInfoAndCounters[key]; !ok {
|
if qiac, ok = s.getQueryInfoAndCounters(key); !ok {
|
||||||
realQuery, _ := util.GetQueryField(doc.Query)
|
realQuery, err := util.GetQueryField(doc.Query)
|
||||||
|
if err != nil {
|
||||||
|
return &StatsGetQueryFieldError{err}
|
||||||
|
}
|
||||||
qiac = &QueryInfoAndCounters{
|
qiac = &QueryInfoAndCounters{
|
||||||
ID: fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s", key)))),
|
ID: fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s", key)))),
|
||||||
Operation: doc.Op,
|
Operation: doc.Op,
|
||||||
@@ -60,7 +87,7 @@ func (s *Stats) Add(doc proto.SystemProfile) error {
|
|||||||
TableScan: false,
|
TableScan: false,
|
||||||
Query: realQuery,
|
Query: realQuery,
|
||||||
}
|
}
|
||||||
s.queryInfoAndCounters[key] = qiac
|
s.setQueryInfoAndCounters(key, qiac)
|
||||||
}
|
}
|
||||||
qiac.Count++
|
qiac.Count++
|
||||||
qiac.NScanned = append(qiac.NScanned, float64(doc.DocsExamined))
|
qiac.NScanned = append(qiac.NScanned, float64(doc.DocsExamined))
|
||||||
@@ -80,7 +107,29 @@ func (s *Stats) Add(doc proto.SystemProfile) error {
|
|||||||
|
|
||||||
// Queries returns all collected statistics
|
// Queries returns all collected statistics
|
||||||
func (s *Stats) Queries() Queries {
|
func (s *Stats) Queries() Queries {
|
||||||
return mapToArray(s.queryInfoAndCounters)
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
queries := []QueryInfoAndCounters{}
|
||||||
|
for _, v := range s.queryInfoAndCounters {
|
||||||
|
queries = append(queries, *v)
|
||||||
|
}
|
||||||
|
return queries
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stats) getQueryInfoAndCounters(key GroupKey) (*QueryInfoAndCounters, bool) {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
v, ok := s.queryInfoAndCounters[key]
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stats) setQueryInfoAndCounters(key GroupKey, value *QueryInfoAndCounters) {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
s.queryInfoAndCounters[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queries is a slice of MongoDB statistics
|
// Queries is a slice of MongoDB statistics
|
||||||
@@ -109,6 +158,25 @@ func (q Queries) CalcTotalQueriesStats(uptime int64) QueryStats {
|
|||||||
return totalStats
|
return totalStats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueryInfoAndCounters struct {
|
||||||
|
ID string
|
||||||
|
Namespace string
|
||||||
|
Operation string
|
||||||
|
Query map[string]interface{}
|
||||||
|
Fingerprint string
|
||||||
|
FirstSeen time.Time
|
||||||
|
LastSeen time.Time
|
||||||
|
TableScan bool
|
||||||
|
|
||||||
|
Count int
|
||||||
|
BlockedTime Times
|
||||||
|
LockTime Times
|
||||||
|
NReturned []float64
|
||||||
|
NScanned []float64
|
||||||
|
QueryTime []float64 // in milliseconds
|
||||||
|
ResponseLength []float64
|
||||||
|
}
|
||||||
|
|
||||||
// times is an array of time.Time that implements the Sorter interface
|
// times is an array of time.Time that implements the Sorter interface
|
||||||
type Times []time.Time
|
type Times []time.Time
|
||||||
|
|
||||||
@@ -149,25 +217,6 @@ type QueryStats struct {
|
|||||||
Scanned Statistics
|
Scanned Statistics
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryInfoAndCounters struct {
|
|
||||||
ID string
|
|
||||||
Namespace string
|
|
||||||
Operation string
|
|
||||||
Query map[string]interface{}
|
|
||||||
Fingerprint string
|
|
||||||
FirstSeen time.Time
|
|
||||||
LastSeen time.Time
|
|
||||||
TableScan bool
|
|
||||||
|
|
||||||
Count int
|
|
||||||
BlockedTime Times
|
|
||||||
LockTime Times
|
|
||||||
NReturned []float64
|
|
||||||
NScanned []float64
|
|
||||||
QueryTime []float64 // in milliseconds
|
|
||||||
ResponseLength []float64
|
|
||||||
}
|
|
||||||
|
|
||||||
type Statistics struct {
|
type Statistics struct {
|
||||||
Pct float64
|
Pct float64
|
||||||
Total float64
|
Total float64
|
||||||
@@ -258,11 +307,3 @@ func calcStats(samples []float64) Statistics {
|
|||||||
s.Median, _ = stats.Median(samples)
|
s.Median, _ = stats.Median(samples)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapToArray(stats map[GroupKey]*QueryInfoAndCounters) []QueryInfoAndCounters {
|
|
||||||
sa := []QueryInfoAndCounters{}
|
|
||||||
for _, s := range stats {
|
|
||||||
sa = append(sa, *s)
|
|
||||||
}
|
|
||||||
return sa
|
|
||||||
}
|
|
||||||
|
@@ -5,7 +5,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -395,8 +394,6 @@ func TestParseArgs(t *testing.T) {
|
|||||||
want: &options{
|
want: &options{
|
||||||
Host: DEFAULT_HOST,
|
Host: DEFAULT_HOST,
|
||||||
LogLevel: DEFAULT_LOGLEVEL,
|
LogLevel: DEFAULT_LOGLEVEL,
|
||||||
OrderBy: strings.Split(DEFAULT_ORDERBY, ","),
|
|
||||||
SkipCollections: strings.Split(DEFAULT_SKIPCOLLECTIONS, ","),
|
|
||||||
AuthDB: DEFAULT_AUTHDB,
|
AuthDB: DEFAULT_AUTHDB,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -405,8 +402,6 @@ func TestParseArgs(t *testing.T) {
|
|||||||
want: &options{
|
want: &options{
|
||||||
Host: "zapp.brannigan.net:27018/samples",
|
Host: "zapp.brannigan.net:27018/samples",
|
||||||
LogLevel: DEFAULT_LOGLEVEL,
|
LogLevel: DEFAULT_LOGLEVEL,
|
||||||
OrderBy: strings.Split(DEFAULT_ORDERBY, ","),
|
|
||||||
SkipCollections: strings.Split(DEFAULT_SKIPCOLLECTIONS, ","),
|
|
||||||
AuthDB: DEFAULT_AUTHDB,
|
AuthDB: DEFAULT_AUTHDB,
|
||||||
Help: true,
|
Help: true,
|
||||||
},
|
},
|
||||||
@@ -415,7 +410,7 @@ func TestParseArgs(t *testing.T) {
|
|||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
getopt.Reset()
|
getopt.Reset()
|
||||||
os.Args = test.args
|
os.Args = test.args
|
||||||
got, err := getOptions()
|
got, err := parseFlags()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("error parsing command line arguments: %s", err.Error())
|
t.Errorf("error parsing command line arguments: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user