From e5aee5eb15d99fa349e20254e69ae28b46376d33 Mon Sep 17 00:00:00 2001 From: Felix Yuan Date: Wed, 28 Jun 2023 11:12:54 -0700 Subject: [PATCH 1/2] Stuck idle in transaction collector and test Signed-off-by: Felix Yuan --- collector/pg_stuck_idle_in_transaction.go | 86 +++++++++++++++++++ .../pg_stuck_idle_in_transaction_test.go | 61 +++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 collector/pg_stuck_idle_in_transaction.go create mode 100644 collector/pg_stuck_idle_in_transaction_test.go diff --git a/collector/pg_stuck_idle_in_transaction.go b/collector/pg_stuck_idle_in_transaction.go new file mode 100644 index 000000000..e6839d4b3 --- /dev/null +++ b/collector/pg_stuck_idle_in_transaction.go @@ -0,0 +1,86 @@ +// 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" + + "github.com/go-kit/log" + "github.com/prometheus/client_golang/prometheus" +) + +const stuckIdleInTransactionSubsystem = "stuck_in_transaction" + +func init() { + registerCollector(stuckIdleInTransactionSubsystem, defaultDisabled, NewPGStuckIdleInTransactionCollector) +} + +type PGStuckIdleInTransactionCollector struct { + log log.Logger +} + +func NewPGStuckIdleInTransactionCollector(config collectorConfig) (Collector, error) { + return &PGStuckIdleInTransactionCollector{log: config.logger}, nil +} + +var ( + stuckIdleInTransactionQueries = prometheus.NewDesc( + prometheus.BuildFQName(namespace, longRunningTransactionsSubsystem, "queries"), + "Current number of queries that are stuck being idle in transactions", + []string{}, + prometheus.Labels{}, + ) + + stuckIdleInTransactionQuery = ` + SELECT + COUNT(*) AS queries + FROM pg_catalog.pg_stat_activity + WHERE + state = 'idle in transaction' AND (now() - query_start) > '10 minutes'::interval + ` +) + +func (PGStuckIdleInTransactionCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error { + db := instance.getDB() + rows, err := db.QueryContext(ctx, + stuckIdleInTransactionQuery) + + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var queries float64 + + if err := rows.Scan(&queries); err != nil { + return err + } + + ch <- prometheus.MustNewConstMetric( + stuckIdleInTransactionQueries, + prometheus.GaugeValue, + queries, + ) + ch <- prometheus.MustNewConstMetric( + longRunningTransactionsAgeInSeconds, + prometheus.GaugeValue, + queries, + ) + } + if err := rows.Err(); err != nil { + return err + } + return nil +} diff --git a/collector/pg_stuck_idle_in_transaction_test.go b/collector/pg_stuck_idle_in_transaction_test.go new file mode 100644 index 000000000..1313806a9 --- /dev/null +++ b/collector/pg_stuck_idle_in_transaction_test.go @@ -0,0 +1,61 @@ +// 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 TestPGStuckIdleInTransactionCollector(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{ + "queries", + } + rows := sqlmock.NewRows(columns). + AddRow(30) + + mock.ExpectQuery(sanitizeQuery(stuckIdleInTransactionQuery)).WillReturnRows(rows) + + ch := make(chan prometheus.Metric) + go func() { + defer close(ch) + c := PGStuckIdleInTransactionCollector{} + + if err := c.Update(context.Background(), inst, ch); err != nil { + t.Errorf("Error calling PGStuckIdleInTransactionCollector.Update: %s", err) + } + }() + expected := []MetricResult{ + {labels: labelMap{}, value: 30, 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 31cf042832c8494eb204b32ba0ff08887fb59455 Mon Sep 17 00:00:00 2001 From: Felix Yuan Date: Wed, 28 Jun 2023 11:17:37 -0700 Subject: [PATCH 2/2] Bug fix Signed-off-by: Felix Yuan --- collector/pg_stuck_idle_in_transaction.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/collector/pg_stuck_idle_in_transaction.go b/collector/pg_stuck_idle_in_transaction.go index e6839d4b3..882170fde 100644 --- a/collector/pg_stuck_idle_in_transaction.go +++ b/collector/pg_stuck_idle_in_transaction.go @@ -36,7 +36,7 @@ func NewPGStuckIdleInTransactionCollector(config collectorConfig) (Collector, er var ( stuckIdleInTransactionQueries = prometheus.NewDesc( - prometheus.BuildFQName(namespace, longRunningTransactionsSubsystem, "queries"), + prometheus.BuildFQName(namespace, stuckIdleInTransactionSubsystem, "queries"), "Current number of queries that are stuck being idle in transactions", []string{}, prometheus.Labels{}, @@ -73,11 +73,6 @@ func (PGStuckIdleInTransactionCollector) Update(ctx context.Context, instance *i prometheus.GaugeValue, queries, ) - ch <- prometheus.MustNewConstMetric( - longRunningTransactionsAgeInSeconds, - prometheus.GaugeValue, - queries, - ) } if err := rows.Err(); err != nil { return err