// NewStatLine constructs a StatLine object from two ServerStatus objects. func NewStatLine(oldStat, newStat ServerStatus, key string, all bool, sampleSecs int64) *StatLine { returnVal := &StatLine{ Key: key, Host: newStat.Host, Mapped: -1, Virtual: -1, Resident: -1, NonMapped: -1, Faults: -1, } // set the storage engine appropriately if newStat.StorageEngine != nil && newStat.StorageEngine["name"] != "" { returnVal.StorageEngine = newStat.StorageEngine["name"] } else { returnVal.StorageEngine = "mmapv1" } if newStat.Opcounters != nil && oldStat.Opcounters != nil { returnVal.Insert = diff(newStat.Opcounters.Insert, oldStat.Opcounters.Insert, sampleSecs) returnVal.Query = diff(newStat.Opcounters.Query, oldStat.Opcounters.Query, sampleSecs) returnVal.Update = diff(newStat.Opcounters.Update, oldStat.Opcounters.Update, sampleSecs) returnVal.Delete = diff(newStat.Opcounters.Delete, oldStat.Opcounters.Delete, sampleSecs) returnVal.GetMore = diff(newStat.Opcounters.GetMore, oldStat.Opcounters.GetMore, sampleSecs) returnVal.Command = diff(newStat.Opcounters.Command, oldStat.Opcounters.Command, sampleSecs) } if newStat.OpcountersRepl != nil && oldStat.OpcountersRepl != nil { returnVal.InsertR = diff(newStat.OpcountersRepl.Insert, oldStat.OpcountersRepl.Insert, sampleSecs) returnVal.QueryR = diff(newStat.OpcountersRepl.Query, oldStat.OpcountersRepl.Query, sampleSecs) returnVal.UpdateR = diff(newStat.OpcountersRepl.Update, oldStat.OpcountersRepl.Update, sampleSecs) returnVal.DeleteR = diff(newStat.OpcountersRepl.Delete, oldStat.OpcountersRepl.Delete, sampleSecs) returnVal.GetMoreR = diff(newStat.OpcountersRepl.GetMore, oldStat.OpcountersRepl.GetMore, sampleSecs) returnVal.CommandR = diff(newStat.OpcountersRepl.Command, oldStat.OpcountersRepl.Command, sampleSecs) } returnVal.CacheDirtyPercent = -1 returnVal.CacheUsedPercent = -1 if newStat.WiredTiger != nil && oldStat.WiredTiger != nil { returnVal.Flushes = newStat.WiredTiger.Transaction.TransCheckpoints - oldStat.WiredTiger.Transaction.TransCheckpoints returnVal.CacheDirtyPercent = float64(newStat.WiredTiger.Cache.TrackedDirtyBytes) / float64(newStat.WiredTiger.Cache.MaxBytesConfigured) returnVal.CacheUsedPercent = float64(newStat.WiredTiger.Cache.CurrentCachedBytes) / float64(newStat.WiredTiger.Cache.MaxBytesConfigured) } else if newStat.BackgroundFlushing != nil && oldStat.BackgroundFlushing != nil { returnVal.Flushes = newStat.BackgroundFlushing.Flushes - oldStat.BackgroundFlushing.Flushes } returnVal.Time = newStat.SampleTime returnVal.IsMongos = (newStat.ShardCursorType != nil || strings.HasPrefix(newStat.Process, MongosProcess)) if util.IsTruthy(oldStat.Mem.Supported) { if !returnVal.IsMongos { returnVal.Mapped = newStat.Mem.Mapped } returnVal.Virtual = newStat.Mem.Virtual returnVal.Resident = newStat.Mem.Resident if !returnVal.IsMongos && all { returnVal.NonMapped = newStat.Mem.Virtual - newStat.Mem.Mapped } } if newStat.Repl != nil { setName, isReplSet := newStat.Repl.SetName.(string) if isReplSet { returnVal.ReplSetName = setName } if util.IsTruthy(newStat.Repl.IsMaster) { returnVal.NodeType = "PRI" } else if util.IsTruthy(newStat.Repl.Secondary) { returnVal.NodeType = "SEC" } else if util.IsTruthy(newStat.Repl.IsReplicaSet) { returnVal.NodeType = "REC" } else if util.IsTruthy(newStat.Repl.ArbiterOnly) { returnVal.NodeType = "ARB" } else if util.SliceContains(newStat.Repl.Passives, newStat.Repl.Me) { returnVal.NodeType = "PSV" } else if isReplSet { returnVal.NodeType = "UNK" } else { returnVal.NodeType = "SLV" } } else if returnVal.IsMongos { returnVal.NodeType = "RTR" } if oldStat.ExtraInfo != nil && newStat.ExtraInfo != nil && oldStat.ExtraInfo.PageFaults != nil && newStat.ExtraInfo.PageFaults != nil { returnVal.Faults = diff(*(newStat.ExtraInfo.PageFaults), *(oldStat.ExtraInfo.PageFaults), sampleSecs) } if !returnVal.IsMongos && oldStat.Locks != nil && oldStat.Locks != nil { globalCheck, hasGlobal := oldStat.Locks["Global"] if hasGlobal && globalCheck.AcquireCount != nil { // This appears to be a 3.0+ server so the data in these fields do *not* refer to // actual namespaces and thus we can't compute lock %. returnVal.HighestLocked = nil // Check if it's a 3.0+ MMAP server so we can still compute collection locks collectionCheck, hasCollection := oldStat.Locks["Collection"] if hasCollection && collectionCheck.AcquireWaitCount != nil { readWaitCountDiff := newStat.Locks["Collection"].AcquireWaitCount.Read - oldStat.Locks["Collection"].AcquireWaitCount.Read readTotalCountDiff := newStat.Locks["Collection"].AcquireCount.Read - oldStat.Locks["Collection"].AcquireCount.Read writeWaitCountDiff := newStat.Locks["Collection"].AcquireWaitCount.Write - oldStat.Locks["Collection"].AcquireWaitCount.Write writeTotalCountDiff := newStat.Locks["Collection"].AcquireCount.Write - oldStat.Locks["Collection"].AcquireCount.Write readAcquireTimeDiff := newStat.Locks["Collection"].TimeAcquiringMicros.Read - oldStat.Locks["Collection"].TimeAcquiringMicros.Read writeAcquireTimeDiff := newStat.Locks["Collection"].TimeAcquiringMicros.Write - oldStat.Locks["Collection"].TimeAcquiringMicros.Write returnVal.CollectionLocks = &CollectionLockStatus{ ReadAcquireWaitsPercentage: percentageInt64(readWaitCountDiff, readTotalCountDiff), WriteAcquireWaitsPercentage: percentageInt64(writeWaitCountDiff, writeTotalCountDiff), ReadAcquireTimeMicros: averageInt64(readAcquireTimeDiff, readWaitCountDiff), WriteAcquireTimeMicros: averageInt64(writeAcquireTimeDiff, writeWaitCountDiff), } } } else { prevLocks := parseLocks(oldStat) curLocks := parseLocks(newStat) lockdiffs := computeLockDiffs(prevLocks, curLocks) if len(lockdiffs) == 0 { if newStat.GlobalLock != nil { returnVal.HighestLocked = &LockStatus{ DBName: "", Percentage: percentageInt64(newStat.GlobalLock.LockTime, newStat.GlobalLock.TotalTime), Global: true, } } } else { // Get the entry with the highest lock highestLocked := lockdiffs[len(lockdiffs)-1] var timeDiffMillis int64 timeDiffMillis = newStat.UptimeMillis - oldStat.UptimeMillis lockToReport := highestLocked.Writes // if the highest locked namespace is not '.' if highestLocked.Namespace != "." { for _, namespaceLockInfo := range lockdiffs { if namespaceLockInfo.Namespace == "." { lockToReport += namespaceLockInfo.Writes } } } // lock data is in microseconds and uptime is in milliseconds - so // divide by 1000 so that they units match lockToReport /= 1000 returnVal.HighestLocked = &LockStatus{ DBName: highestLocked.Namespace, Percentage: percentageInt64(lockToReport, timeDiffMillis), Global: false, } } } } else { returnVal.HighestLocked = nil } if newStat.GlobalLock != nil { hasWT := (newStat.WiredTiger != nil && oldStat.WiredTiger != nil) //If we have wiredtiger stats, use those instead if newStat.GlobalLock.CurrentQueue != nil { if hasWT { returnVal.QueuedReaders = newStat.GlobalLock.CurrentQueue.Readers + newStat.GlobalLock.ActiveClients.Readers - newStat.WiredTiger.Concurrent.Read.Out returnVal.QueuedWriters = newStat.GlobalLock.CurrentQueue.Writers + newStat.GlobalLock.ActiveClients.Writers - newStat.WiredTiger.Concurrent.Write.Out if returnVal.QueuedReaders < 0 { returnVal.QueuedReaders = 0 } if returnVal.QueuedWriters < 0 { returnVal.QueuedWriters = 0 } } else { returnVal.QueuedReaders = newStat.GlobalLock.CurrentQueue.Readers returnVal.QueuedWriters = newStat.GlobalLock.CurrentQueue.Writers } } if hasWT { returnVal.ActiveReaders = newStat.WiredTiger.Concurrent.Read.Out returnVal.ActiveWriters = newStat.WiredTiger.Concurrent.Write.Out } else if newStat.GlobalLock.ActiveClients != nil { returnVal.ActiveReaders = newStat.GlobalLock.ActiveClients.Readers returnVal.ActiveWriters = newStat.GlobalLock.ActiveClients.Writers } } if oldStat.Network != nil && newStat.Network != nil { returnVal.NetIn = diff(newStat.Network.BytesIn, oldStat.Network.BytesIn, sampleSecs) returnVal.NetOut = diff(newStat.Network.BytesOut, oldStat.Network.BytesOut, sampleSecs) } if newStat.Connections != nil { returnVal.NumConnections = newStat.Connections.Current } return returnVal }
//NewStatLine constructs a StatLine object from two ServerStatus objects. func NewStatLine(oldStat, newStat ServerStatus, all bool) *StatLine { returnVal := &StatLine{ Host: newStat.Host, Mapped: -1, Virtual: -1, Resident: -1, NonMapped: -1, Faults: -1, } if newStat.Opcounters != nil && oldStat.Opcounters != nil { returnVal.Insert = newStat.Opcounters.Insert - oldStat.Opcounters.Insert returnVal.Query = newStat.Opcounters.Query - oldStat.Opcounters.Query returnVal.Update = newStat.Opcounters.Update - oldStat.Opcounters.Update returnVal.Delete = newStat.Opcounters.Delete - oldStat.Opcounters.Delete returnVal.GetMore = newStat.Opcounters.GetMore - oldStat.Opcounters.GetMore returnVal.Command = newStat.Opcounters.Command - oldStat.Opcounters.Command } if newStat.BackgroundFlushing != nil && oldStat.BackgroundFlushing != nil { returnVal.Flushes = newStat.BackgroundFlushing.Flushes - oldStat.BackgroundFlushing.Flushes } returnVal.Time = newStat.SampleTime returnVal.IsMongos = (newStat.ShardCursorType != nil || newStat.Process == MongosProcess) if util.IsTruthy(oldStat.Mem.Supported) { if !returnVal.IsMongos { returnVal.Mapped = newStat.Mem.Mapped } returnVal.Virtual = newStat.Mem.Virtual returnVal.Resident = newStat.Mem.Resident if !returnVal.IsMongos && all { returnVal.NonMapped = newStat.Mem.Virtual - newStat.Mem.Mapped } } if newStat.Repl != nil { setName, isReplSet := newStat.Repl.SetName.(string) if isReplSet { returnVal.ReplSetName = setName } if util.IsTruthy(newStat.Repl.IsMaster) { returnVal.NodeType = "PRI" } else if util.IsTruthy(newStat.Repl.Secondary) { returnVal.NodeType = "SEC" } else if util.IsTruthy(newStat.Repl.IsReplicaSet) { returnVal.NodeType = "REC" } else if util.IsTruthy(newStat.Repl.ArbiterOnly) { returnVal.NodeType = "ARB" } else if util.SliceContains(newStat.Repl.Me, newStat.Repl.Passives) { returnVal.NodeType = "PSV" } else if isReplSet { returnVal.NodeType = "UNK" } else { returnVal.NodeType = "SLV" } } else if returnVal.IsMongos { returnVal.NodeType = "RTR" } if oldStat.ExtraInfo != nil && newStat.ExtraInfo != nil && oldStat.ExtraInfo.PageFaults != nil && newStat.ExtraInfo.PageFaults != nil { returnVal.Faults = *(newStat.ExtraInfo.PageFaults) - *(oldStat.ExtraInfo.PageFaults) } if !returnVal.IsMongos { prevLocks := parseLocks(oldStat) curLocks := parseLocks(newStat) lockdiffs := computeLockDiffs(prevLocks, curLocks) if len(lockdiffs) == 0 { if newStat.GlobalLock != nil { returnVal.HighestLocked = &LockStatus{ DBName: "", Percentage: percentageInt64(newStat.GlobalLock.LockTime, newStat.GlobalLock.TotalTime), Global: true, } } } if len(lockdiffs) > 0 { //Get the entry with the highest lock highestLocked := lockdiffs[len(lockdiffs)-1] //TODO use server uptime since the previous sampling? //var timeDiffMillis int64 //timeDiffMillis = newStat.UptimeMillis - stat.UptimeMillis lockToReport := highestLocked.Writes //if the highest locked namespace is not '.' if highestLocked.Namespace != "." { for _, namespaceLockInfo := range lockdiffs { if namespaceLockInfo.Namespace == "." { lockToReport += namespaceLockInfo.Writes } } } //lock data is in microseconds and uptime is in milliseconds - so //divide by 1000 so that they units match lockToReport /= 1000 returnVal.HighestLocked = &LockStatus{ DBName: highestLocked.Namespace, Percentage: percentageInt64(lockToReport, 1000), Global: false, } } } else { returnVal.HighestLocked = nil } if newStat.GlobalLock != nil { if newStat.GlobalLock.CurrentQueue != nil { returnVal.QueuedReaders = newStat.GlobalLock.CurrentQueue.Readers returnVal.QueuedWriters = newStat.GlobalLock.CurrentQueue.Writers } if newStat.GlobalLock.ActiveClients != nil { returnVal.ActiveReaders = newStat.GlobalLock.ActiveClients.Readers returnVal.ActiveWriters = newStat.GlobalLock.ActiveClients.Writers } } if oldStat.Network != nil && newStat.Network != nil { returnVal.NetIn = newStat.Network.BytesIn - oldStat.Network.BytesIn returnVal.NetOut = newStat.Network.BytesOut - oldStat.Network.BytesOut } if newStat.Connections != nil { returnVal.NumConnections = newStat.Connections.Current } return returnVal }