@cryptotaxi247 / netdata-1 / commits / cadedd2ac

mongodb_plugin: replica set heartbeat latency chart added

Ilya committed Feb 24, 2017 at 01:06 UTC cadedd2acd028d6d6d1a129dc3161fc14d7db813
1 file changed +93 -47
python.d/mongodb.chart.py
+93 -47
@@ -4,6 +4,7 @@
4
5 from base import SimpleService
6 from copy import deepcopy
7 +from datetime import datetime
8 try:
9 from pymongo import MongoClient
10 from pymongo.errors import PyMongoError
@@ -38,14 +39,14 @@ ORDER = ['read_operations', 'write_operations', 'active_clients', 'journaling_tr
39
40 CHARTS = {
41 'read_operations': {
41 - 'options': [None, "Received read requests", "requests/s", 'throughput metrics',
42 + 'options': [None, 'Received read requests', 'requests/s', 'throughput metrics',
43 'mongodb.read_operations', 'line'],
44 'lines': [
45 ['readWriteOper_query', 'query', 'incremental'],
46 ['readWriteOper_getmore', 'getmore', 'incremental']
47 ]},
48 'write_operations': {
48 - 'options': [None, "Received write requests", "requests/s", 'throughput metrics',
49 + 'options': [None, 'Received write requests', 'requests/s', 'throughput metrics',
50 'mongodb.write_operations', 'line'],
51 'lines': [
52 ['readWriteOper_insert', 'insert', 'incremental'],
@@ -53,99 +54,99 @@ CHARTS = {
54 ['readWriteOper_delete', 'delete', 'incremental']
55 ]},
56 'active_clients': {
56 - 'options': [None, "Clients with read or write operations in progress or queued", "clients",
57 + 'options': [None, 'Clients with read or write operations in progress or queued', 'clients',
58 'throughput metrics', 'mongodb.active_clients', 'line'],
59 'lines': [
60 ['activeClients_readers', 'readers', 'absolute'],
61 ['activeClients_writers', 'writers', 'absolute']
62 ]},
63 'journaling_transactions': {
63 - 'options': [None, "Transactions that have been written to the journal", "commits",
64 + 'options': [None, 'Transactions that have been written to the journal', 'commits',
65 'database performance', 'mongodb.journaling_transactions', 'line'],
66 'lines': [
67 ['journalTrans_commits', 'commits', 'absolute']
68 ]},
69 'journaling_volume': {
69 - 'options': [None, "Volume of data written to the journal", "MB", 'database performance',
70 + 'options': [None, 'Volume of data written to the journal', 'MB', 'database performance',
71 'mongodb.journaling_volume', 'line'],
72 'lines': [
73 ['journalTrans_journaled', 'volume', 'absolute', 1, 100]
74 ]},
75 'background_flush_average': {
75 - 'options': [None, "Average time taken by flushes to execute", "ms", 'database performance',
76 + 'options': [None, 'Average time taken by flushes to execute', 'ms', 'database performance',
77 'mongodb.background_flush_average', 'line'],
78 'lines': [
79 ['background_flush_average', 'time', 'absolute', 1, 100]
80 ]},
81 'background_flush_last': {
81 - 'options': [None, "Time taken by the last flush operation to execute", "ms", 'database performance',
82 + 'options': [None, 'Time taken by the last flush operation to execute', 'ms', 'database performance',
83 'mongodb.background_flush_last', 'line'],
84 'lines': [
85 ['background_flush_last', 'time', 'absolute', 1, 100]
86 ]},
87 'background_flush_rate': {
87 - 'options': [None, "Flushes rate", "flushes", 'database performance', 'mongodb.background_flush_rate', 'line'],
88 + 'options': [None, 'Flushes rate', 'flushes', 'database performance', 'mongodb.background_flush_rate', 'line'],
89 'lines': [
90 ['background_flush_rate', 'flushes', 'incremental', 1, 1]
91 ]},
92 'wiredtiger_read': {
92 - 'options': [None, "Read tickets in use and remaining", "tickets", 'database performance',
93 + 'options': [None, 'Read tickets in use and remaining', 'tickets', 'database performance',
94 'mongodb.wiredtiger_read', 'stacked'],
95 'lines': [
96 ['wiredTigerRead_available', 'available', 'absolute', 1, 1],
97 ['wiredTigerRead_out', 'inuse', 'absolute', 1, 1]
98 ]},
99 'wiredtiger_write': {
99 - 'options': [None, "Write tickets in use and remaining", "tickets", 'database performance',
100 + 'options': [None, 'Write tickets in use and remaining', 'tickets', 'database performance',
101 'mongodb.wiredtiger_write', 'stacked'],
102 'lines': [
103 ['wiredTigerWrite_available', 'available', 'absolute', 1, 1],
104 ['wiredTigerWrite_out', 'inuse', 'absolute', 1, 1]
105 ]},
106 'cursors': {
106 - 'options': [None, "Currently openned cursors, cursors with timeout disabled and timed out cursors",
107 - "cursors", 'database performance', 'mongodb.cursors', 'stacked'],
107 + 'options': [None, 'Currently openned cursors, cursors with timeout disabled and timed out cursors',
108 + 'cursors', 'database performance', 'mongodb.cursors', 'stacked'],
109 'lines': [
110 ['cursor_total', 'openned', 'absolute', 1, 1],
111 ['cursor_noTimeout', 'notimeout', 'absolute', 1, 1],
112 ['cursor_timedOut', 'timedout', 'incremental', 1, 1]
113 ]},
114 'connections': {
114 - 'options': [None, "Currently connected clients and unused connections", "connections",
115 + 'options': [None, 'Currently connected clients and unused connections', 'connections',
116 'resource utilization', 'mongodb.connections', 'stacked'],
117 'lines': [
118 ['connections_available', 'unused', 'absolute', 1, 1],
119 ['connections_current', 'connected', 'absolute', 1, 1]
120 ]},
121 'memory': {
121 - 'options': [None, "Memory metrics", "MB", 'resource utilization', 'mongodb.memory', 'stacked'],
122 + 'options': [None, 'Memory metrics', 'MB', 'resource utilization', 'mongodb.memory', 'stacked'],
123 'lines': [
124 ['memory_virtual', 'virtual', 'absolute', 1, 1],
125 ['memory_resident', 'resident', 'absolute', 1, 1],
126 ['memory_mapped', 'mapped', 'absolute', 1, 1]
127 ]},
128 'page_faults': {
128 - 'options': [None, "Number of times MongoDB had to fetch data from disk", "request/s",
129 + 'options': [None, 'Number of times MongoDB had to fetch data from disk', 'request/s',
130 'resource utilization', 'mongodb.page_faults', 'line'],
131 'lines': [
132 ['page_faults', 'page_faults', 'incremental', 1, 1]
133 ]},
134 'queued_requests': {
134 - 'options': [None, "Currently queued read and wrire requests", "requests", 'resource saturation',
135 + 'options': [None, 'Currently queued read and wrire requests', 'requests', 'resource saturation',
136 'mongodb.queued_requests', 'line'],
137 'lines': [
138 ['currentQueue_readers', 'readers', 'absolute', 1, 1],
139 ['currentQueue_writers', 'writers', 'absolute', 1, 1]
140 ]},
141 'record_moves': {
141 - 'options': [None, "Number of times documents had to be moved on-disk", "number",
142 + 'options': [None, 'Number of times documents had to be moved on-disk', 'number',
143 'resource saturation', 'mongodb.record_moves', 'line'],
144 'lines': [
145 ['record_moves', 'moves', 'incremental', 1, 1]
146 ]},
147 'asserts': {
147 - 'options': [None, "Number of message, warning, regular, corresponding to errors generated"
148 - " by users assertions raised", "number", 'errors (asserts)', 'mongodb.asserts', 'line'],
148 + 'options': [None, 'Number of message, warning, regular, corresponding to errors generated'
149 + ' by users assertions raised', 'number', 'errors (asserts)', 'mongodb.asserts', 'line'],
150 'lines': [
151 ['errors_msg', 'msg', 'incremental', 1, 1],
152 ['errors_warning', 'warning', 'incremental', 1, 1],
@@ -153,32 +154,32 @@ CHARTS = {
154 ['errors_user', 'user', 'incremental', 1, 1]
155 ]},
156 'wiredtiger_cache': {
156 - 'options': [None, "Amount of space taken by cached data and by dirty data in the cache",
157 - "KB", 'resource utilization', 'mongodb.wiredtiger_cache', 'stacked'],
157 + 'options': [None, 'Amount of space taken by cached data and by dirty data in the cache',
158 + 'KB', 'resource utilization', 'mongodb.wiredtiger_cache', 'stacked'],
159 'lines': [
160 ['wiredTiger_bytes_in_cache', 'cached', 'absolute', 1, 1024],
161 ['wiredTiger_dirty_in_cache', 'dirty', 'absolute', 1, 1024]
162 ]},
163 'wiredtiger_pages_evicted': {
163 - 'options': [None, "Pages evicted from the cache",
164 - "pages", 'resource utilization', 'mongodb.wiredtiger_pages_evicted', 'stacked'],
164 + 'options': [None, 'Pages evicted from the cache',
165 + 'pages', 'resource utilization', 'mongodb.wiredtiger_pages_evicted', 'stacked'],
166 'lines': [
167 ['wiredTiger_unmodified_pages_evicted', 'unmodified', 'absolute', 1, 1],
168 ['wiredTiger_modified_pages_evicted', 'modified', 'absolute', 1, 1]
169 ]},
170 'dbstats_objects': {
170 - 'options': [None, "Number of documents in the database among all the collections", "documents",
171 + 'options': [None, 'Number of documents in the database among all the collections', 'documents',
172 'storage size metrics', 'mongodb.dbstats_objects', 'stacked'],
173 'lines': [
174 ]},
175 'tcmalloc_generic': {
175 - 'options': [None, "Tcmalloc generic metrics", "MB", 'tcmalloc', 'mongodb.tcmalloc_generic', 'stacked'],
176 + 'options': [None, 'Tcmalloc generic metrics', 'MB', 'tcmalloc', 'mongodb.tcmalloc_generic', 'stacked'],
177 'lines': [
178 ['current_allocated_bytes', 'allocated', 'absolute', 1, 1048576],
179 ['heap_size', 'heap_size', 'absolute', 1, 1048576]
180 ]},
181 'tcmalloc_metrics': {
181 - 'options': [None, "Tcmalloc metrics", "KB", 'tcmalloc', 'mongodb.tcmalloc_metrics', 'stacked'],
182 + 'options': [None, 'Tcmalloc metrics', 'KB', 'tcmalloc', 'mongodb.tcmalloc_metrics', 'stacked'],
183 'lines': [
184 ['central_cache_free_bytes', 'central_cache_free', 'absolute', 1, 1024],
185 ['current_total_thread_cache_bytes', 'current_total_thread_cache', 'absolute', 1, 1024],
@@ -188,7 +189,7 @@ CHARTS = {
189 ['transfer_cache_free_bytes', 'transfer_cache_free', 'absolute', 1, 1024]
190 ]},
191 'command_total_rate': {
191 - 'options': [None, "Commands total rate", "commands/s", 'commands', 'mongodb.command_total_rate', 'stacked'],
192 + 'options': [None, 'Commands total rate', 'commands/s', 'commands', 'mongodb.command_total_rate', 'stacked'],
193 'lines': [
194 ['count_total', 'count', 'incremental', 1, 1],
195 ['createIndexes_total', 'createIndexes', 'incremental', 1, 1],
@@ -199,7 +200,7 @@ CHARTS = {
200 ['update_total', 'update', 'incremental', 1, 1]
201 ]},
202 'command_failed_rate': {
202 - 'options': [None, "Commands failed rate", "commands/s", 'commands', 'mongodb.command_failed_rate', 'stacked'],
203 + 'options': [None, 'Commands failed rate', 'commands/s', 'commands', 'mongodb.command_failed_rate', 'stacked'],
204 'lines': [
205 ['count_failed', 'count', 'incremental', 1, 1],
206 ['createIndexes_failed', 'createIndexes', 'incremental', 1, 1],
@@ -225,19 +226,23 @@ class Service(SimpleService):
226 if not PYMONGO:
227 self.error('Pymongo module is needed to use mongodb.chart.py')
228 return False
228 -
229 self.connection, server_status, error = self._create_connection()
230 if error:
231 self.error(error)
232 return False
233
234 self.repl = 'repl' in server_status
235 - self.databases = self.connection.database_names()
236 - self._create_charts(server_status)
235 + try:
236 + self.databases = self.connection.database_names()
237 + except PyMongoError as error:
238 + self.databases = list()
239 + self.info('Can\'t collect databases: %s' % str(error))
240 +
241 + self.create_charts_(server_status)
242
243 return True
244
240 - def _create_charts(self, server_status):
245 + def create_charts_(self, server_status):
246
247 self.order = ORDER[:]
248 self.definitions = deepcopy(CHARTS)
@@ -253,6 +258,7 @@ class Service(SimpleService):
258 if not self.ss['backgroundFlushing']:
259 self.order.remove('background_flush_average')
260 self.order.remove('background_flush_last')
261 + self.order.remove('background_flush_rate')
262
263 if not self.ss['cursor']:
264 self.order.remove('cursors')
@@ -273,7 +279,7 @@ class Service(SimpleService):
279 for dbase in self.databases:
280 self.order.append('_'.join([dbase, 'dbstats']))
281 self.definitions['_'.join([dbase, 'dbstats'])] = {
276 - 'options': [None, "%s: size of all documents, indexes, extents" % dbase, "KB",
282 + 'options': [None, '%s: size of all documents, indexes, extents' % dbase, 'KB',
283 'storage size metrics', 'mongodb.dbstats', 'line'],
284 'lines': [
285 ['_'.join([dbase, 'dataSize']), 'documents', 'absolute', 1, 1024],
@@ -283,19 +289,38 @@ class Service(SimpleService):
289 self.definitions['dbstats_objects']['lines'].append(['_'.join([dbase, 'objects']), dbase, 'absolute'])
290
291 if server_status.get('repl'):
286 - hosts = server_status['repl']['hosts']
287 - for host in hosts:
292 + def create_heartbeat_lines(hosts):
293 + lines = list()
294 + for host in hosts:
295 + dim_id = '_'.join([host, 'heartbeat_lag'])
296 + lines.append([dim_id, host, 'absolute', 1, 1000])
297 + return lines
298 +
299 + def create_state_lines(states):
300 + lines = list()
301 + for state, description in states:
302 + dim_id = '_'.join([host, 'state', state])
303 + lines.append([dim_id, description, 'absolute', 1, 1])
304 + return lines
305 +
306 + all_hosts = server_status['repl']['hosts']
307 + this_host = server_status['repl']['me']
308 + other_hosts = [host for host in all_hosts if host != this_host]
309 +
310 + # Create "heartbeat delay" charts
311 + self.order.append('heartbeat_delay')
312 + self.definitions['heartbeat_delay'] = {
313 + 'options': [None, 'Latency between this node and replica set members (lastHeartbeatRecv)',
314 + 'seconds', 'replication', 'mongodb.replication_heartbeat_delay', 'stacked'],
315 + 'lines': create_heartbeat_lines(other_hosts)}
316 + # Create "replica set members state" chart
317 + for host in all_hosts:
318 chart_name = '_'.join([host, 'state'])
319 self.order.append(chart_name)
320 self.definitions[chart_name] = {
291 - 'options': [None, "%s state" % host, "state",
321 + 'options': [None, '%s state' % host, 'state',
322 'replication', 'mongodb.replication_state', 'line'],
293 - 'lines': [
294 - ]}
295 - for state, description in REPLSET_STATES:
296 - self.definitions[chart_name]['lines'].append(['_'.join([host, 'state', state]), description, 'absolute', 1, 1])
297 -
298 -
323 + 'lines': create_state_lines(REPLSET_STATES)}
324
325 def _get_raw_data(self):
326 raw_data = dict()
@@ -316,6 +341,9 @@ class Service(SimpleService):
341 return raw_data
342
343 def get_dbstats_(self):
344 + if not self.databases:
345 + return None
346 +
347 raw_data = dict()
348 raw_data['dbStats'] = dict()
349 try:
@@ -349,8 +377,9 @@ class Service(SimpleService):
377
378 to_netdata = dict()
379 serverStatus = raw_data['serverStatus']
352 - dbStats = raw_data['dbStats']
380 + dbStats = raw_data.get('dbStats')
381 replSetGetStatus = raw_data.get('replSetGetStatus')
382 + utc_now = datetime.utcnow()
383
384 # serverStatus
385 to_netdata.update(update_dict_key(serverStatus['opcounters'], 'readWriteOper'))
@@ -396,17 +425,27 @@ class Service(SimpleService):
425 to_netdata.update(update_dict_key(serverStatus['metrics']['commands'][elem], elem))
426
427 # dbStats
399 - for dbase in dbStats:
400 - to_netdata.update(update_dict_key(dbStats[dbase], dbase))
428 + if dbStats:
429 + for dbase in dbStats:
430 + to_netdata.update(update_dict_key(dbStats[dbase], dbase))
431
432 # replSetGetStatus
433 if replSetGetStatus:
434 + other_hosts = list()
435 members = replSetGetStatus['members']
436 for member in members:
437 + if not member.get('self'):
438 + other_hosts.append(member)
439 + # Replica set members state
440 for elem in REPLSET_STATES:
441 state = elem[0]
442 to_netdata.update({'_'.join([member['name'], 'state', state]): 0})
443 to_netdata.update({'_'.join([member['name'], 'state', str(member['state'])]): member['state']})
444 + # Heartbeat lag calculation
445 + for other in other_hosts:
446 + if other['lastHeartbeatRecv'] != datetime(1970, 1, 1, 0, 0):
447 + node = other['name'] + '_heartbeat_lag'
448 + to_netdata[node] = int(lag_calculation(utc_now - other['lastHeartbeatRecv']) * 1000)
449
450 return to_netdata
451
@@ -418,8 +457,8 @@ class Service(SimpleService):
457 connection = MongoClient(**conn_vars)
458 if self.user and self.password:
459 connection.admin.authenticate(name=self.user, password=self.password)
421 - # elif self.user:
422 - # connection.admin.authenticate(name=self.user, mechanism='MONGODB-X509')
460 + # elif self.user:
461 + # connection.admin.authenticate(name=self.user, mechanism='MONGODB-X509')
462 server_status = connection.admin.command('serverStatus')
463 except PyMongoError as error:
464 return None, None, str(error)
@@ -437,3 +476,10 @@ def int_or_float(value):
476
477 def in_server_status(elem, server_status):
478 return elem in server_status or elem in server_status['metrics']
479 +
480 +
481 +def lag_calculation(lag):
482 + if hasattr(lag, 'total_seconds'):
483 + return lag.total_seconds()
484 + else:
485 + return (lag.microseconds + (lag.seconds + lag.days * 24 * 3600) * 10 ** 6) / 10.0 ** 6