diff --git a/plugins/mongodb/mongostat.go b/plugins/mongodb/mongostat.go index 891622bd8..e12d797d0 100644 --- a/plugins/mongodb/mongostat.go +++ b/plugins/mongodb/mongostat.go @@ -208,10 +208,11 @@ type LockStats struct { TimeLockedMicros ReadWriteLockTimes `bson:"timeLockedMicros"` TimeAcquiringMicros ReadWriteLockTimes `bson:"timeAcquiringMicros"` - // AcquireCount is a new field of the lock stats only populated on 3.0 or newer. + // AcquireCount and AcquireWaitCount are new fields of the lock stats only populated on 3.0 or newer. // Typed as a pointer so that if it is nil, mongostat can assume the field is not populated // with real namespace data. - AcquireCount *ReadWriteLockTimes `bson:"acquireCount,omitempty"` + AcquireCount *ReadWriteLockTimes `bson:"acquireCount,omitempty"` + AcquireWaitCount *ReadWriteLockTimes `bson:"acquireWaitCount,omitempty"` } // ExtraInfo stores additional platform specific information. @@ -246,6 +247,8 @@ var StatHeaders = []StatHeader{ {"res", Always}, {"non-mapped", MMAPOnly | AllOnly}, {"faults", MMAPOnly}, + {"lr|lw %", MMAPOnly | AllOnly}, + {"lrt|lwt", MMAPOnly | AllOnly}, {" locked db", Locks}, {"qr|qw", Always}, {"ar|aw", Always}, @@ -276,6 +279,13 @@ func percentageInt64(value, outOf int64) float64 { return 100 * (float64(value) / float64(outOf)) } +func averageInt64(value, outOf int64) int64 { + if value == 0 || outOf == 0 { + return 0 + } + return value / outOf +} + func (slice lockUsages) Len() int { return len(slice) } @@ -288,6 +298,14 @@ func (slice lockUsages) Swap(i, j int) { slice[i], slice[j] = slice[j], slice[i] } +// CollectionLockStatus stores a collection's lock statistics. +type CollectionLockStatus struct { + ReadAcquireWaitsPercentage float64 + WriteAcquireWaitsPercentage float64 + ReadAcquireTimeMicros int64 + WriteAcquireTimeMicros int64 +} + // LockStatus stores a database's lock statistics. type LockStatus struct { DBName string @@ -314,6 +332,9 @@ type StatLine struct { // Opcounter fields Insert, Query, Update, Delete, GetMore, Command int64 + // Collection locks (3.0 mmap only) + CollectionLocks *CollectionLockStatus + // Cache utilization (wiredtiger only) CacheDirtyPercent float64 CacheUsedPercent float64 @@ -467,6 +488,23 @@ func NewStatLine(oldStat, newStat ServerStatus, key string, all bool, sampleSecs // 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)