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
// ------------------------------------------------------------------------