diff --git a/gauges/table_sizes.go b/gauges/table_sizes.go new file mode 100644 index 0000000..b03717e --- /dev/null +++ b/gauges/table_sizes.go @@ -0,0 +1,65 @@ +package gauges + +import ( + "time" + + "github.com/prometheus/client_golang/prometheus" +) + +const tableSizesQuery = ` + SELECT + ut.relname AS table_name, + pg_indexes_size(c.oid) AS index_size, + COALESCE(pg_total_relation_size(c.reltoastrelid), 0) AS toast_size, + pg_total_relation_size(c.oid) + - pg_indexes_size(c.oid) + - COALESCE(pg_total_relation_size(c.reltoastrelid), 0) AS table_size + FROM + pg_stat_user_tables ut + JOIN pg_class c on ut.relname = c.relname +` + +type tableSizes struct { + Name string `db:"table_name"` + IndexSize float64 `db:"index_size"` + ToastSize float64 `db:"toast_size"` + TableSize float64 `db:"table_size"` +} + +// TableSizes returns the total disk space in bytes used by the a table, +// including all indexes and TOAST data +func (g *Gauges) TableSizes() *prometheus.GaugeVec { + var gauge = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "postgresql_table_size_bytes", + Help: "Total disk space in bytes used by the a table, including all indexes and TOAST data", + ConstLabels: g.labels, + }, + []string{"table", "type"}, + ) + + go func() { + for { + var tables []tableSizes + if err := g.query(tableSizesQuery, &tables, emptyParams); err == nil { + for _, table := range tables { + gauge.With(prometheus.Labels{ + "table": table.Name, + "type": "index", + }).Set(table.IndexSize) + gauge.With(prometheus.Labels{ + "table": table.Name, + "type": "toast", + }).Set(table.ToastSize) + gauge.With(prometheus.Labels{ + "table": table.Name, + "type": "table", + }).Set(table.TableSize) + } + } + time.Sleep(g.interval) + } + }() + + return gauge +} diff --git a/gauges/table_sizes_test.go b/gauges/table_sizes_test.go new file mode 100644 index 0000000..d1f4341 --- /dev/null +++ b/gauges/table_sizes_test.go @@ -0,0 +1,22 @@ +package gauges + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTableSizes(t *testing.T) { + var assert = assert.New(t) + db, gauges, close := prepare(t) + defer close() + dropTestTable := createTestTable(t, db) + defer dropTestTable() + + var metrics = evaluate(t, gauges.TableSizes()) + assert.Len(metrics, 3) + for _, metric := range metrics { + assertEqual(t, 0, metric) + } + assertNoErrs(t, gauges) +} diff --git a/main.go b/main.go index 915b353..e4670fd 100644 --- a/main.go +++ b/main.go @@ -106,6 +106,7 @@ func watch(db *sql.DB, reg prometheus.Registerer, name string) { reg.MustRegister(gauges.UnusedIndexes()) reg.MustRegister(gauges.Up()) reg.MustRegister(gauges.TableScans()) + reg.MustRegister(gauges.TableSizes()) reg.MustRegister(gauges.DatabaseReadingUsage()) reg.MustRegister(gauges.DatabaseWritingUsage()) reg.MustRegister(gauges.HOTUpdates())