master
go 1,400 lines 43.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package postgres
4
5 import (
6 "fmt"
7 "strings"
8 "time"
9
10 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
11 )
12
13 const (
14 prioConnectionsUtilization = collectorapi.Priority + iota
15 prioConnectionsUsage
16 prioConnectionsStateCount
17 prioDBConnectionsUtilization
18 prioDBConnectionsCount
19
20 prioTransactionsDuration
21 prioDBTransactionsRatio
22 prioDBTransactionsRate
23
24 prioQueriesDuration
25
26 prioDBOpsFetchedRowsRatio
27 prioDBOpsReadRowsRate
28 prioDBOpsWriteRowsRate
29 prioDBTempFilesCreatedRate
30 prioDBTempFilesIORate
31 prioTableOpsRowsRate
32 prioTableOpsRowsHOTRatio
33 prioTableOpsRowsHOTRate
34 prioTableScansRate
35 prioTableScansRowsRate
36
37 prioDBCacheIORatio
38 prioDBIORate
39 prioTableCacheIORatio
40 prioTableIORate
41 prioTableIndexCacheIORatio
42 prioTableIndexIORate
43 prioTableToastCacheIORatio
44 prioTableToastIORate
45 prioTableToastIndexCacheIORatio
46 prioTableToastIndexIORate
47
48 prioDBSize
49 prioTableTotalSize
50 prioIndexSize
51
52 prioTableBloatSizePerc
53 prioTableBloatSize
54 prioIndexBloatSizePerc
55 prioIndexBloatSize
56
57 prioLocksUtilization
58 prioDBLocksHeldCount
59 prioDBLocksAwaitedCount
60 prioDBDeadlocksRate
61
62 prioAutovacuumWorkersCount
63 prioTableAutovacuumSinceTime
64 prioTableVacuumSinceTime
65 prioTableAutoAnalyzeSinceTime
66 prioTableLastAnalyzeAgo
67
68 prioCheckpointsRate
69 prioCheckpointsTime
70 prioBGWriterHaltsRate
71 prioBuffersIORate
72 prioBuffersBackendFsyncRate
73 prioBuffersAllocRate
74 prioTXIDExhaustionTowardsAutovacuumPerc
75 prioTXIDExhaustionPerc
76 prioTXIDExhaustionOldestTXIDNum
77 prioTableRowsDeadRatio
78 prioTableRowsCount
79 prioTableNullColumns
80 prioIndexUsageStatus
81
82 prioReplicationAppWALLagSize
83 prioReplicationAppWALLagTime
84 prioReplicationSlotFilesCount
85 prioDBConflictsRate
86 prioDBConflictsReasonRate
87
88 prioWALIORate
89 prioWALFilesCount
90 prioWALArchivingFilesCount
91
92 prioDatabasesCount
93 prioCatalogRelationsCount
94 prioCatalogRelationsSize
95
96 prioUptime
97 )
98
99 var baseCharts = collectorapi.Charts{
100 serverConnectionsUtilizationChart.Copy(),
101 serverConnectionsUsageChart.Copy(),
102 serverConnectionsStateCount.Copy(),
103 locksUtilization.Copy(),
104 checkpointsChart.Copy(),
105 checkpointWriteChart.Copy(),
106 buffersIORateChart.Copy(),
107 buffersAllocRateChart.Copy(),
108 bgWriterHaltsRateChart.Copy(),
109 buffersBackendFsyncRateChart.Copy(),
110 walIORateChart.Copy(),
111 autovacuumWorkersCountChart.Copy(),
112 txidExhaustionTowardsAutovacuumPercChart.Copy(),
113 txidExhaustionPercChart.Copy(),
114 txidExhaustionOldestTXIDNumChart.Copy(),
115
116 catalogRelationSCountChart.Copy(),
117 catalogRelationsSizeChart.Copy(),
118 serverUptimeChart.Copy(),
119 databasesCountChart.Copy(),
120 }
121
122 var walFilesCharts = collectorapi.Charts{
123 walFilesCountChart.Copy(),
124 walArchivingFilesCountChart.Copy(),
125 }
126
127 func (c *Collector) addWALFilesCharts() {
128 charts := walFilesCharts.Copy()
129
130 if err := c.Charts().Add(*charts...); err != nil {
131 c.Warning(err)
132 }
133 }
134
135 var (
136 serverConnectionsUtilizationChart = collectorapi.Chart{
137 ID: "connections_utilization",
138 Title: "Connections utilization",
139 Units: "percentage",
140 Fam: "connections",
141 Ctx: "postgres.connections_utilization",
142 Priority: prioConnectionsUtilization,
143 Dims: collectorapi.Dims{
144 {ID: "server_connections_utilization", Name: "used"},
145 },
146 }
147 serverConnectionsUsageChart = collectorapi.Chart{
148 ID: "connections_usage",
149 Title: "Connections usage",
150 Units: "connections",
151 Fam: "connections",
152 Ctx: "postgres.connections_usage",
153 Priority: prioConnectionsUsage,
154 Type: collectorapi.Stacked,
155 Dims: collectorapi.Dims{
156 {ID: "server_connections_available", Name: "available"},
157 {ID: "server_connections_used", Name: "used"},
158 },
159 }
160 serverConnectionsStateCount = collectorapi.Chart{
161 ID: "connections_state",
162 Title: "Connections in each state",
163 Units: "connections",
164 Fam: "connections",
165 Ctx: "postgres.connections_state_count",
166 Priority: prioConnectionsStateCount,
167 Type: collectorapi.Stacked,
168 Dims: collectorapi.Dims{
169 {ID: "server_connections_state_active", Name: "active"},
170 {ID: "server_connections_state_idle", Name: "idle"},
171 {ID: "server_connections_state_idle_in_transaction", Name: "idle_in_transaction"},
172 {ID: "server_connections_state_idle_in_transaction_aborted", Name: "idle_in_transaction_aborted"},
173 {ID: "server_connections_state_fastpath_function_call", Name: "fastpath_function_call"},
174 {ID: "server_connections_state_disabled", Name: "disabled"},
175 },
176 }
177
178 locksUtilization = collectorapi.Chart{
179 ID: "locks_utilization",
180 Title: "Acquired locks utilization",
181 Units: "percentage",
182 Fam: "locks",
183 Ctx: "postgres.locks_utilization",
184 Priority: prioLocksUtilization,
185 Dims: collectorapi.Dims{
186 {ID: "locks_utilization", Name: "used"},
187 },
188 }
189
190 checkpointsChart = collectorapi.Chart{
191 ID: "checkpoints_rate",
192 Title: "Checkpoints",
193 Units: "checkpoints/s",
194 Fam: "maintenance",
195 Ctx: "postgres.checkpoints_rate",
196 Priority: prioCheckpointsRate,
197 Type: collectorapi.Stacked,
198 Dims: collectorapi.Dims{
199 {ID: "checkpoints_timed", Name: "scheduled", Algo: collectorapi.Incremental},
200 {ID: "checkpoints_req", Name: "requested", Algo: collectorapi.Incremental},
201 },
202 }
203 // TODO: should be seconds, also it is units/s when using incremental...
204 checkpointWriteChart = collectorapi.Chart{
205 ID: "checkpoints_time",
206 Title: "Checkpoint time",
207 Units: "milliseconds",
208 Fam: "maintenance",
209 Ctx: "postgres.checkpoints_time",
210 Priority: prioCheckpointsTime,
211 Type: collectorapi.Stacked,
212 Dims: collectorapi.Dims{
213 {ID: "checkpoint_write_time", Name: "write", Algo: collectorapi.Incremental},
214 {ID: "checkpoint_sync_time", Name: "sync", Algo: collectorapi.Incremental},
215 },
216 }
217 bgWriterHaltsRateChart = collectorapi.Chart{
218 ID: "bgwriter_halts_rate",
219 Title: "Background writer scan halts",
220 Units: "halts/s",
221 Fam: "maintenance",
222 Ctx: "postgres.bgwriter_halts_rate",
223 Priority: prioBGWriterHaltsRate,
224 Dims: collectorapi.Dims{
225 {ID: "maxwritten_clean", Name: "maxwritten", Algo: collectorapi.Incremental},
226 },
227 }
228
229 buffersIORateChart = collectorapi.Chart{
230 ID: "buffers_io_rate",
231 Title: "Buffers written rate",
232 Units: "B/s",
233 Fam: "maintenance",
234 Ctx: "postgres.buffers_io_rate",
235 Priority: prioBuffersIORate,
236 Type: collectorapi.Area,
237 Dims: collectorapi.Dims{
238 {ID: "buffers_checkpoint", Name: "checkpoint", Algo: collectorapi.Incremental},
239 {ID: "buffers_backend", Name: "backend", Algo: collectorapi.Incremental},
240 {ID: "buffers_clean", Name: "bgwriter", Algo: collectorapi.Incremental},
241 },
242 }
243 buffersBackendFsyncRateChart = collectorapi.Chart{
244 ID: "buffers_backend_fsync_rate",
245 Title: "Backend fsync calls",
246 Units: "calls/s",
247 Fam: "maintenance",
248 Ctx: "postgres.buffers_backend_fsync_rate",
249 Priority: prioBuffersBackendFsyncRate,
250 Dims: collectorapi.Dims{
251 {ID: "buffers_backend_fsync", Name: "fsync", Algo: collectorapi.Incremental},
252 },
253 }
254 buffersAllocRateChart = collectorapi.Chart{
255 ID: "buffers_alloc_rate",
256 Title: "Buffers allocated",
257 Units: "B/s",
258 Fam: "maintenance",
259 Ctx: "postgres.buffers_allocated_rate",
260 Priority: prioBuffersAllocRate,
261 Dims: collectorapi.Dims{
262 {ID: "buffers_alloc", Name: "allocated", Algo: collectorapi.Incremental},
263 },
264 }
265
266 walIORateChart = collectorapi.Chart{
267 ID: "wal_io_rate",
268 Title: "Write-Ahead Log writes",
269 Units: "B/s",
270 Fam: "wal",
271 Ctx: "postgres.wal_io_rate",
272 Priority: prioWALIORate,
273 Dims: collectorapi.Dims{
274 {ID: "wal_writes", Name: "written", Algo: collectorapi.Incremental},
275 },
276 }
277 walFilesCountChart = collectorapi.Chart{
278 ID: "wal_files_count",
279 Title: "Write-Ahead Log files",
280 Units: "files",
281 Fam: "wal",
282 Ctx: "postgres.wal_files_count",
283 Priority: prioWALFilesCount,
284 Type: collectorapi.Stacked,
285 Dims: collectorapi.Dims{
286 {ID: "wal_written_files", Name: "written"},
287 {ID: "wal_recycled_files", Name: "recycled"},
288 },
289 }
290
291 walArchivingFilesCountChart = collectorapi.Chart{
292 ID: "wal_archiving_files_count",
293 Title: "Write-Ahead Log archived files",
294 Units: "files/s",
295 Fam: "wal",
296 Ctx: "postgres.wal_archiving_files_count",
297 Priority: prioWALArchivingFilesCount,
298 Type: collectorapi.Stacked,
299 Dims: collectorapi.Dims{
300 {ID: "wal_archive_files_ready_count", Name: "ready"},
301 {ID: "wal_archive_files_done_count", Name: "done"},
302 },
303 }
304
305 autovacuumWorkersCountChart = collectorapi.Chart{
306 ID: "autovacuum_workers_count",
307 Title: "Autovacuum workers",
308 Units: "workers",
309 Fam: "vacuum and analyze",
310 Ctx: "postgres.autovacuum_workers_count",
311 Priority: prioAutovacuumWorkersCount,
312 Dims: collectorapi.Dims{
313 {ID: "autovacuum_analyze", Name: "analyze"},
314 {ID: "autovacuum_vacuum_analyze", Name: "vacuum_analyze"},
315 {ID: "autovacuum_vacuum", Name: "vacuum"},
316 {ID: "autovacuum_vacuum_freeze", Name: "vacuum_freeze"},
317 {ID: "autovacuum_brin_summarize", Name: "brin_summarize"},
318 },
319 }
320
321 txidExhaustionTowardsAutovacuumPercChart = collectorapi.Chart{
322 ID: "txid_exhaustion_towards_autovacuum_perc",
323 Title: "Percent towards emergency autovacuum",
324 Units: "percentage",
325 Fam: "maintenance",
326 Ctx: "postgres.txid_exhaustion_towards_autovacuum_perc",
327 Priority: prioTXIDExhaustionTowardsAutovacuumPerc,
328 Dims: collectorapi.Dims{
329 {ID: "percent_towards_emergency_autovacuum", Name: "emergency_autovacuum"},
330 },
331 }
332 txidExhaustionPercChart = collectorapi.Chart{
333 ID: "txid_exhaustion_perc",
334 Title: "Percent towards transaction ID wraparound",
335 Units: "percentage",
336 Fam: "maintenance",
337 Ctx: "postgres.txid_exhaustion_perc",
338 Priority: prioTXIDExhaustionPerc,
339 Dims: collectorapi.Dims{
340 {ID: "percent_towards_wraparound", Name: "txid_exhaustion"},
341 },
342 }
343 txidExhaustionOldestTXIDNumChart = collectorapi.Chart{
344 ID: "txid_exhaustion_oldest_txid_num",
345 Title: "Oldest transaction XID",
346 Units: "xid",
347 Fam: "maintenance",
348 Ctx: "postgres.txid_exhaustion_oldest_txid_num",
349 Priority: prioTXIDExhaustionOldestTXIDNum,
350 Dims: collectorapi.Dims{
351 {ID: "oldest_current_xid", Name: "xid"},
352 },
353 }
354
355 catalogRelationSCountChart = collectorapi.Chart{
356 ID: "catalog_relations_count",
357 Title: "Relation count",
358 Units: "relations",
359 Fam: "catalog",
360 Ctx: "postgres.catalog_relations_count",
361 Priority: prioCatalogRelationsCount,
362 Type: collectorapi.Stacked,
363 Dims: collectorapi.Dims{
364 {ID: "catalog_relkind_r_count", Name: "ordinary_table"},
365 {ID: "catalog_relkind_i_count", Name: "index"},
366 {ID: "catalog_relkind_S_count", Name: "sequence"},
367 {ID: "catalog_relkind_t_count", Name: "toast_table"},
368 {ID: "catalog_relkind_v_count", Name: "view"},
369 {ID: "catalog_relkind_m_count", Name: "materialized_view"},
370 {ID: "catalog_relkind_c_count", Name: "composite_type"},
371 {ID: "catalog_relkind_f_count", Name: "foreign_table"},
372 {ID: "catalog_relkind_p_count", Name: "partitioned_table"},
373 {ID: "catalog_relkind_I_count", Name: "partitioned_index"},
374 },
375 }
376 catalogRelationsSizeChart = collectorapi.Chart{
377 ID: "catalog_relations_size",
378 Title: "Relation size",
379 Units: "B",
380 Fam: "catalog",
381 Ctx: "postgres.catalog_relations_size",
382 Priority: prioCatalogRelationsSize,
383 Type: collectorapi.Stacked,
384 Dims: collectorapi.Dims{
385 {ID: "catalog_relkind_r_size", Name: "ordinary_table"},
386 {ID: "catalog_relkind_i_size", Name: "index"},
387 {ID: "catalog_relkind_S_size", Name: "sequence"},
388 {ID: "catalog_relkind_t_size", Name: "toast_table"},
389 {ID: "catalog_relkind_v_size", Name: "view"},
390 {ID: "catalog_relkind_m_size", Name: "materialized_view"},
391 {ID: "catalog_relkind_c_size", Name: "composite_type"},
392 {ID: "catalog_relkind_f_size", Name: "foreign_table"},
393 {ID: "catalog_relkind_p_size", Name: "partitioned_table"},
394 {ID: "catalog_relkind_I_size", Name: "partitioned_index"},
395 },
396 }
397
398 serverUptimeChart = collectorapi.Chart{
399 ID: "server_uptime",
400 Title: "Uptime",
401 Units: "seconds",
402 Fam: "uptime",
403 Ctx: "postgres.uptime",
404 Priority: prioUptime,
405 Dims: collectorapi.Dims{
406 {ID: "server_uptime", Name: "uptime"},
407 },
408 }
409
410 databasesCountChart = collectorapi.Chart{
411 ID: "databases_count",
412 Title: "Number of databases",
413 Units: "databases",
414 Fam: "catalog",
415 Ctx: "postgres.databases_count",
416 Priority: prioDatabasesCount,
417 Dims: collectorapi.Dims{
418 {ID: "databases_count", Name: "databases"},
419 },
420 }
421
422 transactionsDurationChartTmpl = collectorapi.Chart{
423 ID: "transactions_duration",
424 Title: "Observed transactions time",
425 Units: "transactions/s",
426 Fam: "transactions",
427 Ctx: "postgres.transactions_duration",
428 Priority: prioTransactionsDuration,
429 Type: collectorapi.Stacked,
430 }
431 queriesDurationChartTmpl = collectorapi.Chart{
432 ID: "queries_duration",
433 Title: "Observed active queries time",
434 Units: "queries/s",
435 Fam: "queries",
436 Ctx: "postgres.queries_duration",
437 Priority: prioQueriesDuration,
438 Type: collectorapi.Stacked,
439 }
440 )
441
442 func newRunningTimeHistogramChart(tmpl collectorapi.Chart, prefix string, buckets []float64) (*collectorapi.Chart, error) {
443 chart := tmpl.Copy()
444
445 for i, v := range buckets {
446 dim := &collectorapi.Dim{
447 ID: fmt.Sprintf("%s_hist_bucket_%d", prefix, i+1),
448 Name: time.Duration(v * float64(time.Second)).String(),
449 Algo: collectorapi.Incremental,
450 }
451 if err := chart.AddDim(dim); err != nil {
452 return nil, err
453 }
454 }
455
456 dim := &collectorapi.Dim{
457 ID: fmt.Sprintf("%s_hist_bucket_inf", prefix),
458 Name: "+Inf",
459 Algo: collectorapi.Incremental,
460 }
461 if err := chart.AddDim(dim); err != nil {
462 return nil, err
463 }
464
465 return chart, nil
466 }
467
468 func (c *Collector) addTransactionsRunTimeHistogramChart() {
469 chart, err := newRunningTimeHistogramChart(
470 transactionsDurationChartTmpl,
471 "transaction_running_time",
472 c.XactTimeHistogram,
473 )
474 if err != nil {
475 c.Warning(err)
476 return
477 }
478 if err := c.Charts().Add(chart); err != nil {
479 c.Warning(err)
480 }
481 }
482
483 func (c *Collector) addQueriesRunTimeHistogramChart() {
484 chart, err := newRunningTimeHistogramChart(
485 queriesDurationChartTmpl,
486 "query_running_time",
487 c.QueryTimeHistogram,
488 )
489 if err != nil {
490 c.Warning(err)
491 return
492 }
493 if err := c.Charts().Add(chart); err != nil {
494 c.Warning(err)
495 }
496 }
497
498 var (
499 replicationStandbyAppCharts = collectorapi.Charts{
500 replicationAppWALLagSizeChartTmpl.Copy(),
501 replicationAppWALLagTimeChartTmpl.Copy(),
502 }
503 replicationAppWALLagSizeChartTmpl = collectorapi.Chart{
504 ID: "replication_app_%s_wal_lag_size",
505 Title: "Standby application WAL lag size",
506 Units: "B",
507 Fam: "replication",
508 Ctx: "postgres.replication_app_wal_lag_size",
509 Priority: prioReplicationAppWALLagSize,
510 Dims: collectorapi.Dims{
511 {ID: "repl_standby_app_%s_wal_sent_lag_size", Name: "sent_lag"},
512 {ID: "repl_standby_app_%s_wal_write_lag_size", Name: "write_lag"},
513 {ID: "repl_standby_app_%s_wal_flush_lag_size", Name: "flush_lag"},
514 {ID: "repl_standby_app_%s_wal_replay_lag_size", Name: "replay_lag"},
515 },
516 }
517 replicationAppWALLagTimeChartTmpl = collectorapi.Chart{
518 ID: "replication_app_%s_wal_lag_time",
519 Title: "Standby application WAL lag time",
520 Units: "seconds",
521 Fam: "replication",
522 Ctx: "postgres.replication_app_wal_lag_time",
523 Priority: prioReplicationAppWALLagTime,
524 Dims: collectorapi.Dims{
525 {ID: "repl_standby_app_%s_wal_write_lag_time", Name: "write_lag"},
526 {ID: "repl_standby_app_%s_wal_flush_lag_time", Name: "flush_lag"},
527 {ID: "repl_standby_app_%s_wal_replay_lag_time", Name: "replay_lag"},
528 },
529 }
530 )
531
532 func newReplicationStandbyAppCharts(app string) *collectorapi.Charts {
533 charts := replicationStandbyAppCharts.Copy()
534 for _, c := range *charts {
535 c.ID = fmt.Sprintf(c.ID, app)
536 c.Labels = []collectorapi.Label{
537 {Key: "application", Value: app},
538 }
539 for _, d := range c.Dims {
540 d.ID = fmt.Sprintf(d.ID, app)
541 }
542 }
543 return charts
544 }
545
546 func (c *Collector) addNewReplicationStandbyAppCharts(app string) {
547 charts := newReplicationStandbyAppCharts(app)
548 if err := c.Charts().Add(*charts...); err != nil {
549 c.Warning(err)
550 }
551 }
552
553 func (c *Collector) removeReplicationStandbyAppCharts(app string) {
554 prefix := fmt.Sprintf("replication_standby_app_%s_", app)
555 for _, c := range *c.Charts() {
556 if strings.HasPrefix(c.ID, prefix) {
557 c.MarkRemove()
558 c.MarkNotCreated()
559 }
560 }
561 }
562
563 var (
564 replicationSlotCharts = collectorapi.Charts{
565 replicationSlotFilesCountChartTmpl.Copy(),
566 }
567 replicationSlotFilesCountChartTmpl = collectorapi.Chart{
568 ID: "replication_slot_%s_files_count",
569 Title: "Replication slot files",
570 Units: "files",
571 Fam: "replication",
572 Ctx: "postgres.replication_slot_files_count",
573 Priority: prioReplicationSlotFilesCount,
574 Dims: collectorapi.Dims{
575 {ID: "repl_slot_%s_replslot_wal_keep", Name: "wal_keep"},
576 {ID: "repl_slot_%s_replslot_files", Name: "pg_replslot_files"},
577 },
578 }
579 )
580
581 func newReplicationSlotCharts(slot string) *collectorapi.Charts {
582 charts := replicationSlotCharts.Copy()
583 for _, c := range *charts {
584 c.ID = fmt.Sprintf(c.ID, slot)
585 c.Labels = []collectorapi.Label{
586 {Key: "slot", Value: slot},
587 }
588 for _, d := range c.Dims {
589 d.ID = fmt.Sprintf(d.ID, slot)
590 }
591 }
592 return charts
593 }
594
595 func (c *Collector) addNewReplicationSlotCharts(slot string) {
596 charts := newReplicationSlotCharts(slot)
597 if err := c.Charts().Add(*charts...); err != nil {
598 c.Warning(err)
599 }
600 }
601
602 func (c *Collector) removeReplicationSlotCharts(slot string) {
603 prefix := fmt.Sprintf("replication_slot_%s_", slot)
604 for _, c := range *c.Charts() {
605 if strings.HasPrefix(c.ID, prefix) {
606 c.MarkRemove()
607 c.MarkNotCreated()
608 }
609 }
610 }
611
612 var (
613 dbChartsTmpl = collectorapi.Charts{
614 dbTransactionsRatioChartTmpl.Copy(),
615 dbTransactionsRateChartTmpl.Copy(),
616 dbConnectionsUtilizationChartTmpl.Copy(),
617 dbConnectionsCountChartTmpl.Copy(),
618 dbCacheIORatioChartTmpl.Copy(),
619 dbIORateChartTmpl.Copy(),
620 dbOpsFetchedRowsRatioChartTmpl.Copy(),
621 dbOpsReadRowsRateChartTmpl.Copy(),
622 dbOpsWriteRowsRateChartTmpl.Copy(),
623 dbDeadlocksRateChartTmpl.Copy(),
624 dbLocksHeldCountChartTmpl.Copy(),
625 dbLocksAwaitedCountChartTmpl.Copy(),
626 dbTempFilesCreatedRateChartTmpl.Copy(),
627 dbTempFilesIORateChartTmpl.Copy(),
628 dbSizeChartTmpl.Copy(),
629 }
630 dbTransactionsRatioChartTmpl = collectorapi.Chart{
631 ID: "db_%s_transactions_ratio",
632 Title: "Database transactions ratio",
633 Units: "percentage",
634 Fam: "transactions",
635 Ctx: "postgres.db_transactions_ratio",
636 Priority: prioDBTransactionsRatio,
637 Type: collectorapi.Stacked,
638 Dims: collectorapi.Dims{
639 {ID: "db_%s_xact_commit", Name: "committed", Algo: collectorapi.PercentOfIncremental},
640 {ID: "db_%s_xact_rollback", Name: "rollback", Algo: collectorapi.PercentOfIncremental},
641 },
642 }
643 dbTransactionsRateChartTmpl = collectorapi.Chart{
644 ID: "db_%s_transactions_rate",
645 Title: "Database transactions",
646 Units: "transactions/s",
647 Fam: "transactions",
648 Ctx: "postgres.db_transactions_rate",
649 Priority: prioDBTransactionsRate,
650 Dims: collectorapi.Dims{
651 {ID: "db_%s_xact_commit", Name: "committed", Algo: collectorapi.Incremental},
652 {ID: "db_%s_xact_rollback", Name: "rollback", Algo: collectorapi.Incremental},
653 },
654 }
655 dbConnectionsUtilizationChartTmpl = collectorapi.Chart{
656 ID: "db_%s_connections_utilization",
657 Title: "Database connections utilization",
658 Units: "percentage",
659 Fam: "connections",
660 Ctx: "postgres.db_connections_utilization",
661 Priority: prioDBConnectionsUtilization,
662 Dims: collectorapi.Dims{
663 {ID: "db_%s_numbackends_utilization", Name: "used"},
664 },
665 }
666 dbConnectionsCountChartTmpl = collectorapi.Chart{
667 ID: "db_%s_connections",
668 Title: "Database connections",
669 Units: "connections",
670 Fam: "connections",
671 Ctx: "postgres.db_connections_count",
672 Priority: prioDBConnectionsCount,
673 Dims: collectorapi.Dims{
674 {ID: "db_%s_numbackends", Name: "connections"},
675 },
676 }
677 dbCacheIORatioChartTmpl = collectorapi.Chart{
678 ID: "db_%s_cache_io_ratio",
679 Title: "Database buffer cache miss ratio",
680 Units: "percentage",
681 Fam: "cache",
682 Ctx: "postgres.db_cache_io_ratio",
683 Priority: prioDBCacheIORatio,
684 Dims: collectorapi.Dims{
685 {ID: "db_%s_blks_read_perc", Name: "miss"},
686 },
687 }
688 dbIORateChartTmpl = collectorapi.Chart{
689 ID: "db_%s_io_rate",
690 Title: "Database reads",
691 Units: "B/s",
692 Fam: "cache",
693 Ctx: "postgres.db_io_rate",
694 Priority: prioDBIORate,
695 Type: collectorapi.Area,
696 Dims: collectorapi.Dims{
697 {ID: "db_%s_blks_hit", Name: "memory", Algo: collectorapi.Incremental},
698 {ID: "db_%s_blks_read", Name: "disk", Algo: collectorapi.Incremental},
699 },
700 }
701 dbOpsFetchedRowsRatioChartTmpl = collectorapi.Chart{
702 ID: "db_%s_db_ops_fetched_rows_ratio",
703 Title: "Database rows fetched ratio",
704 Units: "percentage",
705 Fam: "throughput",
706 Ctx: "postgres.db_ops_fetched_rows_ratio",
707 Priority: prioDBOpsFetchedRowsRatio,
708 Dims: collectorapi.Dims{
709 {ID: "db_%s_tup_fetched_perc", Name: "fetched"},
710 },
711 }
712 dbOpsReadRowsRateChartTmpl = collectorapi.Chart{
713 ID: "db_%s_ops_read_rows_rate",
714 Title: "Database rows read",
715 Units: "rows/s",
716 Fam: "throughput",
717 Ctx: "postgres.db_ops_read_rows_rate",
718 Priority: prioDBOpsReadRowsRate,
719 Dims: collectorapi.Dims{
720 {ID: "db_%s_tup_returned", Name: "returned", Algo: collectorapi.Incremental},
721 {ID: "db_%s_tup_fetched", Name: "fetched", Algo: collectorapi.Incremental},
722 },
723 }
724 dbOpsWriteRowsRateChartTmpl = collectorapi.Chart{
725 ID: "db_%s_ops_write_rows_rate",
726 Title: "Database rows written",
727 Units: "rows/s",
728 Fam: "throughput",
729 Ctx: "postgres.db_ops_write_rows_rate",
730 Priority: prioDBOpsWriteRowsRate,
731 Dims: collectorapi.Dims{
732 {ID: "db_%s_tup_inserted", Name: "inserted", Algo: collectorapi.Incremental},
733 {ID: "db_%s_tup_deleted", Name: "deleted", Algo: collectorapi.Incremental},
734 {ID: "db_%s_tup_updated", Name: "updated", Algo: collectorapi.Incremental},
735 },
736 }
737 dbConflictsRateChartTmpl = collectorapi.Chart{
738 ID: "db_%s_conflicts_rate",
739 Title: "Database canceled queries",
740 Units: "queries/s",
741 Fam: "replication",
742 Ctx: "postgres.db_conflicts_rate",
743 Priority: prioDBConflictsRate,
744 Dims: collectorapi.Dims{
745 {ID: "db_%s_conflicts", Name: "conflicts", Algo: collectorapi.Incremental},
746 },
747 }
748 dbConflictsReasonRateChartTmpl = collectorapi.Chart{
749 ID: "db_%s_conflicts_reason_rate",
750 Title: "Database canceled queries by reason",
751 Units: "queries/s",
752 Fam: "replication",
753 Ctx: "postgres.db_conflicts_reason_rate",
754 Priority: prioDBConflictsReasonRate,
755 Dims: collectorapi.Dims{
756 {ID: "db_%s_confl_tablespace", Name: "tablespace", Algo: collectorapi.Incremental},
757 {ID: "db_%s_confl_lock", Name: "lock", Algo: collectorapi.Incremental},
758 {ID: "db_%s_confl_snapshot", Name: "snapshot", Algo: collectorapi.Incremental},
759 {ID: "db_%s_confl_bufferpin", Name: "bufferpin", Algo: collectorapi.Incremental},
760 {ID: "db_%s_confl_deadlock", Name: "deadlock", Algo: collectorapi.Incremental},
761 },
762 }
763 dbDeadlocksRateChartTmpl = collectorapi.Chart{
764 ID: "db_%s_deadlocks_rate",
765 Title: "Database deadlocks",
766 Units: "deadlocks/s",
767 Fam: "locks",
768 Ctx: "postgres.db_deadlocks_rate",
769 Priority: prioDBDeadlocksRate,
770 Dims: collectorapi.Dims{
771 {ID: "db_%s_deadlocks", Name: "deadlocks", Algo: collectorapi.Incremental},
772 },
773 }
774 dbLocksHeldCountChartTmpl = collectorapi.Chart{
775 ID: "db_%s_locks_held",
776 Title: "Database locks held",
777 Units: "locks",
778 Fam: "locks",
779 Ctx: "postgres.db_locks_held_count",
780 Priority: prioDBLocksHeldCount,
781 Type: collectorapi.Stacked,
782 Dims: collectorapi.Dims{
783 {ID: "db_%s_lock_mode_AccessShareLock_held", Name: "access_share"},
784 {ID: "db_%s_lock_mode_RowShareLock_held", Name: "row_share"},
785 {ID: "db_%s_lock_mode_RowExclusiveLock_held", Name: "row_exclusive"},
786 {ID: "db_%s_lock_mode_ShareUpdateExclusiveLock_held", Name: "share_update"},
787 {ID: "db_%s_lock_mode_ShareLock_held", Name: "share"},
788 {ID: "db_%s_lock_mode_ShareRowExclusiveLock_held", Name: "share_row_exclusive"},
789 {ID: "db_%s_lock_mode_ExclusiveLock_held", Name: "exclusive"},
790 {ID: "db_%s_lock_mode_AccessExclusiveLock_held", Name: "access_exclusive"},
791 },
792 }
793 dbLocksAwaitedCountChartTmpl = collectorapi.Chart{
794 ID: "db_%s_locks_awaited_count",
795 Title: "Database locks awaited",
796 Units: "locks",
797 Fam: "locks",
798 Ctx: "postgres.db_locks_awaited_count",
799 Priority: prioDBLocksAwaitedCount,
800 Type: collectorapi.Stacked,
801 Dims: collectorapi.Dims{
802 {ID: "db_%s_lock_mode_AccessShareLock_awaited", Name: "access_share"},
803 {ID: "db_%s_lock_mode_RowShareLock_awaited", Name: "row_share"},
804 {ID: "db_%s_lock_mode_RowExclusiveLock_awaited", Name: "row_exclusive"},
805 {ID: "db_%s_lock_mode_ShareUpdateExclusiveLock_awaited", Name: "share_update"},
806 {ID: "db_%s_lock_mode_ShareLock_awaited", Name: "share"},
807 {ID: "db_%s_lock_mode_ShareRowExclusiveLock_awaited", Name: "share_row_exclusive"},
808 {ID: "db_%s_lock_mode_ExclusiveLock_awaited", Name: "exclusive"},
809 {ID: "db_%s_lock_mode_AccessExclusiveLock_awaited", Name: "access_exclusive"},
810 },
811 }
812 dbTempFilesCreatedRateChartTmpl = collectorapi.Chart{
813 ID: "db_%s_temp_files_files_created_rate",
814 Title: "Database created temporary files",
815 Units: "files/s",
816 Fam: "throughput",
817 Ctx: "postgres.db_temp_files_created_rate",
818 Priority: prioDBTempFilesCreatedRate,
819 Dims: collectorapi.Dims{
820 {ID: "db_%s_temp_files", Name: "created", Algo: collectorapi.Incremental},
821 },
822 }
823 dbTempFilesIORateChartTmpl = collectorapi.Chart{
824 ID: "db_%s_temp_files_io_rate",
825 Title: "Database temporary files data written to disk",
826 Units: "B/s",
827 Fam: "throughput",
828 Ctx: "postgres.db_temp_files_io_rate",
829 Priority: prioDBTempFilesIORate,
830 Dims: collectorapi.Dims{
831 {ID: "db_%s_temp_bytes", Name: "written", Algo: collectorapi.Incremental},
832 },
833 }
834 dbSizeChartTmpl = collectorapi.Chart{
835 ID: "db_%s_size",
836 Title: "Database size",
837 Units: "B",
838 Fam: "size",
839 Ctx: "postgres.db_size",
840 Priority: prioDBSize,
841 Dims: collectorapi.Dims{
842 {ID: "db_%s_size", Name: "size"},
843 },
844 }
845 )
846
847 func (c *Collector) addDBConflictsCharts(db *dbMetrics) {
848 tmpl := collectorapi.Charts{
849 dbConflictsRateChartTmpl.Copy(),
850 dbConflictsReasonRateChartTmpl.Copy(),
851 }
852 charts := newDatabaseCharts(tmpl.Copy(), db)
853
854 if err := c.Charts().Add(*charts...); err != nil {
855 c.Warning(err)
856 }
857 }
858
859 func newDatabaseCharts(tmpl *collectorapi.Charts, db *dbMetrics) *collectorapi.Charts {
860 charts := tmpl.Copy()
861 for _, c := range *charts {
862 c.ID = fmt.Sprintf(c.ID, db.name)
863 c.Labels = []collectorapi.Label{
864 {Key: "database", Value: db.name},
865 }
866 for _, d := range c.Dims {
867 d.ID = fmt.Sprintf(d.ID, db.name)
868 }
869 }
870 return charts
871 }
872
873 func (c *Collector) addNewDatabaseCharts(db *dbMetrics) {
874 charts := newDatabaseCharts(dbChartsTmpl.Copy(), db)
875
876 if db.size == nil {
877 _ = charts.Remove(fmt.Sprintf(dbSizeChartTmpl.ID, db.name))
878 }
879
880 if err := c.Charts().Add(*charts...); err != nil {
881 c.Warning(err)
882 }
883 }
884
885 func (c *Collector) removeDatabaseCharts(db *dbMetrics) {
886 prefix := fmt.Sprintf("db_%s_", db.name)
887 for _, c := range *c.Charts() {
888 if strings.HasPrefix(c.ID, prefix) {
889 c.MarkRemove()
890 c.MarkNotCreated()
891 }
892 }
893 }
894
895 var (
896 tableChartsTmpl = collectorapi.Charts{
897 tableRowsCountChartTmpl.Copy(),
898 tableDeadRowsDeadRatioChartTmpl.Copy(),
899 tableOpsRowsRateChartTmpl.Copy(),
900 tableOpsRowsHOTRatioChartTmpl.Copy(),
901 tableOpsRowsHOTRateChartTmpl.Copy(),
902 tableScansRateChartTmpl.Copy(),
903 tableScansRowsRateChartTmpl.Copy(),
904 tableNullColumnsCountChartTmpl.Copy(),
905 tableTotalSizeChartTmpl.Copy(),
906 tableBloatSizePercChartTmpl.Copy(),
907 tableBloatSizeChartTmpl.Copy(),
908 }
909
910 tableDeadRowsDeadRatioChartTmpl = collectorapi.Chart{
911 ID: "table_%s_db_%s_schema_%s_rows_dead_ratio",
912 Title: "Table dead rows",
913 Units: "%",
914 Fam: "maintenance",
915 Ctx: "postgres.table_rows_dead_ratio",
916 Priority: prioTableRowsDeadRatio,
917 Dims: collectorapi.Dims{
918 {ID: "table_%s_db_%s_schema_%s_n_dead_tup_perc", Name: "dead"},
919 },
920 }
921 tableRowsCountChartTmpl = collectorapi.Chart{
922 ID: "table_%s_db_%s_schema_%s_rows_count",
923 Title: "Table total rows",
924 Units: "rows",
925 Fam: "maintenance",
926 Ctx: "postgres.table_rows_count",
927 Priority: prioTableRowsCount,
928 Type: collectorapi.Stacked,
929 Dims: collectorapi.Dims{
930 {ID: "table_%s_db_%s_schema_%s_n_live_tup", Name: "live"},
931 {ID: "table_%s_db_%s_schema_%s_n_dead_tup", Name: "dead"},
932 },
933 }
934 tableOpsRowsRateChartTmpl = collectorapi.Chart{
935 ID: "table_%s_db_%s_schema_%s_ops_rows_rate",
936 Title: "Table throughput",
937 Units: "rows/s",
938 Fam: "throughput",
939 Ctx: "postgres.table_ops_rows_rate",
940 Priority: prioTableOpsRowsRate,
941 Type: collectorapi.Stacked,
942 Dims: collectorapi.Dims{
943 {ID: "table_%s_db_%s_schema_%s_n_tup_ins", Name: "inserted", Algo: collectorapi.Incremental},
944 {ID: "table_%s_db_%s_schema_%s_n_tup_del", Name: "deleted", Algo: collectorapi.Incremental},
945 {ID: "table_%s_db_%s_schema_%s_n_tup_upd", Name: "updated", Algo: collectorapi.Incremental},
946 },
947 }
948 tableOpsRowsHOTRatioChartTmpl = collectorapi.Chart{
949 ID: "table_%s_db_%s_schema_%s_ops_rows_hot_ratio",
950 Title: "Table HOT updates ratio",
951 Units: "percentage",
952 Fam: "throughput",
953 Ctx: "postgres.table_ops_rows_hot_ratio",
954 Priority: prioTableOpsRowsHOTRatio,
955 Dims: collectorapi.Dims{
956 {ID: "table_%s_db_%s_schema_%s_n_tup_hot_upd_perc", Name: "hot"},
957 },
958 }
959 tableOpsRowsHOTRateChartTmpl = collectorapi.Chart{
960 ID: "table_%s_db_%s_schema_%s_ops_rows_hot_rate",
961 Title: "Table HOT updates",
962 Units: "rows/s",
963 Fam: "throughput",
964 Ctx: "postgres.table_ops_rows_hot_rate",
965 Priority: prioTableOpsRowsHOTRate,
966 Dims: collectorapi.Dims{
967 {ID: "table_%s_db_%s_schema_%s_n_tup_hot_upd", Name: "hot", Algo: collectorapi.Incremental},
968 },
969 }
970 tableCacheIORatioChartTmpl = collectorapi.Chart{
971 ID: "table_%s_db_%s_schema_%s_cache_io_ratio",
972 Title: "Table I/O cache miss ratio",
973 Units: "percentage",
974 Fam: "cache",
975 Ctx: "postgres.table_cache_io_ratio",
976 Priority: prioTableCacheIORatio,
977 Dims: collectorapi.Dims{
978 {ID: "table_%s_db_%s_schema_%s_heap_blks_read_perc", Name: "miss"},
979 },
980 }
981 tableIORateChartTmpl = collectorapi.Chart{
982 ID: "table_%s_db_%s_schema_%s_io_rate",
983 Title: "Table I/O",
984 Units: "B/s",
985 Fam: "cache",
986 Ctx: "postgres.table_io_rate",
987 Priority: prioTableIORate,
988 Dims: collectorapi.Dims{
989 {ID: "table_%s_db_%s_schema_%s_heap_blks_hit", Name: "memory", Algo: collectorapi.Incremental},
990 {ID: "table_%s_db_%s_schema_%s_heap_blks_read", Name: "disk", Algo: collectorapi.Incremental},
991 },
992 }
993 tableIndexCacheIORatioChartTmpl = collectorapi.Chart{
994 ID: "table_%s_db_%s_schema_%s_index_cache_io_ratio",
995 Title: "Table index I/O cache miss ratio",
996 Units: "percentage",
997 Fam: "cache",
998 Ctx: "postgres.table_index_cache_io_ratio",
999 Priority: prioTableIndexCacheIORatio,
1000 Dims: collectorapi.Dims{
1001 {ID: "table_%s_db_%s_schema_%s_idx_blks_read_perc", Name: "miss", Algo: collectorapi.Incremental},
1002 },
1003 }
1004 tableIndexIORateChartTmpl = collectorapi.Chart{
1005 ID: "table_%s_db_%s_schema_%s_index_io_rate",
1006 Title: "Table index I/O",
1007 Units: "B/s",
1008 Fam: "cache",
1009 Ctx: "postgres.table_index_io_rate",
1010 Priority: prioTableIndexIORate,
1011 Dims: collectorapi.Dims{
1012 {ID: "table_%s_db_%s_schema_%s_idx_blks_hit", Name: "memory", Algo: collectorapi.Incremental},
1013 {ID: "table_%s_db_%s_schema_%s_idx_blks_read", Name: "disk", Algo: collectorapi.Incremental},
1014 },
1015 }
1016 tableTOASCacheIORatioChartTmpl = collectorapi.Chart{
1017 ID: "table_%s_db_%s_schema_%s_toast_cache_io_ratio",
1018 Title: "Table TOAST I/O cache miss ratio",
1019 Units: "percentage",
1020 Fam: "cache",
1021 Ctx: "postgres.table_toast_cache_io_ratio",
1022 Priority: prioTableToastCacheIORatio,
1023 Dims: collectorapi.Dims{
1024 {ID: "table_%s_db_%s_schema_%s_toast_blks_read_perc", Name: "miss", Algo: collectorapi.Incremental},
1025 },
1026 }
1027 tableTOASTIORateChartTmpl = collectorapi.Chart{
1028 ID: "table_%s_db_%s_schema_%s_toast_io_rate",
1029 Title: "Table TOAST I/O",
1030 Units: "B/s",
1031 Fam: "cache",
1032 Ctx: "postgres.table_toast_io_rate",
1033 Priority: prioTableToastIORate,
1034 Dims: collectorapi.Dims{
1035 {ID: "table_%s_db_%s_schema_%s_toast_blks_hit", Name: "memory", Algo: collectorapi.Incremental},
1036 {ID: "table_%s_db_%s_schema_%s_toast_blks_read", Name: "disk", Algo: collectorapi.Incremental},
1037 },
1038 }
1039 tableTOASTIndexCacheIORatioChartTmpl = collectorapi.Chart{
1040 ID: "table_%s_db_%s_schema_%s_toast_index_cache_io_ratio",
1041 Title: "Table TOAST index I/O cache miss ratio",
1042 Units: "percentage",
1043 Fam: "cache",
1044 Ctx: "postgres.table_toast_index_cache_io_ratio",
1045 Priority: prioTableToastIndexCacheIORatio,
1046 Dims: collectorapi.Dims{
1047 {ID: "table_%s_db_%s_schema_%s_tidx_blks_read_perc", Name: "miss", Algo: collectorapi.Incremental},
1048 },
1049 }
1050 tableTOASTIndexIORateChartTmpl = collectorapi.Chart{
1051 ID: "table_%s_db_%s_schema_%s_toast_index_io_rate",
1052 Title: "Table TOAST index I/O",
1053 Units: "B/s",
1054 Fam: "cache",
1055 Ctx: "postgres.table_toast_index_io_rate",
1056 Priority: prioTableToastIndexIORate,
1057 Dims: collectorapi.Dims{
1058 {ID: "table_%s_db_%s_schema_%s_tidx_blks_hit", Name: "memory", Algo: collectorapi.Incremental},
1059 {ID: "table_%s_db_%s_schema_%s_tidx_blks_read", Name: "disk", Algo: collectorapi.Incremental},
1060 },
1061 }
1062 tableScansRateChartTmpl = collectorapi.Chart{
1063 ID: "table_%s_db_%s_schema_%s_scans_rate",
1064 Title: "Table scans",
1065 Units: "scans/s",
1066 Fam: "throughput",
1067 Ctx: "postgres.table_scans_rate",
1068 Priority: prioTableScansRate,
1069 Dims: collectorapi.Dims{
1070 {ID: "table_%s_db_%s_schema_%s_idx_scan", Name: "index", Algo: collectorapi.Incremental},
1071 {ID: "table_%s_db_%s_schema_%s_seq_scan", Name: "sequential", Algo: collectorapi.Incremental},
1072 },
1073 }
1074 tableScansRowsRateChartTmpl = collectorapi.Chart{
1075 ID: "table_%s_db_%s_schema_%s_scans_rows_rate",
1076 Title: "Table live rows fetched by scans",
1077 Units: "rows/s",
1078 Fam: "throughput",
1079 Ctx: "postgres.table_scans_rows_rate",
1080 Priority: prioTableScansRowsRate,
1081 Dims: collectorapi.Dims{
1082 {ID: "table_%s_db_%s_schema_%s_idx_tup_fetch", Name: "index", Algo: collectorapi.Incremental},
1083 {ID: "table_%s_db_%s_schema_%s_seq_tup_read", Name: "sequential", Algo: collectorapi.Incremental},
1084 },
1085 }
1086 tableAutoVacuumSinceTimeChartTmpl = collectorapi.Chart{
1087 ID: "table_%s_db_%s_schema_%s_autovacuum_since_time",
1088 Title: "Table time since last auto VACUUM",
1089 Units: "seconds",
1090 Fam: "vacuum and analyze",
1091 Ctx: "postgres.table_autovacuum_since_time",
1092 Priority: prioTableAutovacuumSinceTime,
1093 Dims: collectorapi.Dims{
1094 {ID: "table_%s_db_%s_schema_%s_last_autovacuum_ago", Name: "time"},
1095 },
1096 }
1097 tableVacuumSinceTimeChartTmpl = collectorapi.Chart{
1098 ID: "table_%s_db_%s_schema_%s_vacuum_since_time",
1099 Title: "Table time since last manual VACUUM",
1100 Units: "seconds",
1101 Fam: "vacuum and analyze",
1102 Ctx: "postgres.table_vacuum_since_time",
1103 Priority: prioTableVacuumSinceTime,
1104 Dims: collectorapi.Dims{
1105 {ID: "table_%s_db_%s_schema_%s_last_vacuum_ago", Name: "time"},
1106 },
1107 }
1108 tableAutoAnalyzeSinceTimeChartTmpl = collectorapi.Chart{
1109 ID: "table_%s_db_%s_schema_%s_autoanalyze_since_time",
1110 Title: "Table time since last auto ANALYZE",
1111 Units: "seconds",
1112 Fam: "vacuum and analyze",
1113 Ctx: "postgres.table_autoanalyze_since_time",
1114 Priority: prioTableAutoAnalyzeSinceTime,
1115 Dims: collectorapi.Dims{
1116 {ID: "table_%s_db_%s_schema_%s_last_autoanalyze_ago", Name: "time"},
1117 },
1118 }
1119 tableAnalyzeSinceTimeChartTmpl = collectorapi.Chart{
1120 ID: "table_%s_db_%s_schema_%s_analyze_since_time",
1121 Title: "Table time since last manual ANALYZE",
1122 Units: "seconds",
1123 Fam: "vacuum and analyze",
1124 Ctx: "postgres.table_analyze_since_time",
1125 Priority: prioTableLastAnalyzeAgo,
1126 Dims: collectorapi.Dims{
1127 {ID: "table_%s_db_%s_schema_%s_last_analyze_ago", Name: "time"},
1128 },
1129 }
1130 tableNullColumnsCountChartTmpl = collectorapi.Chart{
1131 ID: "table_%s_db_%s_schema_%s_null_columns_count",
1132 Title: "Table null columns",
1133 Units: "columns",
1134 Fam: "maintenance",
1135 Ctx: "postgres.table_null_columns_count",
1136 Priority: prioTableNullColumns,
1137 Dims: collectorapi.Dims{
1138 {ID: "table_%s_db_%s_schema_%s_null_columns", Name: "null"},
1139 },
1140 }
1141 tableTotalSizeChartTmpl = collectorapi.Chart{
1142 ID: "table_%s_db_%s_schema_%s_total_size",
1143 Title: "Table total size",
1144 Units: "B",
1145 Fam: "size",
1146 Ctx: "postgres.table_total_size",
1147 Priority: prioTableTotalSize,
1148 Dims: collectorapi.Dims{
1149 {ID: "table_%s_db_%s_schema_%s_total_size", Name: "size"},
1150 },
1151 }
1152 tableBloatSizePercChartTmpl = collectorapi.Chart{
1153 ID: "table_%s_db_%s_schema_%s_bloat_size_perc",
1154 Title: "Table bloat size percentage",
1155 Units: "percentage",
1156 Fam: "bloat",
1157 Ctx: "postgres.table_bloat_size_perc",
1158 Priority: prioTableBloatSizePerc,
1159 Dims: collectorapi.Dims{
1160 {ID: "table_%s_db_%s_schema_%s_bloat_size_perc", Name: "bloat"},
1161 },
1162 Vars: collectorapi.Vars{
1163 {ID: "table_%s_db_%s_schema_%s_total_size", Name: "table_size"},
1164 },
1165 }
1166 tableBloatSizeChartTmpl = collectorapi.Chart{
1167 ID: "table_%s_db_%s_schema_%s_bloat_size",
1168 Title: "Table bloat size",
1169 Units: "B",
1170 Fam: "bloat",
1171 Ctx: "postgres.table_bloat_size",
1172 Priority: prioTableBloatSize,
1173 Dims: collectorapi.Dims{
1174 {ID: "table_%s_db_%s_schema_%s_bloat_size", Name: "bloat"},
1175 },
1176 }
1177 )
1178
1179 func newTableCharts(tbl *tableMetrics) *collectorapi.Charts {
1180 charts := tableChartsTmpl.Copy()
1181
1182 if tbl.bloatSize == nil {
1183 _ = charts.Remove(tableBloatSizeChartTmpl.ID)
1184 _ = charts.Remove(tableBloatSizePercChartTmpl.ID)
1185 }
1186
1187 for i, chart := range *charts {
1188 (*charts)[i] = newTableChart(chart, tbl)
1189 }
1190
1191 return charts
1192 }
1193
1194 func newTableChart(chart *collectorapi.Chart, tbl *tableMetrics) *collectorapi.Chart {
1195 chart = chart.Copy()
1196 chart.ID = fmt.Sprintf(chart.ID, tbl.name, tbl.db, tbl.schema)
1197 chart.Labels = []collectorapi.Label{
1198 {Key: "database", Value: tbl.db},
1199 {Key: "schema", Value: tbl.schema},
1200 {Key: "table", Value: tbl.name},
1201 {Key: "parent_table", Value: tbl.parentName},
1202 }
1203 for _, d := range chart.Dims {
1204 d.ID = fmt.Sprintf(d.ID, tbl.name, tbl.db, tbl.schema)
1205 }
1206 for _, v := range chart.Vars {
1207 v.ID = fmt.Sprintf(v.ID, tbl.name, tbl.db, tbl.schema)
1208 }
1209 return chart
1210 }
1211
1212 func (c *Collector) addNewTableCharts(tbl *tableMetrics) {
1213 charts := newTableCharts(tbl)
1214 if err := c.Charts().Add(*charts...); err != nil {
1215 c.Warning(err)
1216 }
1217 }
1218
1219 func (c *Collector) addTableLastAutoVacuumAgoChart(tbl *tableMetrics) {
1220 chart := newTableChart(tableAutoVacuumSinceTimeChartTmpl.Copy(), tbl)
1221
1222 if err := c.Charts().Add(chart); err != nil {
1223 c.Warning(err)
1224 }
1225 }
1226
1227 func (c *Collector) addTableLastVacuumAgoChart(tbl *tableMetrics) {
1228 chart := newTableChart(tableVacuumSinceTimeChartTmpl.Copy(), tbl)
1229
1230 if err := c.Charts().Add(chart); err != nil {
1231 c.Warning(err)
1232 }
1233 }
1234
1235 func (c *Collector) addTableLastAutoAnalyzeAgoChart(tbl *tableMetrics) {
1236 chart := newTableChart(tableAutoAnalyzeSinceTimeChartTmpl.Copy(), tbl)
1237
1238 if err := c.Charts().Add(chart); err != nil {
1239 c.Warning(err)
1240 }
1241 }
1242
1243 func (c *Collector) addTableLastAnalyzeAgoChart(tbl *tableMetrics) {
1244 chart := newTableChart(tableAnalyzeSinceTimeChartTmpl.Copy(), tbl)
1245
1246 if err := c.Charts().Add(chart); err != nil {
1247 c.Warning(err)
1248 }
1249 }
1250
1251 func (c *Collector) addTableIOChartsCharts(tbl *tableMetrics) {
1252 charts := collectorapi.Charts{
1253 newTableChart(tableCacheIORatioChartTmpl.Copy(), tbl),
1254 newTableChart(tableIORateChartTmpl.Copy(), tbl),
1255 }
1256
1257 if err := c.Charts().Add(charts...); err != nil {
1258 c.Warning(err)
1259 }
1260 }
1261
1262 func (c *Collector) addTableIndexIOCharts(tbl *tableMetrics) {
1263 charts := collectorapi.Charts{
1264 newTableChart(tableIndexCacheIORatioChartTmpl.Copy(), tbl),
1265 newTableChart(tableIndexIORateChartTmpl.Copy(), tbl),
1266 }
1267
1268 if err := c.Charts().Add(charts...); err != nil {
1269 c.Warning(err)
1270 }
1271 }
1272
1273 func (c *Collector) addTableTOASTIOCharts(tbl *tableMetrics) {
1274 charts := collectorapi.Charts{
1275 newTableChart(tableTOASCacheIORatioChartTmpl.Copy(), tbl),
1276 newTableChart(tableTOASTIORateChartTmpl.Copy(), tbl),
1277 }
1278
1279 if err := c.Charts().Add(charts...); err != nil {
1280 c.Warning(err)
1281 }
1282 }
1283
1284 func (c *Collector) addTableTOASTIndexIOCharts(tbl *tableMetrics) {
1285 charts := collectorapi.Charts{
1286 newTableChart(tableTOASTIndexCacheIORatioChartTmpl.Copy(), tbl),
1287 newTableChart(tableTOASTIndexIORateChartTmpl.Copy(), tbl),
1288 }
1289
1290 if err := c.Charts().Add(charts...); err != nil {
1291 c.Warning(err)
1292 }
1293 }
1294
1295 func (c *Collector) removeTableCharts(tbl *tableMetrics) {
1296 prefix := fmt.Sprintf("table_%s_db_%s_schema_%s", tbl.name, tbl.db, tbl.schema)
1297 for _, c := range *c.Charts() {
1298 if strings.HasPrefix(c.ID, prefix) {
1299 c.MarkRemove()
1300 c.MarkNotCreated()
1301 }
1302 }
1303 }
1304
1305 var (
1306 indexChartsTmpl = collectorapi.Charts{
1307 indexSizeChartTmpl.Copy(),
1308 indexBloatSizePercChartTmpl.Copy(),
1309 indexBloatSizeChartTmpl.Copy(),
1310 indexUsageStatusChartTmpl.Copy(),
1311 }
1312 indexSizeChartTmpl = collectorapi.Chart{
1313 ID: "index_%s_table_%s_db_%s_schema_%s_size",
1314 Title: "Index size",
1315 Units: "B",
1316 Fam: "size",
1317 Ctx: "postgres.index_size",
1318 Priority: prioIndexSize,
1319 Dims: collectorapi.Dims{
1320 {ID: "index_%s_table_%s_db_%s_schema_%s_size", Name: "size"},
1321 },
1322 }
1323 indexBloatSizePercChartTmpl = collectorapi.Chart{
1324 ID: "index_%s_table_%s_db_%s_schema_%s_bloat_size_perc",
1325 Title: "Index bloat size percentage",
1326 Units: "percentage",
1327 Fam: "bloat",
1328 Ctx: "postgres.index_bloat_size_perc",
1329 Priority: prioIndexBloatSizePerc,
1330 Dims: collectorapi.Dims{
1331 {ID: "index_%s_table_%s_db_%s_schema_%s_bloat_size_perc", Name: "bloat"},
1332 },
1333 Vars: collectorapi.Vars{
1334 {ID: "index_%s_table_%s_db_%s_schema_%s_size", Name: "index_size"},
1335 },
1336 }
1337 indexBloatSizeChartTmpl = collectorapi.Chart{
1338 ID: "index_%s_table_%s_db_%s_schema_%s_bloat_size",
1339 Title: "Index bloat size",
1340 Units: "B",
1341 Fam: "bloat",
1342 Ctx: "postgres.index_bloat_size",
1343 Priority: prioIndexBloatSize,
1344 Dims: collectorapi.Dims{
1345 {ID: "index_%s_table_%s_db_%s_schema_%s_bloat_size", Name: "bloat"},
1346 },
1347 }
1348 indexUsageStatusChartTmpl = collectorapi.Chart{
1349 ID: "index_%s_table_%s_db_%s_schema_%s_usage_status",
1350 Title: "Index usage status",
1351 Units: "status",
1352 Fam: "maintenance",
1353 Ctx: "postgres.index_usage_status",
1354 Priority: prioIndexUsageStatus,
1355 Dims: collectorapi.Dims{
1356 {ID: "index_%s_table_%s_db_%s_schema_%s_usage_status_used", Name: "used"},
1357 {ID: "index_%s_table_%s_db_%s_schema_%s_usage_status_unused", Name: "unused"},
1358 },
1359 }
1360 )
1361
1362 func (c *Collector) addNewIndexCharts(idx *indexMetrics) {
1363 charts := indexChartsTmpl.Copy()
1364
1365 if idx.bloatSize == nil {
1366 _ = charts.Remove(indexBloatSizeChartTmpl.ID)
1367 _ = charts.Remove(indexBloatSizePercChartTmpl.ID)
1368 }
1369
1370 for _, chart := range *charts {
1371 chart.ID = fmt.Sprintf(chart.ID, idx.name, idx.table, idx.db, idx.schema)
1372 chart.Labels = []collectorapi.Label{
1373 {Key: "database", Value: idx.db},
1374 {Key: "schema", Value: idx.schema},
1375 {Key: "table", Value: idx.table},
1376 {Key: "parent_table", Value: idx.parentTable},
1377 {Key: "index", Value: idx.name},
1378 }
1379 for _, d := range chart.Dims {
1380 d.ID = fmt.Sprintf(d.ID, idx.name, idx.table, idx.db, idx.schema)
1381 }
1382 for _, v := range chart.Vars {
1383 v.ID = fmt.Sprintf(v.ID, idx.name, idx.table, idx.db, idx.schema)
1384 }
1385 }
1386
1387 if err := c.Charts().Add(*charts...); err != nil {
1388 c.Warning(err)
1389 }
1390 }
1391
1392 func (c *Collector) removeIndexCharts(idx *indexMetrics) {
1393 prefix := fmt.Sprintf("index_%s_table_%s_db_%s_schema_%s", idx.name, idx.table, idx.db, idx.schema)
1394 for _, c := range *c.Charts() {
1395 if strings.HasPrefix(c.ID, prefix) {
1396 c.MarkRemove()
1397 c.MarkNotCreated()
1398 }
1399 }
1400 }