@cryptotaxi247 / netdata-1 / commits / 1d7c6bba1

Postgres : Add replication slot and wal writes charts.

Fix units for other charts.

anayrat committed Mar 9, 2018 at 18:14 UTC 1d7c6bba16d32ddf688fec29dc88e7b4e39f884c
2 files changed +97 -9
python.d/postgres.chart.py
+90 -9
@@ -44,6 +44,7 @@ METRICS = dict(
44 WAL=['written_wal',
45 'recycled_wal',
46 'total_wal'],
47 + WAL_WRITES=['wal_writes'],
48 ARCHIVE=['ready_count',
49 'done_count',
50 'file_count'],
@@ -72,7 +73,10 @@ METRICS = dict(
73 STANDBY_DELTA=['sent_delta',
74 'write_delta',
75 'flush_delta',
75 - 'replay_delta']
76 + 'replay_delta'],
77 + REPSLOT_FILES=['replslot_wal_keep',
78 + 'replslot_files']
79 +
80 )
81
82 QUERIES = dict(
@@ -172,6 +176,10 @@ SELECT application_name
176 FROM pg_stat_replication
177 WHERE application_name IS NOT NULL
178 GROUP BY application_name;
179 +""",
180 + FIND_REPLICATION_SLOT="""
181 +SELECT slot_name
182 +FROM pg_replication_slots;
183 """,
184 STANDBY_DELTA="""
185 SELECT application_name,
@@ -181,6 +189,28 @@ SELECT application_name,
189 pg_{0}_{1}_diff(CASE pg_is_in_recovery() WHEN true THEN pg_last_{0}_receive_{1}() ELSE pg_current_{0}_{1}() END , replay_{1}) AS replay_delta
190 FROM pg_stat_replication
191 WHERE application_name IS NOT NULL;
192 +""",
193 + REPSLOT_FILES="""
194 +WITH wal_size AS (
195 + SELECT current_setting('wal_block_size')::INT * setting::INT AS val
196 + FROM pg_settings
197 + WHERE name = 'wal_segment_size'
198 +)
199 +SELECT slot_name, slot_type, replslot_wal_keep, count(slot_file) AS replslot_files
200 +FROM (
201 + SELECT slot.slot_name, CASE WHEN slot_file <> 'state' THEN 1 END AS slot_file , slot_type,
202 + COALESCE (floor((pg_wal_lsn_diff (pg_current_wal_lsn (),
203 + slot.restart_lsn) - (pg_walfile_name_offset (restart_lsn)).file_offset) / (s.val)),
204 + 0) AS replslot_wal_keep
205 + FROM pg_replication_slots slot
206 + LEFT JOIN (
207 + SELECT slot2.slot_name,
208 + pg_ls_dir('pg_replslot/' || slot2.slot_name) AS slot_file
209 + FROM pg_replication_slots slot2
210 + ) files (slot_name, slot_file)
211 + ON slot.slot_name = files.slot_name
212 + CROSS JOIN wal_size s) AS d
213 +GROUP BY slot_name, slot_type, replslot_wal_keep;
214 """,
215 IF_SUPERUSER="""
216 SELECT current_setting('is_superuser') = 'on' AS is_superuser;
@@ -199,7 +229,11 @@ SELECT
229 count(*) FILTER (WHERE query LIKE 'autovacuum: BRIN summarize%%') AS brin_summarize
230 FROM pg_stat_activity
231 WHERE query NOT LIKE '%%pg_stat_activity%%';
232 +""",
233 + DIFF_LSN="""
234 +SELECT pg_{0}_{1}_diff(CASE pg_is_in_recovery() WHEN true THEN pg_last_{0}_receive_{1}() ELSE pg_current_{0}_{1}() END, '0/0') as wal_writes ;
235 """
236 +
237 )
238
239
@@ -209,11 +243,11 @@ QUERY_STATS = {
243 QUERIES['LOCKS']: METRICS['LOCKS']
244 }
245
212 -ORDER = ['db_stat_temp_bytes', 'db_stat_temp_files', 'db_stat_blks', 'db_stat_tuple_returned', 'db_stat_tuple_write',
246 +ORDER = ['db_stat_temp_files', 'db_stat_temp_bytes', 'db_stat_blks', 'db_stat_tuple_returned', 'db_stat_tuple_write',
247 'db_stat_transactions','db_stat_connections', 'database_size', 'backend_process', 'index_count', 'index_size',
214 - 'table_count', 'table_size', 'wal', 'archive_wal', 'checkpointer', 'stat_bgwriter_alloc', 'stat_bgwriter_checkpoint',
248 + 'table_count', 'table_size', 'wal', 'wal_writes', 'archive_wal', 'checkpointer', 'stat_bgwriter_alloc', 'stat_bgwriter_checkpoint',
249 'stat_bgwriter_backend', 'stat_bgwriter_backend_fsync' , 'stat_bgwriter_bgwriter', 'stat_bgwriter_maxwritten',
216 - 'standby_delta','autovacuum']
250 + 'replication_slot', 'standby_delta', 'autovacuum']
251
252 CHARTS = {
253 'db_stat_transactions': {
@@ -298,6 +332,11 @@ CHARTS = {
332 ['recycled_wal', 'recycled', 'absolute'],
333 ['total_wal', 'total', 'absolute']
334 ]},
335 + 'wal_writes': {
336 + 'options': [None, 'Write-Ahead Logs', 'kilobytes/s', 'wal_writes', 'postgres.wal_writes', 'line'],
337 + 'lines': [
338 + ['wal_writes', 'writes', 'incremental', 1, 1024]
339 + ]},
340 'archive_wal': {
341 'options': [None, 'Archive Write-Ahead Logs', 'files/s', 'archive wal', 'postgres.archive_wal', 'line'],
342 'lines': [
@@ -314,17 +353,17 @@ CHARTS = {
353 'stat_bgwriter_alloc': {
354 'options': [None, 'Buffers allocated', 'kilobytes/s', 'bgwriter', 'postgres.stat_bgwriter_alloc', 'line'],
355 'lines': [
317 - ['buffers_alloc', 'alloc', 'incremental', 8, 1024]
356 + ['buffers_alloc', 'alloc', 'incremental', 1, 1024]
357 ]},
358 'stat_bgwriter_checkpoint': {
359 'options': [None, 'Buffers written during checkpoints', 'kilobytes/s', 'bgwriter', 'postgres.stat_bgwriter_checkpoint', 'line'],
360 'lines': [
322 - ['buffers_checkpoint', 'checkpoint', 'incremental', 8, 1024]
361 + ['buffers_checkpoint', 'checkpoint', 'incremental', 1, 1024]
362 ]},
363 'stat_bgwriter_backend': {
364 'options': [None, 'Buffers written directly by a backend', 'kilobytes/s', 'bgwriter', 'postgres.stat_bgwriter_backend', 'line'],
365 'lines': [
327 - ['buffers_backend', 'backend', 'incremental', 8, 1024]
366 + ['buffers_backend', 'backend', 'incremental', 1, 1024]
367 ]},
368 'stat_bgwriter_backend_fsync': {
369 'options': [None, 'Fsync by backend', 'times', 'bgwriter', 'postgres.stat_bgwriter_backend_fsync', 'line'],
@@ -334,7 +373,7 @@ CHARTS = {
373 'stat_bgwriter_bgwriter': {
374 'options': [None, 'Buffers written by the background writer', 'kilobytes/s', 'bgwriter', 'postgres.bgwriter_bgwriter', 'line'],
375 'lines': [
337 - ['buffers_clean', 'clean', 'incremental', 8, 1024]
376 + ['buffers_clean', 'clean', 'incremental', 1, 1024]
377 ]},
378 'stat_bgwriter_maxwritten': {
379 'options': [None, 'Too many buffers written', 'times', 'bgwriter', 'postgres.stat_bgwriter_maxwritten', 'line'],
@@ -357,6 +396,12 @@ CHARTS = {
396 ['write_delta', 'write delta', 'absolute', 1, 1024],
397 ['flush_delta', 'flush delta', 'absolute', 1, 1024],
398 ['replay_delta', 'replay delta', 'absolute', 1, 1024]
399 + ]},
400 + 'replication_slot': {
401 + 'options': [None, 'Replication slot files', 'files', 'replication slot', 'postgres.replication_slot', 'line'],
402 + 'lines': [
403 + ['replslot_wal_keep', 'wal keeped', 'absolute'],
404 + ['replslot_files', 'pg_replslot files', 'absolute']
405 ]}
406 }
407
@@ -376,6 +421,7 @@ class Service(SimpleService):
421 self.locks_zeroed = dict()
422 self.databases = list()
423 self.secondaries = list()
424 + self.replication_slots = list()
425 self.queries = QUERY_STATS.copy()
426
427 def _connect(self):
@@ -411,6 +457,8 @@ class Service(SimpleService):
457 is_superuser = check_if_superuser_(cursor, QUERIES['IF_SUPERUSER'])
458 self.secondaries = discover_secondaries_(cursor, QUERIES['FIND_STANDBY'])
459 self.server_version = detect_server_version(cursor, QUERIES['DETECT_SERVER_VERSION'])
460 + if self.server_version >= 94000:
461 + self.replication_slots = discover_replication_slots_(cursor, QUERIES['FIND_REPLICATION_SLOT'])
462 cursor.close()
463
464 if self.database_poll and isinstance(self.database_poll, str):
@@ -433,8 +481,8 @@ class Service(SimpleService):
481 else:
482 wal = 'xlog'
483 lsn = 'location'
436 -
484 self.queries[QUERIES['BGWRITER']] = METRICS['BGWRITER']
485 + self.queries[QUERIES['DIFF_LSN'].format(wal,lsn)] = METRICS['WAL_WRITES']
486 self.queries[QUERIES['STANDBY_DELTA'].format(wal,lsn)] = METRICS['STANDBY_DELTA']
487
488 if self.index_stats:
@@ -445,6 +493,8 @@ class Service(SimpleService):
493 self.queries[QUERIES['ARCHIVE'].format(wal)] = METRICS['ARCHIVE']
494 if self.server_version >= 90400:
495 self.queries[QUERIES['WAL'].format(wal,lsn)] = METRICS['WAL']
496 + if self.server_version >= 100000:
497 + self.queries[QUERIES['REPSLOT_FILES']] = METRICS['REPSLOT_FILES']
498 if self.server_version >= 90400:
499 self.queries[QUERIES['AUTOVACUUM']] = METRICS['AUTOVACUUM']
500
@@ -463,6 +513,11 @@ class Service(SimpleService):
513 add_replication_delta_chart_(order=self.order, definitions=self.definitions,
514 name='standby_delta', application_name=application_name)
515
516 + for slot_name in self.replication_slots[::-1]:
517 + add_replication_slot_chart_(order=self.order, definitions=self.definitions,
518 + name='replication_slot', slot_name=slot_name)
519 +
520 +
521
522 def _get_data(self):
523 result, error = self._connect()
@@ -491,6 +546,8 @@ class Service(SimpleService):
546 dimension_id = '_'.join([row['database_name'], metric])
547 elif 'application_name' in row:
548 dimension_id = '_'.join([row['application_name'], metric])
549 + elif 'slot_name' in row:
550 + dimension_id = '_'.join([row['slot_name'], metric])
551 else:
552 dimension_id = metric
553 if metric in row:
@@ -515,6 +572,13 @@ def discover_secondaries_(cursor, query):
572 result.append(sc)
573 return result
574
575 +def discover_replication_slots_(cursor, query):
576 + cursor.execute(query)
577 + result = list()
578 + for slot in [replication_slot[0] for replication_slot in cursor]:
579 + if slot not in result:
580 + result.append(slot)
581 + return result
582
583 def check_if_superuser_(cursor, query):
584 cursor.execute(query)
@@ -583,3 +647,20 @@ def add_replication_delta_chart_(order, definitions, name, application_name):
647 definitions[chart_name] = {
648 'options': [name, title + ': ' + application_name, units, 'replication delta', context, chart_type],
649 'lines': create_lines(application_name, chart_template['lines'])}
650 +
651 +def add_replication_slot_chart_(order, definitions, name, slot_name):
652 + def create_lines(slot, lines):
653 + result = list()
654 + for line in lines:
655 + new_line = ['_'.join([slot, line[0]])] + line[1:]
656 + result.append(new_line)
657 + return result
658 +
659 + chart_template = CHARTS[name]
660 + chart_name = '_'.join([slot_name, name])
661 + position = order.index('database_size')
662 + order.insert(position, chart_name)
663 + name, title, units, family, context, chart_type = chart_template['options']
664 + definitions[chart_name] = {
665 + 'options': [name, title + ': ' + slot_name, units, 'replication slot files', context, chart_type],
666 + 'lines': create_lines(slot_name, chart_template['lines'])}
web/dashboard_info.js
+7
@@ -1068,6 +1068,13 @@ netdataDashboard.context = {
1068 '</ul>' +
1069 'For more information see <a href="https://www.postgresql.org/docs/current/static/warm-standby.html#SYNCHRONOUS-REPLICATION" target="_blank">Synchronous Replication</a>.'
1070 },
1071 + 'postgres.replication_slot': {
1072 + info: 'Replication slot files.<ul>' +
1073 + '<li><strong>wal_keeped:</strong> WAL files retained by each replication slots.</li>' +
1074 + '<li><strong>pg_replslot_files:</strong> files present in pg_replslot.</li>' +
1075 + '</ul>' +
1076 + 'For more information see <a href="https://www.postgresql.org/docs/current/static/warm-standby.html#STREAMING-REPLICATION-SLOTS" target="_blank">Replication Slots</a>.'
1077 + },
1078
1079
1080 // ------------------------------------------------------------------------