From 7c4b2e6bb3af7b8f4f82a1d57c0f733fb5bf7c55 Mon Sep 17 00:00:00 2001 From: Felix Yuan Date: Wed, 28 Jun 2023 10:44:03 -0700 Subject: [PATCH 1/3] Index size collector and test Signed-off-by: Felix Yuan --- collector/pg_index_size.go | 106 ++++++++++++++++++++++++++++++++ collector/pg_index_size_test.go | 105 +++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 collector/pg_index_size.go create mode 100644 collector/pg_index_size_test.go diff --git a/collector/pg_index_size.go b/collector/pg_index_size.go new file mode 100644 index 00000000..fcc33d2c --- /dev/null +++ b/collector/pg_index_size.go @@ -0,0 +1,106 @@ +// Copyright 2023 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collector + +import ( + "context" + "database/sql" + + "github.com/go-kit/log" + "github.com/prometheus/client_golang/prometheus" +) + +const indexSizeSubsystem = "index_size" + +func init() { + registerCollector(indexSizeSubsystem, defaultDisabled, NewPGIndexSizeCollector) +} + +type PGIndexSizeCollector struct { + log log.Logger +} + +func NewPGIndexSizeCollector(config collectorConfig) (Collector, error) { + return &PGIndexSizeCollector{log: config.logger}, nil +} + +var ( + indexSizeDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, indexSizeSubsystem, "bytes"), + "Size of the index as per pg_table_size function", + []string{"schemaname", "relname", "indexrelname"}, + prometheus.Labels{}, + ) + + indexSizeQuery = ` + SELECT + schemaname, + tablename as relname, + indexname as indexrelname, + pg_class.relpages * 8192::bigint as index_size + FROM + pg_indexes inner join pg_namespace on pg_indexes.schemaname = pg_namespace.nspname + inner join pg_class on pg_class.relnamespace = pg_namespace.oid and pg_class.relname = pg_indexes.indexname + WHERE + pg_indexes.schemaname != 'pg_catalog' + ` +) + +func (PGIndexSizeCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error { + db := instance.getDB() + rows, err := db.QueryContext(ctx, + indexSizeQuery) + + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var schemaname, relname, indexrelname sql.NullString + var indexSize sql.NullFloat64 + + if err := rows.Scan(&schemaname, &relname, &indexrelname, &indexSize); err != nil { + return err + } + schemanameLabel := "unknown" + if schemaname.Valid { + schemanameLabel = schemaname.String + } + relnameLabel := "unknown" + if relname.Valid { + relnameLabel = relname.String + } + indexrelnameLabel := "unknown" + if indexrelname.Valid { + indexrelnameLabel = indexrelname.String + } + labels := []string{schemanameLabel, relnameLabel, indexrelnameLabel} + + indexSizeMetric := 0.0 + if indexSize.Valid { + indexSizeMetric = indexSize.Float64 + } + ch <- prometheus.MustNewConstMetric( + indexSizeDesc, + prometheus.GaugeValue, + indexSizeMetric, + labels..., + ) + } + if err := rows.Err(); err != nil { + return err + } + return nil +} diff --git a/collector/pg_index_size_test.go b/collector/pg_index_size_test.go new file mode 100644 index 00000000..8adaa470 --- /dev/null +++ b/collector/pg_index_size_test.go @@ -0,0 +1,105 @@ +// Copyright 2023 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package collector + +import ( + "context" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/smartystreets/goconvey/convey" +) + +func TestPgIndexSizeCollector(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("Error opening a stub db connection: %s", err) + } + defer db.Close() + inst := &instance{db: db} + columns := []string{ + "schemaname", + "relname", + "indexrelname", + "index_size", + } + rows := sqlmock.NewRows(columns). + AddRow("public", "foo", "foo_key", 100) + + mock.ExpectQuery(sanitizeQuery(indexSizeQuery)).WillReturnRows(rows) + + ch := make(chan prometheus.Metric) + go func() { + defer close(ch) + c := PGIndexSizeCollector{} + + if err := c.Update(context.Background(), inst, ch); err != nil { + t.Errorf("Error calling PGIndexSizeCollector.Update: %s", err) + } + }() + expected := []MetricResult{ + {labels: labelMap{"schemaname": "public", "relname": "foo", "indexrelname": "foo_key"}, value: 100, metricType: dto.MetricType_GAUGE}, + } + convey.Convey("Metrics comparison", t, func() { + for _, expect := range expected { + m := readMetric(<-ch) + convey.So(expect, convey.ShouldResemble, m) + } + }) + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled exceptions: %s", err) + } +} + +func TestPgIndexSizeCollectorNull(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("Error opening a stub db connection: %s", err) + } + defer db.Close() + inst := &instance{db: db} + columns := []string{ + "schemaname", + "relname", + "indexrelname", + "index_size", + } + rows := sqlmock.NewRows(columns). + AddRow(nil, nil, nil, nil) + + mock.ExpectQuery(sanitizeQuery(indexSizeQuery)).WillReturnRows(rows) + + ch := make(chan prometheus.Metric) + go func() { + defer close(ch) + c := PGIndexSizeCollector{} + + if err := c.Update(context.Background(), inst, ch); err != nil { + t.Errorf("Error calling PGIndexSizeCollector.Update: %s", err) + } + }() + expected := []MetricResult{ + {labels: labelMap{"schemaname": "unknown", "relname": "unknown", "indexrelname": "unknown"}, value: 0, metricType: dto.MetricType_GAUGE}, + } + convey.Convey("Metrics comparison", t, func() { + for _, expect := range expected { + m := readMetric(<-ch) + convey.So(expect, convey.ShouldResemble, m) + } + }) + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled exceptions: %s", err) + } +} From 745636279de4322086a51c41933820ffb5852e31 Mon Sep 17 00:00:00 2001 From: Felix Yuan Date: Wed, 28 Jun 2023 18:26:22 -0700 Subject: [PATCH 2/3] Update collector/pg_index_size.go Co-authored-by: Joe Adams Signed-off-by: Felix Yuan --- collector/pg_index_size.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/collector/pg_index_size.go b/collector/pg_index_size.go index fcc33d2c..db7d1ea7 100644 --- a/collector/pg_index_size.go +++ b/collector/pg_index_size.go @@ -50,8 +50,11 @@ var ( indexname as indexrelname, pg_class.relpages * 8192::bigint as index_size FROM - pg_indexes inner join pg_namespace on pg_indexes.schemaname = pg_namespace.nspname - inner join pg_class on pg_class.relnamespace = pg_namespace.oid and pg_class.relname = pg_indexes.indexname + pg_indexes + INNER JOIN pg_namespace + ON pg_indexes.schemaname = pg_namespace.nspname + INNER JOIN pg_class + ON pg_class.relnamespace = pg_namespace.oid AND pg_class.relname = pg_indexes.indexname WHERE pg_indexes.schemaname != 'pg_catalog' ` From d526f445825cf0506946a6c2d2f86b03790cb742 Mon Sep 17 00:00:00 2001 From: Felix Yuan Date: Wed, 28 Jun 2023 18:26:30 -0700 Subject: [PATCH 3/3] Update collector/pg_index_size.go Co-authored-by: Joe Adams Signed-off-by: Felix Yuan --- collector/pg_index_size.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/collector/pg_index_size.go b/collector/pg_index_size.go index db7d1ea7..44686e6b 100644 --- a/collector/pg_index_size.go +++ b/collector/pg_index_size.go @@ -46,9 +46,9 @@ var ( indexSizeQuery = ` SELECT schemaname, - tablename as relname, - indexname as indexrelname, - pg_class.relpages * 8192::bigint as index_size + tablename AS relname, + indexname AS indexrelname, + pg_class.relpages * 8192::bigint AS index_size FROM pg_indexes INNER JOIN pg_namespace