1 // top.cpp
2 
3 /**
4  *    Copyright (C) 2018-present MongoDB, Inc.
5  *
6  *    This program is free software: you can redistribute it and/or modify
7  *    it under the terms of the Server Side Public License, version 1,
8  *    as published by MongoDB, Inc.
9  *
10  *    This program is distributed in the hope that it will be useful,
11  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *    Server Side Public License for more details.
14  *
15  *    You should have received a copy of the Server Side Public License
16  *    along with this program. If not, see
17  *    <http://www.mongodb.com/licensing/server-side-public-license>.
18  *
19  *    As a special exception, the copyright holders give permission to link the
20  *    code of portions of this program with the OpenSSL library under certain
21  *    conditions as described in each individual source file and distribute
22  *    linked combinations including the program with the OpenSSL library. You
23  *    must comply with the Server Side Public License in all respects for
24  *    all of the code used other than as permitted herein. If you modify file(s)
25  *    with this exception, you may extend this exception to your version of the
26  *    file(s), but you are not obligated to do so. If you do not wish to do so,
27  *    delete this exception statement from your version. If you delete this
28  *    exception statement from all source files in the program, then also delete
29  *    it in the license file.
30  */
31 
32 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
33 
34 #include "mongo/platform/basic.h"
35 
36 #include "mongo/db/stats/top.h"
37 
38 #include "mongo/db/jsobj.h"
39 #include "mongo/db/service_context.h"
40 #include "mongo/util/log.h"
41 
42 namespace mongo {
43 
44 using std::endl;
45 using std::string;
46 using std::stringstream;
47 using std::vector;
48 
49 namespace {
50 
51 const auto getTop = ServiceContext::declareDecoration<Top>();
52 
53 }  // namespace
54 
UsageData(const UsageData & older,const UsageData & newer)55 Top::UsageData::UsageData(const UsageData& older, const UsageData& newer) {
56     // this won't be 100% accurate on rollovers and drop(), but at least it won't be negative
57     time = (newer.time >= older.time) ? (newer.time - older.time) : newer.time;
58     count = (newer.count >= older.count) ? (newer.count - older.count) : newer.count;
59 }
60 
CollectionData(const CollectionData & older,const CollectionData & newer)61 Top::CollectionData::CollectionData(const CollectionData& older, const CollectionData& newer)
62     : total(older.total, newer.total),
63       readLock(older.readLock, newer.readLock),
64       writeLock(older.writeLock, newer.writeLock),
65       queries(older.queries, newer.queries),
66       getmore(older.getmore, newer.getmore),
67       insert(older.insert, newer.insert),
68       update(older.update, newer.update),
69       remove(older.remove, newer.remove),
70       commands(older.commands, newer.commands) {}
71 
72 // static
get(ServiceContext * service)73 Top& Top::get(ServiceContext* service) {
74     return getTop(service);
75 }
76 
record(OperationContext * opCtx,StringData ns,LogicalOp logicalOp,LockType lockType,long long micros,bool command,Command::ReadWriteType readWriteType)77 void Top::record(OperationContext* opCtx,
78                  StringData ns,
79                  LogicalOp logicalOp,
80                  LockType lockType,
81                  long long micros,
82                  bool command,
83                  Command::ReadWriteType readWriteType) {
84     if (ns[0] == '?')
85         return;
86 
87     auto hashedNs = UsageMap::HashedKey(ns);
88     stdx::lock_guard<SimpleMutex> lk(_lock);
89 
90     if ((command || logicalOp == LogicalOp::opQuery) && ns == _lastDropped) {
91         _lastDropped = "";
92         return;
93     }
94 
95     CollectionData& coll = _usage[hashedNs];
96     _record(opCtx, coll, logicalOp, lockType, micros, readWriteType);
97 }
98 
_record(OperationContext * opCtx,CollectionData & c,LogicalOp logicalOp,LockType lockType,long long micros,Command::ReadWriteType readWriteType)99 void Top::_record(OperationContext* opCtx,
100                   CollectionData& c,
101                   LogicalOp logicalOp,
102                   LockType lockType,
103                   long long micros,
104                   Command::ReadWriteType readWriteType) {
105 
106     _incrementHistogram(opCtx, micros, &c.opLatencyHistogram, readWriteType);
107 
108     c.total.inc(micros);
109 
110     if (lockType == LockType::WriteLocked)
111         c.writeLock.inc(micros);
112     else if (lockType == LockType::ReadLocked)
113         c.readLock.inc(micros);
114 
115     switch (logicalOp) {
116         case LogicalOp::opInvalid:
117             // use 0 for unknown, non-specific
118             break;
119         case LogicalOp::opUpdate:
120             c.update.inc(micros);
121             break;
122         case LogicalOp::opInsert:
123             c.insert.inc(micros);
124             break;
125         case LogicalOp::opQuery:
126             c.queries.inc(micros);
127             break;
128         case LogicalOp::opGetMore:
129             c.getmore.inc(micros);
130             break;
131         case LogicalOp::opDelete:
132             c.remove.inc(micros);
133             break;
134         case LogicalOp::opKillCursors:
135             break;
136         case LogicalOp::opCommand:
137             c.commands.inc(micros);
138             break;
139         default:
140             MONGO_UNREACHABLE;
141     }
142 }
143 
collectionDropped(StringData ns,bool databaseDropped)144 void Top::collectionDropped(StringData ns, bool databaseDropped) {
145     stdx::lock_guard<SimpleMutex> lk(_lock);
146     _usage.erase(ns);
147     if (!databaseDropped) {
148         // If a collection drop occurred, there will be a subsequent call to record for this
149         // collection namespace which must be ignored. This does not apply to a database drop.
150         _lastDropped = ns.toString();
151     }
152 }
153 
cloneMap(Top::UsageMap & out) const154 void Top::cloneMap(Top::UsageMap& out) const {
155     stdx::lock_guard<SimpleMutex> lk(_lock);
156     out = _usage;
157 }
158 
append(BSONObjBuilder & b)159 void Top::append(BSONObjBuilder& b) {
160     stdx::lock_guard<SimpleMutex> lk(_lock);
161     _appendToUsageMap(b, _usage);
162 }
163 
_appendToUsageMap(BSONObjBuilder & b,const UsageMap & map) const164 void Top::_appendToUsageMap(BSONObjBuilder& b, const UsageMap& map) const {
165     // pull all the names into a vector so we can sort them for the user
166 
167     vector<string> names;
168     for (UsageMap::const_iterator i = map.begin(); i != map.end(); ++i) {
169         names.push_back(i->first);
170     }
171 
172     std::sort(names.begin(), names.end());
173 
174     for (size_t i = 0; i < names.size(); i++) {
175         BSONObjBuilder bb(b.subobjStart(names[i]));
176 
177         const CollectionData& coll = map.find(names[i])->second;
178 
179         _appendStatsEntry(b, "total", coll.total);
180 
181         _appendStatsEntry(b, "readLock", coll.readLock);
182         _appendStatsEntry(b, "writeLock", coll.writeLock);
183 
184         _appendStatsEntry(b, "queries", coll.queries);
185         _appendStatsEntry(b, "getmore", coll.getmore);
186         _appendStatsEntry(b, "insert", coll.insert);
187         _appendStatsEntry(b, "update", coll.update);
188         _appendStatsEntry(b, "remove", coll.remove);
189         _appendStatsEntry(b, "commands", coll.commands);
190 
191         bb.done();
192     }
193 }
194 
_appendStatsEntry(BSONObjBuilder & b,const char * statsName,const UsageData & map) const195 void Top::_appendStatsEntry(BSONObjBuilder& b, const char* statsName, const UsageData& map) const {
196     BSONObjBuilder bb(b.subobjStart(statsName));
197     bb.appendNumber("time", map.time);
198     bb.appendNumber("count", map.count);
199     bb.done();
200 }
201 
appendLatencyStats(StringData ns,bool includeHistograms,BSONObjBuilder * builder)202 void Top::appendLatencyStats(StringData ns, bool includeHistograms, BSONObjBuilder* builder) {
203     auto hashedNs = UsageMap::HashedKey(ns);
204     stdx::lock_guard<SimpleMutex> lk(_lock);
205     BSONObjBuilder latencyStatsBuilder;
206     _usage[hashedNs].opLatencyHistogram.append(includeHistograms, &latencyStatsBuilder);
207     builder->append("ns", ns);
208     builder->append("latencyStats", latencyStatsBuilder.obj());
209 }
210 
incrementGlobalLatencyStats(OperationContext * opCtx,uint64_t latency,Command::ReadWriteType readWriteType)211 void Top::incrementGlobalLatencyStats(OperationContext* opCtx,
212                                       uint64_t latency,
213                                       Command::ReadWriteType readWriteType) {
214     stdx::lock_guard<SimpleMutex> guard(_lock);
215     _incrementHistogram(opCtx, latency, &_globalHistogramStats, readWriteType);
216 }
217 
appendGlobalLatencyStats(bool includeHistograms,BSONObjBuilder * builder)218 void Top::appendGlobalLatencyStats(bool includeHistograms, BSONObjBuilder* builder) {
219     stdx::lock_guard<SimpleMutex> guard(_lock);
220     _globalHistogramStats.append(includeHistograms, builder);
221 }
222 
_incrementHistogram(OperationContext * opCtx,long long latency,OperationLatencyHistogram * histogram,Command::ReadWriteType readWriteType)223 void Top::_incrementHistogram(OperationContext* opCtx,
224                               long long latency,
225                               OperationLatencyHistogram* histogram,
226                               Command::ReadWriteType readWriteType) {
227     // Only update histogram if operation came from a user.
228     Client* client = opCtx->getClient();
229     if (client->isFromUserConnection() && !client->isInDirectClient()) {
230         histogram->increment(latency, readWriteType);
231     }
232 }
233 }  // namespace mongo
234