diff --git a/collector/pg_stat_user_tables.go b/collector/pg_stat_user_tables.go index 254d76a6..97c7104a 100644 --- a/collector/pg_stat_user_tables.go +++ b/collector/pg_stat_user_tables.go @@ -36,6 +36,24 @@ func NewPGStatUserTablesCollector(config collectorConfig) (Collector, error) { } var ( + statUserTablesRelPages = prometheus.NewDesc( + prometheus.BuildFQName(namespace, userTableSubsystem, "relpages"), + "Estimated number of live pages", + []string{"datname", "schemaname", "relname"}, + prometheus.Labels{}, + ) + statUserTablesRelSize = prometheus.NewDesc( + prometheus.BuildFQName(namespace, userTableSubsystem, "relsize_bytes"), + "Estimated table size", + []string{"datname", "schemaname", "relname"}, + prometheus.Labels{}, + ) + statUserTablesRelSizeTotal = prometheus.NewDesc( + prometheus.BuildFQName(namespace, userTableSubsystem, "relsize_total_bytes"), + "Total disk space used by this table, in bytes, including all indexes and TOAST data", + []string{"datname", "schemaname", "relname"}, + prometheus.Labels{}, + ) statUserTablesSeqScan = prometheus.NewDesc( prometheus.BuildFQName(namespace, userTableSubsystem, "seq_scan"), "Number of sequential scans initiated on this table", @@ -150,17 +168,14 @@ var ( []string{"datname", "schemaname", "relname"}, prometheus.Labels{}, ) - statUserTablesTotalSize = prometheus.NewDesc( - prometheus.BuildFQName(namespace, userTableSubsystem, "size_bytes"), - "Total disk space used by this table, in bytes, including all indexes and TOAST data", - []string{"datname", "schemaname", "relname"}, - prometheus.Labels{}, - ) statUserTablesQuery = `SELECT current_database() datname, schemaname, - relname, + pg_stat_user_tables.relname, + relpages, + pg_relation_size(relid) AS relsize, + pg_total_relation_size(relid) as relsize_total, seq_scan, seq_tup_read, idx_scan, @@ -179,10 +194,11 @@ var ( vacuum_count, autovacuum_count, analyze_count, - autoanalyze_count, - pg_total_relation_size(relid) as total_size + autoanalyze_count FROM - pg_stat_user_tables` + pg_stat_user_tables + JOIN + pg_class ON pg_stat_user_tables.relid = pg_class.oid` ) func (c *PGStatUserTablesCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error { @@ -197,11 +213,11 @@ func (c *PGStatUserTablesCollector) Update(ctx context.Context, instance *instan for rows.Next() { var datname, schemaname, relname sql.NullString - var seqScan, seqTupRead, idxScan, idxTupFetch, nTupIns, nTupUpd, nTupDel, nTupHotUpd, nLiveTup, nDeadTup, - nModSinceAnalyze, vacuumCount, autovacuumCount, analyzeCount, autoanalyzeCount, totalSize sql.NullInt64 + var relPages, relSize, relSizeTotal, seqScan, seqTupRead, idxScan, idxTupFetch, nTupIns, nTupUpd, nTupDel, nTupHotUpd, nLiveTup, nDeadTup, + nModSinceAnalyze, vacuumCount, autovacuumCount, analyzeCount, autoanalyzeCount sql.NullInt64 var lastVacuum, lastAutovacuum, lastAnalyze, lastAutoanalyze sql.NullTime - if err := rows.Scan(&datname, &schemaname, &relname, &seqScan, &seqTupRead, &idxScan, &idxTupFetch, &nTupIns, &nTupUpd, &nTupDel, &nTupHotUpd, &nLiveTup, &nDeadTup, &nModSinceAnalyze, &lastVacuum, &lastAutovacuum, &lastAnalyze, &lastAutoanalyze, &vacuumCount, &autovacuumCount, &analyzeCount, &autoanalyzeCount, &totalSize); err != nil { + if err := rows.Scan(&datname, &schemaname, &relname, &relPages, &relSize, &relSizeTotal, &seqScan, &seqTupRead, &idxScan, &idxTupFetch, &nTupIns, &nTupUpd, &nTupDel, &nTupHotUpd, &nLiveTup, &nDeadTup, &nModSinceAnalyze, &lastVacuum, &lastAutovacuum, &lastAnalyze, &lastAutoanalyze, &vacuumCount, &autovacuumCount, &analyzeCount, &autoanalyzeCount); err != nil { return err } @@ -218,6 +234,39 @@ func (c *PGStatUserTablesCollector) Update(ctx context.Context, instance *instan relnameLabel = relname.String } + relPagesMetric := 0.0 + if relPages.Valid { + relPagesMetric = float64(relPages.Int64) + } + ch <- prometheus.MustNewConstMetric( + statUserTablesRelPages, + prometheus.GaugeValue, + relPagesMetric, + datnameLabel, schemanameLabel, relnameLabel, + ) + + relSizeMetric := 0.0 + if relSize.Valid { + relSizeMetric = float64(relSize.Int64) + } + ch <- prometheus.MustNewConstMetric( + statUserTablesRelSize, + prometheus.GaugeValue, + relSizeMetric, + datnameLabel, schemanameLabel, relnameLabel, + ) + + relSizeTotalMetric := 0.0 + if relSizeTotal.Valid { + relSizeTotalMetric = float64(relSizeTotal.Int64) + } + ch <- prometheus.MustNewConstMetric( + statUserTablesRelSizeTotal, + prometheus.GaugeValue, + relSizeTotalMetric, + datnameLabel, schemanameLabel, relnameLabel, + ) + seqScanMetric := 0.0 if seqScan.Valid { seqScanMetric = float64(seqScan.Int64) @@ -426,17 +475,6 @@ func (c *PGStatUserTablesCollector) Update(ctx context.Context, instance *instan autoanalyzeCountMetric, datnameLabel, schemanameLabel, relnameLabel, ) - - totalSizeMetric := 0.0 - if totalSize.Valid { - totalSizeMetric = float64(totalSize.Int64) - } - ch <- prometheus.MustNewConstMetric( - statUserTablesTotalSize, - prometheus.GaugeValue, - totalSizeMetric, - datnameLabel, schemanameLabel, relnameLabel, - ) } if err := rows.Err(); err != nil { diff --git a/collector/pg_stat_user_tables_test.go b/collector/pg_stat_user_tables_test.go index 5e82335c..5a9c2bef 100644 --- a/collector/pg_stat_user_tables_test.go +++ b/collector/pg_stat_user_tables_test.go @@ -53,6 +53,9 @@ func TestPGStatUserTablesCollector(t *testing.T) { "datname", "schemaname", "relname", + "relpages", + "relsize", + "relsize_total", "seq_scan", "seq_tup_read", "idx_scan", @@ -71,8 +74,7 @@ func TestPGStatUserTablesCollector(t *testing.T) { "vacuum_count", "autovacuum_count", "analyze_count", - "autoanalyze_count", - "total_size"} + "autoanalyze_count"} rows := sqlmock.NewRows(columns). AddRow("postgres", "public", @@ -87,16 +89,18 @@ func TestPGStatUserTablesCollector(t *testing.T) { 8, 9, 10, + 11, + 12, + 13, 0, lastVacuumTime, lastAutoVacuumTime, lastAnalyzeTime, lastAutoAnalyzeTime, - 11, - 12, - 13, 14, - 15) + 15, + 16, + 17) mock.ExpectQuery(sanitizeQuery(statUserTablesQuery)).WillReturnRows(rows) ch := make(chan prometheus.Metric) go func() { @@ -109,25 +113,28 @@ func TestPGStatUserTablesCollector(t *testing.T) { }() expected := []MetricResult{ - {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 1}, - {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 2}, - {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 3}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 1}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 2}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 3}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 4}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 5}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 6}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 7}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 8}, - {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 9}, - {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 10}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 9}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 10}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 11}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 12}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 13}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 0}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 1685664000}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 1685750400}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 1685836800}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_GAUGE, value: 1685923200}, - {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 11}, - {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 12}, - {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 13}, {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 14}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 15}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 16}, + {labels: labelMap{"datname": "postgres", "schemaname": "public", "relname": "a_table"}, metricType: dto.MetricType_COUNTER, value: 17}, } convey.Convey("Metrics comparison", t, func() { @@ -154,6 +161,9 @@ func TestPGStatUserTablesCollectorNullValues(t *testing.T) { "datname", "schemaname", "relname", + "relpages", + "relsize", + "relsize_total", "seq_scan", "seq_tup_read", "idx_scan", @@ -172,8 +182,7 @@ func TestPGStatUserTablesCollectorNullValues(t *testing.T) { "vacuum_count", "autovacuum_count", "analyze_count", - "autoanalyze_count", - "total_size"} + "autoanalyze_count"} rows := sqlmock.NewRows(columns). AddRow("postgres", nil, @@ -197,6 +206,8 @@ func TestPGStatUserTablesCollectorNullValues(t *testing.T) { nil, nil, nil, + nil, + nil, nil) mock.ExpectQuery(sanitizeQuery(statUserTablesQuery)).WillReturnRows(rows) ch := make(chan prometheus.Metric) @@ -210,6 +221,9 @@ func TestPGStatUserTablesCollectorNullValues(t *testing.T) { }() expected := []MetricResult{ + {labels: labelMap{"datname": "postgres", "schemaname": "unknown", "relname": "unknown"}, metricType: dto.MetricType_GAUGE, value: 0}, + {labels: labelMap{"datname": "postgres", "schemaname": "unknown", "relname": "unknown"}, metricType: dto.MetricType_GAUGE, value: 0}, + {labels: labelMap{"datname": "postgres", "schemaname": "unknown", "relname": "unknown"}, metricType: dto.MetricType_GAUGE, value: 0}, {labels: labelMap{"datname": "postgres", "schemaname": "unknown", "relname": "unknown"}, metricType: dto.MetricType_COUNTER, value: 0}, {labels: labelMap{"datname": "postgres", "schemaname": "unknown", "relname": "unknown"}, metricType: dto.MetricType_COUNTER, value: 0}, {labels: labelMap{"datname": "postgres", "schemaname": "unknown", "relname": "unknown"}, metricType: dto.MetricType_COUNTER, value: 0},