diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-26 08:15:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-26 08:15:20 +0000 |
commit | 87d772a7d708fec12f48cd8adc0dedff6e1025da (patch) | |
tree | 1fee344c64cc3f43074a01981e21126c8482a522 /src/go/plugin/go.d/modules/postgres/charts.go | |
parent | Adding upstream version 1.46.3. (diff) | |
download | netdata-87d772a7d708fec12f48cd8adc0dedff6e1025da.tar.xz netdata-87d772a7d708fec12f48cd8adc0dedff6e1025da.zip |
Adding upstream version 1.47.0.upstream/1.47.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/go/plugin/go.d/modules/postgres/charts.go')
-rw-r--r-- | src/go/plugin/go.d/modules/postgres/charts.go | 1400 |
1 files changed, 1400 insertions, 0 deletions
diff --git a/src/go/plugin/go.d/modules/postgres/charts.go b/src/go/plugin/go.d/modules/postgres/charts.go new file mode 100644 index 000000000..da9b04af0 --- /dev/null +++ b/src/go/plugin/go.d/modules/postgres/charts.go @@ -0,0 +1,1400 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package postgres + +import ( + "fmt" + "strings" + "time" + + "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module" +) + +const ( + prioConnectionsUtilization = module.Priority + iota + prioConnectionsUsage + prioConnectionsStateCount + prioDBConnectionsUtilization + prioDBConnectionsCount + + prioTransactionsDuration + prioDBTransactionsRatio + prioDBTransactionsRate + + prioQueriesDuration + + prioDBOpsFetchedRowsRatio + prioDBOpsReadRowsRate + prioDBOpsWriteRowsRate + prioDBTempFilesCreatedRate + prioDBTempFilesIORate + prioTableOpsRowsRate + prioTableOpsRowsHOTRatio + prioTableOpsRowsHOTRate + prioTableScansRate + prioTableScansRowsRate + + prioDBCacheIORatio + prioDBIORate + prioTableCacheIORatio + prioTableIORate + prioTableIndexCacheIORatio + prioTableIndexIORate + prioTableToastCacheIORatio + prioTableToastIORate + prioTableToastIndexCacheIORatio + prioTableToastIndexIORate + + prioDBSize + prioTableTotalSize + prioIndexSize + + prioTableBloatSizePerc + prioTableBloatSize + prioIndexBloatSizePerc + prioIndexBloatSize + + prioLocksUtilization + prioDBLocksHeldCount + prioDBLocksAwaitedCount + prioDBDeadlocksRate + + prioAutovacuumWorkersCount + prioTableAutovacuumSinceTime + prioTableVacuumSinceTime + prioTableAutoAnalyzeSinceTime + prioTableLastAnalyzeAgo + + prioCheckpointsRate + prioCheckpointsTime + prioBGWriterHaltsRate + prioBuffersIORate + prioBuffersBackendFsyncRate + prioBuffersAllocRate + prioTXIDExhaustionTowardsAutovacuumPerc + prioTXIDExhaustionPerc + prioTXIDExhaustionOldestTXIDNum + prioTableRowsDeadRatio + prioTableRowsCount + prioTableNullColumns + prioIndexUsageStatus + + prioReplicationAppWALLagSize + prioReplicationAppWALLagTime + prioReplicationSlotFilesCount + prioDBConflictsRate + prioDBConflictsReasonRate + + prioWALIORate + prioWALFilesCount + prioWALArchivingFilesCount + + prioDatabasesCount + prioCatalogRelationsCount + prioCatalogRelationsSize + + prioUptime +) + +var baseCharts = module.Charts{ + serverConnectionsUtilizationChart.Copy(), + serverConnectionsUsageChart.Copy(), + serverConnectionsStateCount.Copy(), + locksUtilization.Copy(), + checkpointsChart.Copy(), + checkpointWriteChart.Copy(), + buffersIORateChart.Copy(), + buffersAllocRateChart.Copy(), + bgWriterHaltsRateChart.Copy(), + buffersBackendFsyncRateChart.Copy(), + walIORateChart.Copy(), + autovacuumWorkersCountChart.Copy(), + txidExhaustionTowardsAutovacuumPercChart.Copy(), + txidExhaustionPercChart.Copy(), + txidExhaustionOldestTXIDNumChart.Copy(), + + catalogRelationSCountChart.Copy(), + catalogRelationsSizeChart.Copy(), + serverUptimeChart.Copy(), + databasesCountChart.Copy(), +} + +var walFilesCharts = module.Charts{ + walFilesCountChart.Copy(), + walArchivingFilesCountChart.Copy(), +} + +func (p *Postgres) addWALFilesCharts() { + charts := walFilesCharts.Copy() + + if err := p.Charts().Add(*charts...); err != nil { + p.Warning(err) + } +} + +var ( + serverConnectionsUtilizationChart = module.Chart{ + ID: "connections_utilization", + Title: "Connections utilization", + Units: "percentage", + Fam: "connections", + Ctx: "postgres.connections_utilization", + Priority: prioConnectionsUtilization, + Dims: module.Dims{ + {ID: "server_connections_utilization", Name: "used"}, + }, + } + serverConnectionsUsageChart = module.Chart{ + ID: "connections_usage", + Title: "Connections usage", + Units: "connections", + Fam: "connections", + Ctx: "postgres.connections_usage", + Priority: prioConnectionsUsage, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "server_connections_available", Name: "available"}, + {ID: "server_connections_used", Name: "used"}, + }, + } + serverConnectionsStateCount = module.Chart{ + ID: "connections_state", + Title: "Connections in each state", + Units: "connections", + Fam: "connections", + Ctx: "postgres.connections_state_count", + Priority: prioConnectionsStateCount, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "server_connections_state_active", Name: "active"}, + {ID: "server_connections_state_idle", Name: "idle"}, + {ID: "server_connections_state_idle_in_transaction", Name: "idle_in_transaction"}, + {ID: "server_connections_state_idle_in_transaction_aborted", Name: "idle_in_transaction_aborted"}, + {ID: "server_connections_state_fastpath_function_call", Name: "fastpath_function_call"}, + {ID: "server_connections_state_disabled", Name: "disabled"}, + }, + } + + locksUtilization = module.Chart{ + ID: "locks_utilization", + Title: "Acquired locks utilization", + Units: "percentage", + Fam: "locks", + Ctx: "postgres.locks_utilization", + Priority: prioLocksUtilization, + Dims: module.Dims{ + {ID: "locks_utilization", Name: "used"}, + }, + } + + checkpointsChart = module.Chart{ + ID: "checkpoints_rate", + Title: "Checkpoints", + Units: "checkpoints/s", + Fam: "maintenance", + Ctx: "postgres.checkpoints_rate", + Priority: prioCheckpointsRate, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "checkpoints_timed", Name: "scheduled", Algo: module.Incremental}, + {ID: "checkpoints_req", Name: "requested", Algo: module.Incremental}, + }, + } + // TODO: should be seconds, also it is units/s when using incremental... + checkpointWriteChart = module.Chart{ + ID: "checkpoints_time", + Title: "Checkpoint time", + Units: "milliseconds", + Fam: "maintenance", + Ctx: "postgres.checkpoints_time", + Priority: prioCheckpointsTime, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "checkpoint_write_time", Name: "write", Algo: module.Incremental}, + {ID: "checkpoint_sync_time", Name: "sync", Algo: module.Incremental}, + }, + } + bgWriterHaltsRateChart = module.Chart{ + ID: "bgwriter_halts_rate", + Title: "Background writer scan halts", + Units: "halts/s", + Fam: "maintenance", + Ctx: "postgres.bgwriter_halts_rate", + Priority: prioBGWriterHaltsRate, + Dims: module.Dims{ + {ID: "maxwritten_clean", Name: "maxwritten", Algo: module.Incremental}, + }, + } + + buffersIORateChart = module.Chart{ + ID: "buffers_io_rate", + Title: "Buffers written rate", + Units: "B/s", + Fam: "maintenance", + Ctx: "postgres.buffers_io_rate", + Priority: prioBuffersIORate, + Type: module.Area, + Dims: module.Dims{ + {ID: "buffers_checkpoint", Name: "checkpoint", Algo: module.Incremental}, + {ID: "buffers_backend", Name: "backend", Algo: module.Incremental}, + {ID: "buffers_clean", Name: "bgwriter", Algo: module.Incremental}, + }, + } + buffersBackendFsyncRateChart = module.Chart{ + ID: "buffers_backend_fsync_rate", + Title: "Backend fsync calls", + Units: "calls/s", + Fam: "maintenance", + Ctx: "postgres.buffers_backend_fsync_rate", + Priority: prioBuffersBackendFsyncRate, + Dims: module.Dims{ + {ID: "buffers_backend_fsync", Name: "fsync", Algo: module.Incremental}, + }, + } + buffersAllocRateChart = module.Chart{ + ID: "buffers_alloc_rate", + Title: "Buffers allocated", + Units: "B/s", + Fam: "maintenance", + Ctx: "postgres.buffers_allocated_rate", + Priority: prioBuffersAllocRate, + Dims: module.Dims{ + {ID: "buffers_alloc", Name: "allocated", Algo: module.Incremental}, + }, + } + + walIORateChart = module.Chart{ + ID: "wal_io_rate", + Title: "Write-Ahead Log writes", + Units: "B/s", + Fam: "wal", + Ctx: "postgres.wal_io_rate", + Priority: prioWALIORate, + Dims: module.Dims{ + {ID: "wal_writes", Name: "written", Algo: module.Incremental}, + }, + } + walFilesCountChart = module.Chart{ + ID: "wal_files_count", + Title: "Write-Ahead Log files", + Units: "files", + Fam: "wal", + Ctx: "postgres.wal_files_count", + Priority: prioWALFilesCount, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "wal_written_files", Name: "written"}, + {ID: "wal_recycled_files", Name: "recycled"}, + }, + } + + walArchivingFilesCountChart = module.Chart{ + ID: "wal_archiving_files_count", + Title: "Write-Ahead Log archived files", + Units: "files/s", + Fam: "wal", + Ctx: "postgres.wal_archiving_files_count", + Priority: prioWALArchivingFilesCount, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "wal_archive_files_ready_count", Name: "ready"}, + {ID: "wal_archive_files_done_count", Name: "done"}, + }, + } + + autovacuumWorkersCountChart = module.Chart{ + ID: "autovacuum_workers_count", + Title: "Autovacuum workers", + Units: "workers", + Fam: "vacuum and analyze", + Ctx: "postgres.autovacuum_workers_count", + Priority: prioAutovacuumWorkersCount, + Dims: module.Dims{ + {ID: "autovacuum_analyze", Name: "analyze"}, + {ID: "autovacuum_vacuum_analyze", Name: "vacuum_analyze"}, + {ID: "autovacuum_vacuum", Name: "vacuum"}, + {ID: "autovacuum_vacuum_freeze", Name: "vacuum_freeze"}, + {ID: "autovacuum_brin_summarize", Name: "brin_summarize"}, + }, + } + + txidExhaustionTowardsAutovacuumPercChart = module.Chart{ + ID: "txid_exhaustion_towards_autovacuum_perc", + Title: "Percent towards emergency autovacuum", + Units: "percentage", + Fam: "maintenance", + Ctx: "postgres.txid_exhaustion_towards_autovacuum_perc", + Priority: prioTXIDExhaustionTowardsAutovacuumPerc, + Dims: module.Dims{ + {ID: "percent_towards_emergency_autovacuum", Name: "emergency_autovacuum"}, + }, + } + txidExhaustionPercChart = module.Chart{ + ID: "txid_exhaustion_perc", + Title: "Percent towards transaction ID wraparound", + Units: "percentage", + Fam: "maintenance", + Ctx: "postgres.txid_exhaustion_perc", + Priority: prioTXIDExhaustionPerc, + Dims: module.Dims{ + {ID: "percent_towards_wraparound", Name: "txid_exhaustion"}, + }, + } + txidExhaustionOldestTXIDNumChart = module.Chart{ + ID: "txid_exhaustion_oldest_txid_num", + Title: "Oldest transaction XID", + Units: "xid", + Fam: "maintenance", + Ctx: "postgres.txid_exhaustion_oldest_txid_num", + Priority: prioTXIDExhaustionOldestTXIDNum, + Dims: module.Dims{ + {ID: "oldest_current_xid", Name: "xid"}, + }, + } + + catalogRelationSCountChart = module.Chart{ + ID: "catalog_relations_count", + Title: "Relation count", + Units: "relations", + Fam: "catalog", + Ctx: "postgres.catalog_relations_count", + Priority: prioCatalogRelationsCount, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "catalog_relkind_r_count", Name: "ordinary_table"}, + {ID: "catalog_relkind_i_count", Name: "index"}, + {ID: "catalog_relkind_S_count", Name: "sequence"}, + {ID: "catalog_relkind_t_count", Name: "toast_table"}, + {ID: "catalog_relkind_v_count", Name: "view"}, + {ID: "catalog_relkind_m_count", Name: "materialized_view"}, + {ID: "catalog_relkind_c_count", Name: "composite_type"}, + {ID: "catalog_relkind_f_count", Name: "foreign_table"}, + {ID: "catalog_relkind_p_count", Name: "partitioned_table"}, + {ID: "catalog_relkind_I_count", Name: "partitioned_index"}, + }, + } + catalogRelationsSizeChart = module.Chart{ + ID: "catalog_relations_size", + Title: "Relation size", + Units: "B", + Fam: "catalog", + Ctx: "postgres.catalog_relations_size", + Priority: prioCatalogRelationsSize, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "catalog_relkind_r_size", Name: "ordinary_table"}, + {ID: "catalog_relkind_i_size", Name: "index"}, + {ID: "catalog_relkind_S_size", Name: "sequence"}, + {ID: "catalog_relkind_t_size", Name: "toast_table"}, + {ID: "catalog_relkind_v_size", Name: "view"}, + {ID: "catalog_relkind_m_size", Name: "materialized_view"}, + {ID: "catalog_relkind_c_size", Name: "composite_type"}, + {ID: "catalog_relkind_f_size", Name: "foreign_table"}, + {ID: "catalog_relkind_p_size", Name: "partitioned_table"}, + {ID: "catalog_relkind_I_size", Name: "partitioned_index"}, + }, + } + + serverUptimeChart = module.Chart{ + ID: "server_uptime", + Title: "Uptime", + Units: "seconds", + Fam: "uptime", + Ctx: "postgres.uptime", + Priority: prioUptime, + Dims: module.Dims{ + {ID: "server_uptime", Name: "uptime"}, + }, + } + + databasesCountChart = module.Chart{ + ID: "databases_count", + Title: "Number of databases", + Units: "databases", + Fam: "catalog", + Ctx: "postgres.databases_count", + Priority: prioDatabasesCount, + Dims: module.Dims{ + {ID: "databases_count", Name: "databases"}, + }, + } + + transactionsDurationChartTmpl = module.Chart{ + ID: "transactions_duration", + Title: "Observed transactions time", + Units: "transactions/s", + Fam: "transactions", + Ctx: "postgres.transactions_duration", + Priority: prioTransactionsDuration, + Type: module.Stacked, + } + queriesDurationChartTmpl = module.Chart{ + ID: "queries_duration", + Title: "Observed active queries time", + Units: "queries/s", + Fam: "queries", + Ctx: "postgres.queries_duration", + Priority: prioQueriesDuration, + Type: module.Stacked, + } +) + +func newRunningTimeHistogramChart(tmpl module.Chart, prefix string, buckets []float64) (*module.Chart, error) { + chart := tmpl.Copy() + + for i, v := range buckets { + dim := &module.Dim{ + ID: fmt.Sprintf("%s_hist_bucket_%d", prefix, i+1), + Name: time.Duration(v * float64(time.Second)).String(), + Algo: module.Incremental, + } + if err := chart.AddDim(dim); err != nil { + return nil, err + } + } + + dim := &module.Dim{ + ID: fmt.Sprintf("%s_hist_bucket_inf", prefix), + Name: "+Inf", + Algo: module.Incremental, + } + if err := chart.AddDim(dim); err != nil { + return nil, err + } + + return chart, nil +} + +func (p *Postgres) addTransactionsRunTimeHistogramChart() { + chart, err := newRunningTimeHistogramChart( + transactionsDurationChartTmpl, + "transaction_running_time", + p.XactTimeHistogram, + ) + if err != nil { + p.Warning(err) + return + } + if err := p.Charts().Add(chart); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) addQueriesRunTimeHistogramChart() { + chart, err := newRunningTimeHistogramChart( + queriesDurationChartTmpl, + "query_running_time", + p.QueryTimeHistogram, + ) + if err != nil { + p.Warning(err) + return + } + if err := p.Charts().Add(chart); err != nil { + p.Warning(err) + } +} + +var ( + replicationStandbyAppCharts = module.Charts{ + replicationAppWALLagSizeChartTmpl.Copy(), + replicationAppWALLagTimeChartTmpl.Copy(), + } + replicationAppWALLagSizeChartTmpl = module.Chart{ + ID: "replication_app_%s_wal_lag_size", + Title: "Standby application WAL lag size", + Units: "B", + Fam: "replication", + Ctx: "postgres.replication_app_wal_lag_size", + Priority: prioReplicationAppWALLagSize, + Dims: module.Dims{ + {ID: "repl_standby_app_%s_wal_sent_lag_size", Name: "sent_lag"}, + {ID: "repl_standby_app_%s_wal_write_lag_size", Name: "write_lag"}, + {ID: "repl_standby_app_%s_wal_flush_lag_size", Name: "flush_lag"}, + {ID: "repl_standby_app_%s_wal_replay_lag_size", Name: "replay_lag"}, + }, + } + replicationAppWALLagTimeChartTmpl = module.Chart{ + ID: "replication_app_%s_wal_lag_time", + Title: "Standby application WAL lag time", + Units: "seconds", + Fam: "replication", + Ctx: "postgres.replication_app_wal_lag_time", + Priority: prioReplicationAppWALLagTime, + Dims: module.Dims{ + {ID: "repl_standby_app_%s_wal_write_lag_time", Name: "write_lag"}, + {ID: "repl_standby_app_%s_wal_flush_lag_time", Name: "flush_lag"}, + {ID: "repl_standby_app_%s_wal_replay_lag_time", Name: "replay_lag"}, + }, + } +) + +func newReplicationStandbyAppCharts(app string) *module.Charts { + charts := replicationStandbyAppCharts.Copy() + for _, c := range *charts { + c.ID = fmt.Sprintf(c.ID, app) + c.Labels = []module.Label{ + {Key: "application", Value: app}, + } + for _, d := range c.Dims { + d.ID = fmt.Sprintf(d.ID, app) + } + } + return charts +} + +func (p *Postgres) addNewReplicationStandbyAppCharts(app string) { + charts := newReplicationStandbyAppCharts(app) + if err := p.Charts().Add(*charts...); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) removeReplicationStandbyAppCharts(app string) { + prefix := fmt.Sprintf("replication_standby_app_%s_", app) + for _, c := range *p.Charts() { + if strings.HasPrefix(c.ID, prefix) { + c.MarkRemove() + c.MarkNotCreated() + } + } +} + +var ( + replicationSlotCharts = module.Charts{ + replicationSlotFilesCountChartTmpl.Copy(), + } + replicationSlotFilesCountChartTmpl = module.Chart{ + ID: "replication_slot_%s_files_count", + Title: "Replication slot files", + Units: "files", + Fam: "replication", + Ctx: "postgres.replication_slot_files_count", + Priority: prioReplicationSlotFilesCount, + Dims: module.Dims{ + {ID: "repl_slot_%s_replslot_wal_keep", Name: "wal_keep"}, + {ID: "repl_slot_%s_replslot_files", Name: "pg_replslot_files"}, + }, + } +) + +func newReplicationSlotCharts(slot string) *module.Charts { + charts := replicationSlotCharts.Copy() + for _, c := range *charts { + c.ID = fmt.Sprintf(c.ID, slot) + c.Labels = []module.Label{ + {Key: "slot", Value: slot}, + } + for _, d := range c.Dims { + d.ID = fmt.Sprintf(d.ID, slot) + } + } + return charts +} + +func (p *Postgres) addNewReplicationSlotCharts(slot string) { + charts := newReplicationSlotCharts(slot) + if err := p.Charts().Add(*charts...); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) removeReplicationSlotCharts(slot string) { + prefix := fmt.Sprintf("replication_slot_%s_", slot) + for _, c := range *p.Charts() { + if strings.HasPrefix(c.ID, prefix) { + c.MarkRemove() + c.MarkNotCreated() + } + } +} + +var ( + dbChartsTmpl = module.Charts{ + dbTransactionsRatioChartTmpl.Copy(), + dbTransactionsRateChartTmpl.Copy(), + dbConnectionsUtilizationChartTmpl.Copy(), + dbConnectionsCountChartTmpl.Copy(), + dbCacheIORatioChartTmpl.Copy(), + dbIORateChartTmpl.Copy(), + dbOpsFetchedRowsRatioChartTmpl.Copy(), + dbOpsReadRowsRateChartTmpl.Copy(), + dbOpsWriteRowsRateChartTmpl.Copy(), + dbDeadlocksRateChartTmpl.Copy(), + dbLocksHeldCountChartTmpl.Copy(), + dbLocksAwaitedCountChartTmpl.Copy(), + dbTempFilesCreatedRateChartTmpl.Copy(), + dbTempFilesIORateChartTmpl.Copy(), + dbSizeChartTmpl.Copy(), + } + dbTransactionsRatioChartTmpl = module.Chart{ + ID: "db_%s_transactions_ratio", + Title: "Database transactions ratio", + Units: "percentage", + Fam: "transactions", + Ctx: "postgres.db_transactions_ratio", + Priority: prioDBTransactionsRatio, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "db_%s_xact_commit", Name: "committed", Algo: module.PercentOfIncremental}, + {ID: "db_%s_xact_rollback", Name: "rollback", Algo: module.PercentOfIncremental}, + }, + } + dbTransactionsRateChartTmpl = module.Chart{ + ID: "db_%s_transactions_rate", + Title: "Database transactions", + Units: "transactions/s", + Fam: "transactions", + Ctx: "postgres.db_transactions_rate", + Priority: prioDBTransactionsRate, + Dims: module.Dims{ + {ID: "db_%s_xact_commit", Name: "committed", Algo: module.Incremental}, + {ID: "db_%s_xact_rollback", Name: "rollback", Algo: module.Incremental}, + }, + } + dbConnectionsUtilizationChartTmpl = module.Chart{ + ID: "db_%s_connections_utilization", + Title: "Database connections utilization", + Units: "percentage", + Fam: "connections", + Ctx: "postgres.db_connections_utilization", + Priority: prioDBConnectionsUtilization, + Dims: module.Dims{ + {ID: "db_%s_numbackends_utilization", Name: "used"}, + }, + } + dbConnectionsCountChartTmpl = module.Chart{ + ID: "db_%s_connections", + Title: "Database connections", + Units: "connections", + Fam: "connections", + Ctx: "postgres.db_connections_count", + Priority: prioDBConnectionsCount, + Dims: module.Dims{ + {ID: "db_%s_numbackends", Name: "connections"}, + }, + } + dbCacheIORatioChartTmpl = module.Chart{ + ID: "db_%s_cache_io_ratio", + Title: "Database buffer cache miss ratio", + Units: "percentage", + Fam: "cache", + Ctx: "postgres.db_cache_io_ratio", + Priority: prioDBCacheIORatio, + Dims: module.Dims{ + {ID: "db_%s_blks_read_perc", Name: "miss"}, + }, + } + dbIORateChartTmpl = module.Chart{ + ID: "db_%s_io_rate", + Title: "Database reads", + Units: "B/s", + Fam: "cache", + Ctx: "postgres.db_io_rate", + Priority: prioDBIORate, + Type: module.Area, + Dims: module.Dims{ + {ID: "db_%s_blks_hit", Name: "memory", Algo: module.Incremental}, + {ID: "db_%s_blks_read", Name: "disk", Algo: module.Incremental}, + }, + } + dbOpsFetchedRowsRatioChartTmpl = module.Chart{ + ID: "db_%s_db_ops_fetched_rows_ratio", + Title: "Database rows fetched ratio", + Units: "percentage", + Fam: "throughput", + Ctx: "postgres.db_ops_fetched_rows_ratio", + Priority: prioDBOpsFetchedRowsRatio, + Dims: module.Dims{ + {ID: "db_%s_tup_fetched_perc", Name: "fetched"}, + }, + } + dbOpsReadRowsRateChartTmpl = module.Chart{ + ID: "db_%s_ops_read_rows_rate", + Title: "Database rows read", + Units: "rows/s", + Fam: "throughput", + Ctx: "postgres.db_ops_read_rows_rate", + Priority: prioDBOpsReadRowsRate, + Dims: module.Dims{ + {ID: "db_%s_tup_returned", Name: "returned", Algo: module.Incremental}, + {ID: "db_%s_tup_fetched", Name: "fetched", Algo: module.Incremental}, + }, + } + dbOpsWriteRowsRateChartTmpl = module.Chart{ + ID: "db_%s_ops_write_rows_rate", + Title: "Database rows written", + Units: "rows/s", + Fam: "throughput", + Ctx: "postgres.db_ops_write_rows_rate", + Priority: prioDBOpsWriteRowsRate, + Dims: module.Dims{ + {ID: "db_%s_tup_inserted", Name: "inserted", Algo: module.Incremental}, + {ID: "db_%s_tup_deleted", Name: "deleted", Algo: module.Incremental}, + {ID: "db_%s_tup_updated", Name: "updated", Algo: module.Incremental}, + }, + } + dbConflictsRateChartTmpl = module.Chart{ + ID: "db_%s_conflicts_rate", + Title: "Database canceled queries", + Units: "queries/s", + Fam: "replication", + Ctx: "postgres.db_conflicts_rate", + Priority: prioDBConflictsRate, + Dims: module.Dims{ + {ID: "db_%s_conflicts", Name: "conflicts", Algo: module.Incremental}, + }, + } + dbConflictsReasonRateChartTmpl = module.Chart{ + ID: "db_%s_conflicts_reason_rate", + Title: "Database canceled queries by reason", + Units: "queries/s", + Fam: "replication", + Ctx: "postgres.db_conflicts_reason_rate", + Priority: prioDBConflictsReasonRate, + Dims: module.Dims{ + {ID: "db_%s_confl_tablespace", Name: "tablespace", Algo: module.Incremental}, + {ID: "db_%s_confl_lock", Name: "lock", Algo: module.Incremental}, + {ID: "db_%s_confl_snapshot", Name: "snapshot", Algo: module.Incremental}, + {ID: "db_%s_confl_bufferpin", Name: "bufferpin", Algo: module.Incremental}, + {ID: "db_%s_confl_deadlock", Name: "deadlock", Algo: module.Incremental}, + }, + } + dbDeadlocksRateChartTmpl = module.Chart{ + ID: "db_%s_deadlocks_rate", + Title: "Database deadlocks", + Units: "deadlocks/s", + Fam: "locks", + Ctx: "postgres.db_deadlocks_rate", + Priority: prioDBDeadlocksRate, + Dims: module.Dims{ + {ID: "db_%s_deadlocks", Name: "deadlocks", Algo: module.Incremental}, + }, + } + dbLocksHeldCountChartTmpl = module.Chart{ + ID: "db_%s_locks_held", + Title: "Database locks held", + Units: "locks", + Fam: "locks", + Ctx: "postgres.db_locks_held_count", + Priority: prioDBLocksHeldCount, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "db_%s_lock_mode_AccessShareLock_held", Name: "access_share"}, + {ID: "db_%s_lock_mode_RowShareLock_held", Name: "row_share"}, + {ID: "db_%s_lock_mode_RowExclusiveLock_held", Name: "row_exclusive"}, + {ID: "db_%s_lock_mode_ShareUpdateExclusiveLock_held", Name: "share_update"}, + {ID: "db_%s_lock_mode_ShareLock_held", Name: "share"}, + {ID: "db_%s_lock_mode_ShareRowExclusiveLock_held", Name: "share_row_exclusive"}, + {ID: "db_%s_lock_mode_ExclusiveLock_held", Name: "exclusive"}, + {ID: "db_%s_lock_mode_AccessExclusiveLock_held", Name: "access_exclusive"}, + }, + } + dbLocksAwaitedCountChartTmpl = module.Chart{ + ID: "db_%s_locks_awaited_count", + Title: "Database locks awaited", + Units: "locks", + Fam: "locks", + Ctx: "postgres.db_locks_awaited_count", + Priority: prioDBLocksAwaitedCount, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "db_%s_lock_mode_AccessShareLock_awaited", Name: "access_share"}, + {ID: "db_%s_lock_mode_RowShareLock_awaited", Name: "row_share"}, + {ID: "db_%s_lock_mode_RowExclusiveLock_awaited", Name: "row_exclusive"}, + {ID: "db_%s_lock_mode_ShareUpdateExclusiveLock_awaited", Name: "share_update"}, + {ID: "db_%s_lock_mode_ShareLock_awaited", Name: "share"}, + {ID: "db_%s_lock_mode_ShareRowExclusiveLock_awaited", Name: "share_row_exclusive"}, + {ID: "db_%s_lock_mode_ExclusiveLock_awaited", Name: "exclusive"}, + {ID: "db_%s_lock_mode_AccessExclusiveLock_awaited", Name: "access_exclusive"}, + }, + } + dbTempFilesCreatedRateChartTmpl = module.Chart{ + ID: "db_%s_temp_files_files_created_rate", + Title: "Database created temporary files", + Units: "files/s", + Fam: "throughput", + Ctx: "postgres.db_temp_files_created_rate", + Priority: prioDBTempFilesCreatedRate, + Dims: module.Dims{ + {ID: "db_%s_temp_files", Name: "created", Algo: module.Incremental}, + }, + } + dbTempFilesIORateChartTmpl = module.Chart{ + ID: "db_%s_temp_files_io_rate", + Title: "Database temporary files data written to disk", + Units: "B/s", + Fam: "throughput", + Ctx: "postgres.db_temp_files_io_rate", + Priority: prioDBTempFilesIORate, + Dims: module.Dims{ + {ID: "db_%s_temp_bytes", Name: "written", Algo: module.Incremental}, + }, + } + dbSizeChartTmpl = module.Chart{ + ID: "db_%s_size", + Title: "Database size", + Units: "B", + Fam: "size", + Ctx: "postgres.db_size", + Priority: prioDBSize, + Dims: module.Dims{ + {ID: "db_%s_size", Name: "size"}, + }, + } +) + +func (p *Postgres) addDBConflictsCharts(db *dbMetrics) { + tmpl := module.Charts{ + dbConflictsRateChartTmpl.Copy(), + dbConflictsReasonRateChartTmpl.Copy(), + } + charts := newDatabaseCharts(tmpl.Copy(), db) + + if err := p.Charts().Add(*charts...); err != nil { + p.Warning(err) + } +} + +func newDatabaseCharts(tmpl *module.Charts, db *dbMetrics) *module.Charts { + charts := tmpl.Copy() + for _, c := range *charts { + c.ID = fmt.Sprintf(c.ID, db.name) + c.Labels = []module.Label{ + {Key: "database", Value: db.name}, + } + for _, d := range c.Dims { + d.ID = fmt.Sprintf(d.ID, db.name) + } + } + return charts +} + +func (p *Postgres) addNewDatabaseCharts(db *dbMetrics) { + charts := newDatabaseCharts(dbChartsTmpl.Copy(), db) + + if db.size == nil { + _ = charts.Remove(fmt.Sprintf(dbSizeChartTmpl.ID, db.name)) + } + + if err := p.Charts().Add(*charts...); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) removeDatabaseCharts(db *dbMetrics) { + prefix := fmt.Sprintf("db_%s_", db.name) + for _, c := range *p.Charts() { + if strings.HasPrefix(c.ID, prefix) { + c.MarkRemove() + c.MarkNotCreated() + } + } +} + +var ( + tableChartsTmpl = module.Charts{ + tableRowsCountChartTmpl.Copy(), + tableDeadRowsDeadRatioChartTmpl.Copy(), + tableOpsRowsRateChartTmpl.Copy(), + tableOpsRowsHOTRatioChartTmpl.Copy(), + tableOpsRowsHOTRateChartTmpl.Copy(), + tableScansRateChartTmpl.Copy(), + tableScansRowsRateChartTmpl.Copy(), + tableNullColumnsCountChartTmpl.Copy(), + tableTotalSizeChartTmpl.Copy(), + tableBloatSizePercChartTmpl.Copy(), + tableBloatSizeChartTmpl.Copy(), + } + + tableDeadRowsDeadRatioChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_rows_dead_ratio", + Title: "Table dead rows", + Units: "%", + Fam: "maintenance", + Ctx: "postgres.table_rows_dead_ratio", + Priority: prioTableRowsDeadRatio, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_n_dead_tup_perc", Name: "dead"}, + }, + } + tableRowsCountChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_rows_count", + Title: "Table total rows", + Units: "rows", + Fam: "maintenance", + Ctx: "postgres.table_rows_count", + Priority: prioTableRowsCount, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_n_live_tup", Name: "live"}, + {ID: "table_%s_db_%s_schema_%s_n_dead_tup", Name: "dead"}, + }, + } + tableOpsRowsRateChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_ops_rows_rate", + Title: "Table throughput", + Units: "rows/s", + Fam: "throughput", + Ctx: "postgres.table_ops_rows_rate", + Priority: prioTableOpsRowsRate, + Type: module.Stacked, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_n_tup_ins", Name: "inserted", Algo: module.Incremental}, + {ID: "table_%s_db_%s_schema_%s_n_tup_del", Name: "deleted", Algo: module.Incremental}, + {ID: "table_%s_db_%s_schema_%s_n_tup_upd", Name: "updated", Algo: module.Incremental}, + }, + } + tableOpsRowsHOTRatioChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_ops_rows_hot_ratio", + Title: "Table HOT updates ratio", + Units: "percentage", + Fam: "throughput", + Ctx: "postgres.table_ops_rows_hot_ratio", + Priority: prioTableOpsRowsHOTRatio, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_n_tup_hot_upd_perc", Name: "hot"}, + }, + } + tableOpsRowsHOTRateChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_ops_rows_hot_rate", + Title: "Table HOT updates", + Units: "rows/s", + Fam: "throughput", + Ctx: "postgres.table_ops_rows_hot_rate", + Priority: prioTableOpsRowsHOTRate, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_n_tup_hot_upd", Name: "hot", Algo: module.Incremental}, + }, + } + tableCacheIORatioChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_cache_io_ratio", + Title: "Table I/O cache miss ratio", + Units: "percentage", + Fam: "cache", + Ctx: "postgres.table_cache_io_ratio", + Priority: prioTableCacheIORatio, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_heap_blks_read_perc", Name: "miss"}, + }, + } + tableIORateChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_io_rate", + Title: "Table I/O", + Units: "B/s", + Fam: "cache", + Ctx: "postgres.table_io_rate", + Priority: prioTableIORate, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_heap_blks_hit", Name: "memory", Algo: module.Incremental}, + {ID: "table_%s_db_%s_schema_%s_heap_blks_read", Name: "disk", Algo: module.Incremental}, + }, + } + tableIndexCacheIORatioChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_index_cache_io_ratio", + Title: "Table index I/O cache miss ratio", + Units: "percentage", + Fam: "cache", + Ctx: "postgres.table_index_cache_io_ratio", + Priority: prioTableIndexCacheIORatio, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_idx_blks_read_perc", Name: "miss", Algo: module.Incremental}, + }, + } + tableIndexIORateChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_index_io_rate", + Title: "Table index I/O", + Units: "B/s", + Fam: "cache", + Ctx: "postgres.table_index_io_rate", + Priority: prioTableIndexIORate, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_idx_blks_hit", Name: "memory", Algo: module.Incremental}, + {ID: "table_%s_db_%s_schema_%s_idx_blks_read", Name: "disk", Algo: module.Incremental}, + }, + } + tableTOASCacheIORatioChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_toast_cache_io_ratio", + Title: "Table TOAST I/O cache miss ratio", + Units: "percentage", + Fam: "cache", + Ctx: "postgres.table_toast_cache_io_ratio", + Priority: prioTableToastCacheIORatio, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_toast_blks_read_perc", Name: "miss", Algo: module.Incremental}, + }, + } + tableTOASTIORateChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_toast_io_rate", + Title: "Table TOAST I/O", + Units: "B/s", + Fam: "cache", + Ctx: "postgres.table_toast_io_rate", + Priority: prioTableToastIORate, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_toast_blks_hit", Name: "memory", Algo: module.Incremental}, + {ID: "table_%s_db_%s_schema_%s_toast_blks_read", Name: "disk", Algo: module.Incremental}, + }, + } + tableTOASTIndexCacheIORatioChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_toast_index_cache_io_ratio", + Title: "Table TOAST index I/O cache miss ratio", + Units: "percentage", + Fam: "cache", + Ctx: "postgres.table_toast_index_cache_io_ratio", + Priority: prioTableToastIndexCacheIORatio, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_tidx_blks_read_perc", Name: "miss", Algo: module.Incremental}, + }, + } + tableTOASTIndexIORateChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_toast_index_io_rate", + Title: "Table TOAST index I/O", + Units: "B/s", + Fam: "cache", + Ctx: "postgres.table_toast_index_io_rate", + Priority: prioTableToastIndexIORate, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_tidx_blks_hit", Name: "memory", Algo: module.Incremental}, + {ID: "table_%s_db_%s_schema_%s_tidx_blks_read", Name: "disk", Algo: module.Incremental}, + }, + } + tableScansRateChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_scans_rate", + Title: "Table scans", + Units: "scans/s", + Fam: "throughput", + Ctx: "postgres.table_scans_rate", + Priority: prioTableScansRate, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_idx_scan", Name: "index", Algo: module.Incremental}, + {ID: "table_%s_db_%s_schema_%s_seq_scan", Name: "sequential", Algo: module.Incremental}, + }, + } + tableScansRowsRateChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_scans_rows_rate", + Title: "Table live rows fetched by scans", + Units: "rows/s", + Fam: "throughput", + Ctx: "postgres.table_scans_rows_rate", + Priority: prioTableScansRowsRate, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_idx_tup_fetch", Name: "index", Algo: module.Incremental}, + {ID: "table_%s_db_%s_schema_%s_seq_tup_read", Name: "sequential", Algo: module.Incremental}, + }, + } + tableAutoVacuumSinceTimeChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_autovacuum_since_time", + Title: "Table time since last auto VACUUM", + Units: "seconds", + Fam: "vacuum and analyze", + Ctx: "postgres.table_autovacuum_since_time", + Priority: prioTableAutovacuumSinceTime, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_last_autovacuum_ago", Name: "time"}, + }, + } + tableVacuumSinceTimeChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_vacuum_since_time", + Title: "Table time since last manual VACUUM", + Units: "seconds", + Fam: "vacuum and analyze", + Ctx: "postgres.table_vacuum_since_time", + Priority: prioTableVacuumSinceTime, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_last_vacuum_ago", Name: "time"}, + }, + } + tableAutoAnalyzeSinceTimeChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_autoanalyze_since_time", + Title: "Table time since last auto ANALYZE", + Units: "seconds", + Fam: "vacuum and analyze", + Ctx: "postgres.table_autoanalyze_since_time", + Priority: prioTableAutoAnalyzeSinceTime, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_last_autoanalyze_ago", Name: "time"}, + }, + } + tableAnalyzeSinceTimeChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_analyze_since_time", + Title: "Table time since last manual ANALYZE", + Units: "seconds", + Fam: "vacuum and analyze", + Ctx: "postgres.table_analyze_since_time", + Priority: prioTableLastAnalyzeAgo, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_last_analyze_ago", Name: "time"}, + }, + } + tableNullColumnsCountChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_null_columns_count", + Title: "Table null columns", + Units: "columns", + Fam: "maintenance", + Ctx: "postgres.table_null_columns_count", + Priority: prioTableNullColumns, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_null_columns", Name: "null"}, + }, + } + tableTotalSizeChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_total_size", + Title: "Table total size", + Units: "B", + Fam: "size", + Ctx: "postgres.table_total_size", + Priority: prioTableTotalSize, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_total_size", Name: "size"}, + }, + } + tableBloatSizePercChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_bloat_size_perc", + Title: "Table bloat size percentage", + Units: "percentage", + Fam: "bloat", + Ctx: "postgres.table_bloat_size_perc", + Priority: prioTableBloatSizePerc, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_bloat_size_perc", Name: "bloat"}, + }, + Vars: module.Vars{ + {ID: "table_%s_db_%s_schema_%s_total_size", Name: "table_size"}, + }, + } + tableBloatSizeChartTmpl = module.Chart{ + ID: "table_%s_db_%s_schema_%s_bloat_size", + Title: "Table bloat size", + Units: "B", + Fam: "bloat", + Ctx: "postgres.table_bloat_size", + Priority: prioTableBloatSize, + Dims: module.Dims{ + {ID: "table_%s_db_%s_schema_%s_bloat_size", Name: "bloat"}, + }, + } +) + +func newTableCharts(tbl *tableMetrics) *module.Charts { + charts := tableChartsTmpl.Copy() + + if tbl.bloatSize == nil { + _ = charts.Remove(tableBloatSizeChartTmpl.ID) + _ = charts.Remove(tableBloatSizePercChartTmpl.ID) + } + + for i, chart := range *charts { + (*charts)[i] = newTableChart(chart, tbl) + } + + return charts +} + +func newTableChart(chart *module.Chart, tbl *tableMetrics) *module.Chart { + chart = chart.Copy() + chart.ID = fmt.Sprintf(chart.ID, tbl.name, tbl.db, tbl.schema) + chart.Labels = []module.Label{ + {Key: "database", Value: tbl.db}, + {Key: "schema", Value: tbl.schema}, + {Key: "table", Value: tbl.name}, + {Key: "parent_table", Value: tbl.parentName}, + } + for _, d := range chart.Dims { + d.ID = fmt.Sprintf(d.ID, tbl.name, tbl.db, tbl.schema) + } + for _, v := range chart.Vars { + v.ID = fmt.Sprintf(v.ID, tbl.name, tbl.db, tbl.schema) + } + return chart +} + +func (p *Postgres) addNewTableCharts(tbl *tableMetrics) { + charts := newTableCharts(tbl) + if err := p.Charts().Add(*charts...); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) addTableLastAutoVacuumAgoChart(tbl *tableMetrics) { + chart := newTableChart(tableAutoVacuumSinceTimeChartTmpl.Copy(), tbl) + + if err := p.Charts().Add(chart); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) addTableLastVacuumAgoChart(tbl *tableMetrics) { + chart := newTableChart(tableVacuumSinceTimeChartTmpl.Copy(), tbl) + + if err := p.Charts().Add(chart); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) addTableLastAutoAnalyzeAgoChart(tbl *tableMetrics) { + chart := newTableChart(tableAutoAnalyzeSinceTimeChartTmpl.Copy(), tbl) + + if err := p.Charts().Add(chart); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) addTableLastAnalyzeAgoChart(tbl *tableMetrics) { + chart := newTableChart(tableAnalyzeSinceTimeChartTmpl.Copy(), tbl) + + if err := p.Charts().Add(chart); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) addTableIOChartsCharts(tbl *tableMetrics) { + charts := module.Charts{ + newTableChart(tableCacheIORatioChartTmpl.Copy(), tbl), + newTableChart(tableIORateChartTmpl.Copy(), tbl), + } + + if err := p.Charts().Add(charts...); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) addTableIndexIOCharts(tbl *tableMetrics) { + charts := module.Charts{ + newTableChart(tableIndexCacheIORatioChartTmpl.Copy(), tbl), + newTableChart(tableIndexIORateChartTmpl.Copy(), tbl), + } + + if err := p.Charts().Add(charts...); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) addTableTOASTIOCharts(tbl *tableMetrics) { + charts := module.Charts{ + newTableChart(tableTOASCacheIORatioChartTmpl.Copy(), tbl), + newTableChart(tableTOASTIORateChartTmpl.Copy(), tbl), + } + + if err := p.Charts().Add(charts...); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) addTableTOASTIndexIOCharts(tbl *tableMetrics) { + charts := module.Charts{ + newTableChart(tableTOASTIndexCacheIORatioChartTmpl.Copy(), tbl), + newTableChart(tableTOASTIndexIORateChartTmpl.Copy(), tbl), + } + + if err := p.Charts().Add(charts...); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) removeTableCharts(tbl *tableMetrics) { + prefix := fmt.Sprintf("table_%s_db_%s_schema_%s", tbl.name, tbl.db, tbl.schema) + for _, c := range *p.Charts() { + if strings.HasPrefix(c.ID, prefix) { + c.MarkRemove() + c.MarkNotCreated() + } + } +} + +var ( + indexChartsTmpl = module.Charts{ + indexSizeChartTmpl.Copy(), + indexBloatSizePercChartTmpl.Copy(), + indexBloatSizeChartTmpl.Copy(), + indexUsageStatusChartTmpl.Copy(), + } + indexSizeChartTmpl = module.Chart{ + ID: "index_%s_table_%s_db_%s_schema_%s_size", + Title: "Index size", + Units: "B", + Fam: "size", + Ctx: "postgres.index_size", + Priority: prioIndexSize, + Dims: module.Dims{ + {ID: "index_%s_table_%s_db_%s_schema_%s_size", Name: "size"}, + }, + } + indexBloatSizePercChartTmpl = module.Chart{ + ID: "index_%s_table_%s_db_%s_schema_%s_bloat_size_perc", + Title: "Index bloat size percentage", + Units: "percentage", + Fam: "bloat", + Ctx: "postgres.index_bloat_size_perc", + Priority: prioIndexBloatSizePerc, + Dims: module.Dims{ + {ID: "index_%s_table_%s_db_%s_schema_%s_bloat_size_perc", Name: "bloat"}, + }, + Vars: module.Vars{ + {ID: "index_%s_table_%s_db_%s_schema_%s_size", Name: "index_size"}, + }, + } + indexBloatSizeChartTmpl = module.Chart{ + ID: "index_%s_table_%s_db_%s_schema_%s_bloat_size", + Title: "Index bloat size", + Units: "B", + Fam: "bloat", + Ctx: "postgres.index_bloat_size", + Priority: prioIndexBloatSize, + Dims: module.Dims{ + {ID: "index_%s_table_%s_db_%s_schema_%s_bloat_size", Name: "bloat"}, + }, + } + indexUsageStatusChartTmpl = module.Chart{ + ID: "index_%s_table_%s_db_%s_schema_%s_usage_status", + Title: "Index usage status", + Units: "status", + Fam: "maintenance", + Ctx: "postgres.index_usage_status", + Priority: prioIndexUsageStatus, + Dims: module.Dims{ + {ID: "index_%s_table_%s_db_%s_schema_%s_usage_status_used", Name: "used"}, + {ID: "index_%s_table_%s_db_%s_schema_%s_usage_status_unused", Name: "unused"}, + }, + } +) + +func (p *Postgres) addNewIndexCharts(idx *indexMetrics) { + charts := indexChartsTmpl.Copy() + + if idx.bloatSize == nil { + _ = charts.Remove(indexBloatSizeChartTmpl.ID) + _ = charts.Remove(indexBloatSizePercChartTmpl.ID) + } + + for _, chart := range *charts { + chart.ID = fmt.Sprintf(chart.ID, idx.name, idx.table, idx.db, idx.schema) + chart.Labels = []module.Label{ + {Key: "database", Value: idx.db}, + {Key: "schema", Value: idx.schema}, + {Key: "table", Value: idx.table}, + {Key: "parent_table", Value: idx.parentTable}, + {Key: "index", Value: idx.name}, + } + for _, d := range chart.Dims { + d.ID = fmt.Sprintf(d.ID, idx.name, idx.table, idx.db, idx.schema) + } + for _, v := range chart.Vars { + v.ID = fmt.Sprintf(v.ID, idx.name, idx.table, idx.db, idx.schema) + } + } + + if err := p.Charts().Add(*charts...); err != nil { + p.Warning(err) + } +} + +func (p *Postgres) removeIndexCharts(idx *indexMetrics) { + prefix := fmt.Sprintf("index_%s_table_%s_db_%s_schema_%s", idx.name, idx.table, idx.db, idx.schema) + for _, c := range *p.Charts() { + if strings.HasPrefix(c.ID, prefix) { + c.MarkRemove() + c.MarkNotCreated() + } + } +} |