Files
percona-toolkit/src/go/mongolib/proto/system.profile.go
2019-07-01 12:53:15 +03:00

287 lines
7.4 KiB
Go

package proto
import (
"strings"
"time"
"go.mongodb.org/mongo-driver/bson"
)
// docsExamined is renamed from nscannedObjects in 3.2.0
// https://docs.mongodb.com/manual/reference/database-profiler/#system.profile.docsExamined
type SystemProfile struct {
AllUsers []interface{} `bson:"allUsers"`
Client string `bson:"client"`
CursorExhausted bool `bson:"cursorExhausted"`
DocsExamined int `bson:"docsExamined"`
NscannedObjects int `bson:"nscannedObjects"`
ExecStats struct {
Advanced int `bson:"advanced"`
ExecutionTimeMillisEstimate int `bson:"executionTimeMillisEstimate"`
InputStage struct {
Advanced int `bson:"advanced"`
Direction string `bson:"direction"`
DocsExamined int `bson:"docsExamined"`
ExecutionTimeMillisEstimate int `bson:"executionTimeMillisEstimate"`
Filter struct {
Date struct {
Eq string `bson:"$eq"`
} `bson:"date"`
} `bson:"filter"`
Invalidates int `bson:"invalidates"`
IsEOF int `bson:"isEOF"`
NReturned int `bson:"nReturned"`
NeedTime int `bson:"needTime"`
NeedYield int `bson:"needYield"`
RestoreState int `bson:"restoreState"`
SaveState int `bson:"saveState"`
Stage string `bson:"stage"`
Works int `bson:"works"`
} `bson:"inputStage"`
Invalidates int `bson:"invalidates"`
IsEOF int `bson:"isEOF"`
LimitAmount int `bson:"limitAmount"`
NReturned int `bson:"nReturned"`
NeedTime int `bson:"needTime"`
NeedYield int `bson:"needYield"`
RestoreState int `bson:"restoreState"`
SaveState int `bson:"saveState"`
Stage string `bson:"stage"`
Works int `bson:"works"`
} `bson:"execStats"`
KeyUpdates int `bson:"keyUpdates"`
KeysExamined int `bson:"keysExamined"`
Locks struct {
Collection struct {
AcquireCount struct {
R int `bson:"R"`
} `bson:"acquireCount"`
} `bson:"Collection"`
Database struct {
AcquireCount struct {
R int `bson:"r"`
} `bson:"acquireCount"`
} `bson:"Database"`
Global struct {
AcquireCount struct {
R int `bson:"r"`
} `bson:"acquireCount"`
} `bson:"Global"`
MMAPV1Journal struct {
AcquireCount struct {
R int `bson:"r"`
} `bson:"acquireCount"`
} `bson:"MMAPV1Journal"`
} `bson:"locks"`
Millis int `bson:"millis"`
Nreturned int `bson:"nreturned"`
Ns string `bson:"ns"`
NumYield int `bson:"numYield"`
Op string `bson:"op"`
Protocol string `bson:"protocol"`
Query BsonD `bson:"query"`
UpdateObj BsonD `bson:"updateobj"`
Command BsonD `bson:"command"`
OriginatingCommand BsonD `bson:"originatingCommand"`
ResponseLength int `bson:"responseLength"`
Ts time.Time `bson:"ts"`
User string `bson:"user"`
WriteConflicts int `bson:"writeConflicts"`
}
func NewExampleQuery(doc SystemProfile) ExampleQuery {
return ExampleQuery{
Ns: doc.Ns,
Op: doc.Op,
Query: doc.Query,
Command: doc.Command,
OriginatingCommand: doc.OriginatingCommand,
UpdateObj: doc.UpdateObj,
}
}
// ExampleQuery is a subset of SystemProfile
type ExampleQuery struct {
Ns string `bson:"ns" json:"ns"`
Op string `bson:"op" json:"op"`
Query BsonD `bson:"query,omitempty" json:"query,omitempty"`
Command BsonD `bson:"command,omitempty" json:"command,omitempty"`
OriginatingCommand BsonD `bson:"originatingCommand,omitempty" json:"originatingCommand,omitempty"`
UpdateObj BsonD `bson:"updateobj,omitempty" json:"updateobj,omitempty"`
}
func (self ExampleQuery) Db() string {
ns := strings.SplitN(self.Ns, ".", 2)
if len(ns) > 0 {
return ns[0]
}
return ""
}
// ExplainCmd returns bson.D ready to use in https://godoc.org/labix.org/v2/mgo#Database.Run
func (self ExampleQuery) ExplainCmd() bson.D {
cmd := self.Command
switch self.Op {
case "query":
if cmd.Len() == 0 {
cmd = self.Query
}
// MongoDB 2.6:
//
// "query" : {
// "query" : {
//
// },
// "$explain" : true
// },
if _, ok := cmd.Map()["$explain"]; ok {
cmd = BsonD{
{"explain", ""},
}
break
}
if cmd.Len() == 0 || cmd[0].Key != "find" {
var filter interface{}
if cmd.Len() > 0 && cmd[0].Key == "query" {
filter = cmd[0].Value
} else {
filter = cmd
}
coll := ""
s := strings.SplitN(self.Ns, ".", 2)
if len(s) == 2 {
coll = s[1]
}
cmd = BsonD{
{"find", coll},
{"filter", filter},
}
} else {
for i := range cmd {
switch cmd[i].Key {
// PMM-1905: Drop "ntoreturn" if it's negative.
case "ntoreturn":
// If it's non-negative, then we are fine, continue to next param.
if cmd[i].Value.(int64) >= 0 {
continue
}
fallthrough
// Drop $db as it is not supported in MongoDB 3.0.
case "$db":
if len(cmd)-1 == i {
cmd = cmd[:i]
} else {
cmd = append(cmd[:i], cmd[i+1:]...)
}
break
}
}
}
case "update":
s := strings.SplitN(self.Ns, ".", 2)
coll := ""
if len(s) == 2 {
coll = s[1]
}
if cmd.Len() == 0 {
cmd = BsonD{
{Key: "q", Value: self.Query},
{Key: "u", Value: self.UpdateObj},
}
}
cmd = BsonD{
{Key: "update", Value: coll},
{Key: "updates", Value: []interface{}{cmd}},
}
case "remove":
s := strings.SplitN(self.Ns, ".", 2)
coll := ""
if len(s) == 2 {
coll = s[1]
}
if cmd.Len() == 0 {
cmd = BsonD{
{Key: "q", Value: self.Query},
// we can't determine if limit was 1 or 0 so we assume 0
{Key: "limit", Value: 0},
}
}
cmd = BsonD{
{Key: "delete", Value: coll},
{Key: "deletes", Value: []interface{}{cmd}},
}
case "insert":
if cmd.Len() == 0 {
cmd = self.Query
}
if cmd.Len() == 0 || cmd[0].Key != "insert" {
coll := ""
s := strings.SplitN(self.Ns, ".", 2)
if len(s) == 2 {
coll = s[1]
}
cmd = BsonD{
{"insert", coll},
}
}
case "getmore":
if self.OriginatingCommand.Len() > 0 {
cmd = self.OriginatingCommand
for i := range cmd {
// drop $db param as it is not supported in MongoDB 3.0
if cmd[i].Key == "$db" {
if len(cmd)-1 == i {
cmd = cmd[:i]
} else {
cmd = append(cmd[:i], cmd[i+1:]...)
}
break
}
}
} else {
cmd = BsonD{
{Key: "getmore", Value: ""},
}
}
case "command":
if cmd.Len() == 0 || cmd[0].Key != "group" {
break
}
if group, ok := cmd[0].Value.(BsonD); ok {
for i := range group {
// for MongoDB <= 3.2
// "$reduce" : function () {}
// It is then Unmarshaled as empty value, so in essence not working
//
// for MongoDB >= 3.4
// "$reduce" : {
// "code" : "function () {}"
// }
// It is then properly Unmarshaled but then explain fails with "not code"
//
// The $reduce function shouldn't affect explain execution plan (e.g. what indexes are picked)
// so we ignore it for now until we find better way to handle this issue
if group[i].Key == "$reduce" {
group[i].Value = "{}"
cmd[0].Value = group
break
}
}
}
}
return bson.D{
{
Key: "explain",
Value: cmd,
},
}
}