@cryptotaxi247 / netdata-1 / commits / 31e68fa95

Show locks per database.

Facetoe committed Oct 23, 2016 at 17:44 UTC 31e68fa954f8c42063b8cf5909b2bcdff4c5a06d
1 file changed +99 -63
python.d/postgres.chart.py
+99 -63
@@ -2,6 +2,7 @@
2 # Description: example netdata python.d module
3 # Authors: facetoe, dangtranhoang
4
5 +import re
6 from copy import deepcopy
7
8 import psycopg2
@@ -90,7 +91,16 @@ WHERE
91 schemaname <> 'pg_catalog';
92 """
93 BGWRITER = 'SELECT * FROM pg_stat_bgwriter;'
93 -LOCKS = 'SELECT mode, count(mode) AS count FROM pg_locks GROUP BY mode ORDER BY mode;'
94 +DATABASE_LOCKS = """
95 +SELECT
96 + pg_database.datname as database_name,
97 + mode,
98 + count(mode) AS count
99 +FROM pg_locks
100 + INNER JOIN pg_database ON pg_database.oid = pg_locks.database
101 +GROUP BY datname, mode
102 +ORDER BY datname, mode;
103 +"""
104 REPLICATION = """
105 SELECT
106 client_hostname,
@@ -109,18 +119,29 @@ FROM (
119 ) AS s;
120 """
121
112 -LOCK_MAP = {'AccessExclusiveLock': 'lock_access_exclusive',
113 - 'AccessShareLock': 'lock_access_share',
114 - 'ExclusiveLock': 'lock_exclusive',
115 - 'RowExclusiveLock': 'lock_row_exclusive',
116 - 'RowShareLock': 'lock_row_share',
117 - 'ShareUpdateExclusiveLock': 'lock_update_exclusive_lock',
118 - 'ShareLock': 'lock_share',
119 - 'ShareRowExclusiveLock': 'lock_share_row_exclusive',
120 - 'SIReadLock': 'lock_si_read'}
122 +# LOCK_MAP = {'AccessExclusiveLock': 'lock_access_exclusive',
123 +# 'AccessShareLock': 'lock_access_share',
124 +# 'ExclusiveLock': 'lock_exclusive',
125 +# 'RowExclusiveLock': 'lock_row_exclusive',
126 +# 'RowShareLock': 'lock_row_share',
127 +# 'ShareUpdateExclusiveLock': 'lock_update_exclusive_lock',
128 +# 'ShareLock': 'lock_share',
129 +# 'ShareRowExclusiveLock': 'lock_share_row_exclusive',
130 +# 'SIReadLock': 'lock_si_read'}
131 +LOCK_TYPES = [
132 + 'ExclusiveLock',
133 + 'RowShareLock',
134 + 'SIReadLock',
135 + 'ShareUpdateExclusiveLock',
136 + 'AccessExclusiveLock',
137 + 'AccessShareLock',
138 + 'ShareRowExclusiveLock',
139 + 'ShareLock',
140 + 'RowExclusiveLock'
141 +]
142
143 ORDER = ['db_stat_transactions', 'db_stat_tuple_read', 'db_stat_tuple_returned', 'db_stat_tuple_write',
123 - 'backend_process', 'index_count', 'index_size', 'table_count', 'table_size', 'locks', 'wal', 'operations_heap',
144 + 'backend_process', 'index_count', 'index_size', 'table_count', 'table_size', 'wal', 'operations_heap',
145 'operations_index', 'operations_toast', 'operations_toast_index', 'background_writer']
146
147 CHARTS = {
@@ -176,19 +197,6 @@ CHARTS = {
197 'lines': [
198 ['table_size', 'Size', 'absolute', 1, 1024 * 1024]
199 ]},
179 - 'locks': {
180 - 'options': [None, 'Table size', 'Count', 'Locks', 'postgres.locks', 'line'],
181 - 'lines': [
182 - ['lock_access_exclusive', 'Access Exclusive', 'absolute'],
183 - ['lock_access_share', 'Access Share', 'absolute'],
184 - ['lock_exclusive', 'Exclusive', 'absolute'],
185 - ['lock_row_exclusive', 'Row Exclusive', 'absolute'],
186 - ['lock_row_share', 'Row Share', 'absolute'],
187 - ['lock_update_exclusive_lock', 'Update Exclusive Lock', 'absolute'],
188 - ['lock_share', 'Share', 'absolute'],
189 - ['lock_share_row_exclusive', 'Share Row Exclusive', 'absolute'],
190 - ['lock_si_read', 'SI Read', 'absolute']
191 - ]},
200 'wal': {
201 'options': [None, 'WAL stats', 'Files', 'WAL', 'postgres.wal', 'line'],
202 'lines': [
@@ -238,6 +246,7 @@ class Service(SimpleService):
246 self.connection = None
247 self.data = {}
248 self.old_data = {}
249 + self.databases = set()
250
251 def connect(self):
252 params = dict(user='postgres',
@@ -253,6 +262,7 @@ class Service(SimpleService):
262 def check(self):
263 try:
264 self.connect()
265 + self.discover_databases()
266 self._create_definitions()
267 return True
268 except Exception as e:
@@ -260,42 +270,67 @@ class Service(SimpleService):
270 return False
271
272 def _create_definitions(self):
273 + for database_name in self.databases:
274 + self.databases.add(database_name)
275 + for chart_template_name in list(CHARTS):
276 + if chart_template_name.startswith('db_stat'):
277 + self._add_database_stat_chart(chart_template_name, database_name)
278 + self._add_database_lock_chart(database_name)
279 +
280 + def discover_databases(self):
281 cursor = self.connection.cursor()
282 cursor.execute("""
283 SELECT datname
284 FROM pg_stat_database
285 WHERE NOT datname ~* '^template\d+'
286 """)
269 -
270 - for row in cursor:
271 - database_name = row[0]
272 - for chart_template_name in list(CHARTS):
273 - if not chart_template_name.startswith('db_stat'):
274 - continue
275 -
276 - chart_template = CHARTS[chart_template_name]
277 - chart_name = "{}_{}".format(database_name, chart_template_name)
278 - if chart_name not in self.order:
279 - self.order.insert(0, chart_name)
280 - name, title, units, family, context, chart_type = chart_template['options']
281 - self.definitions[chart_name] = {
282 - 'options': [
283 - name,
284 - database_name + title,
285 - units,
286 - database_name + family,
287 - database_name + context,
288 - chart_type
289 - ]
290 - }
291 -
292 - self.definitions[chart_name]['lines'] = []
293 - for line in deepcopy(chart_template['lines']):
294 - line[0] = "{}_{}".format(database_name, line[0])
295 - self.definitions[chart_name]['lines'].append(line)
296 -
287 + self.databases = set(r[0] for r in cursor)
288 cursor.close()
289
290 + def _add_database_stat_chart(self, chart_template_name, database_name):
291 + chart_template = CHARTS[chart_template_name]
292 + chart_name = "{}_{}".format(database_name, chart_template_name)
293 + if chart_name not in self.order:
294 + self.order.insert(0, chart_name)
295 + name, title, units, family, context, chart_type = chart_template['options']
296 + self.definitions[chart_name] = {
297 + 'options': [
298 + name,
299 + database_name + title,
300 + units,
301 + database_name + family,
302 + database_name + context,
303 + chart_type
304 + ]
305 + }
306 +
307 + self.definitions[chart_name]['lines'] = []
308 + for line in deepcopy(chart_template['lines']):
309 + line[0] = "{}_{}".format(database_name, line[0])
310 + self.definitions[chart_name]['lines'].append(line)
311 +
312 + def _add_database_lock_chart(self, database_name):
313 + chart_name = "{}_locks".format(database_name)
314 + if chart_name not in self.order:
315 + self.order.insert(0, chart_name)
316 + self.definitions[chart_name] = dict(
317 + options=
318 + [
319 + None,
320 + database_name + ' locks',
321 + 'Count',
322 + database_name + ' database statistics',
323 + database_name + '.locks',
324 + 'line'
325 + ],
326 + lines=[]
327 + )
328 +
329 + for lock_type in LOCK_TYPES:
330 + lock_id = "{}_{}".format(database_name, lock_type.lower())
331 + label = re.sub("([a-z])([A-Z])", "\g<1> \g<2>", lock_type)
332 + self.definitions[chart_name]['lines'].append([lock_id, label, 'absolute'])
333 +
334 def _get_data(self):
335 self.connect()
336
@@ -357,17 +392,18 @@ class Service(SimpleService):
392 self.data['table_size'] = int(temp.get('size_relations', 0))
393
394 def add_lock_stats(self, cursor):
360 - cursor.execute(LOCKS)
361 - temp = cursor.fetchall()
362 - for key in LOCK_MAP:
363 - found = False
364 - for row in temp:
365 - if row['mode'] == key:
366 - found = True
367 - self.data[LOCK_MAP[key]] = int(row['count'])
368 -
369 - if not found:
370 - self.data[LOCK_MAP[key]] = 0
395 + cursor.execute(DATABASE_LOCKS)
396 + # First zero out all current lock values.
397 + for database_name in self.databases:
398 + for lock_type in LOCK_TYPES:
399 + lock_id = "{}_{}".format(database_name, lock_type.lower())
400 + self.data[lock_id] = 0
401 +
402 + # Now populate those that have current locks
403 + for row in cursor:
404 + database_name, lock_type, lock_count = row
405 + lock_id = "{}_{}".format(database_name, lock_type.lower())
406 + self.data[lock_id] = lock_count
407
408 def add_wal_stats(self, cursor):
409 cursor.execute(ARCHIVE)