From bdc24f3fe99dd4aaaa6b68da20404dccbab4d709 Mon Sep 17 00:00:00 2001 From: Vlad Gusev Date: Mon, 12 Aug 2024 13:28:04 +0300 Subject: [PATCH] Add the instance struct to handle connections The intent is to use the instance struct to hold the connection to the database as well as metadata about the instance Change is similar to prometheus-community/postgres_exporter#785 --- collector/binlog.go | 4 +- collector/binlog_test.go | 4 +- collector/engine_innodb.go | 4 +- collector/engine_innodb_test.go | 4 +- collector/engine_tokudb.go | 3 +- collector/engine_tokudb_test.go | 3 +- collector/exporter.go | 44 ++----- collector/exporter_test.go | 20 --- collector/global_status.go | 3 +- collector/global_status_test.go | 3 +- collector/global_variables.go | 3 +- collector/global_variables_test.go | 3 +- collector/heartbeat.go | 3 +- collector/heartbeat_test.go | 3 +- collector/info_schema_auto_increment.go | 4 +- collector/info_schema_clientstats.go | 4 +- collector/info_schema_clientstats_test.go | 3 +- collector/info_schema_innodb_cmp.go | 4 +- collector/info_schema_innodb_cmp_test.go | 3 +- collector/info_schema_innodb_cmpmem.go | 4 +- collector/info_schema_innodb_cmpmem_test.go | 3 +- collector/info_schema_innodb_metrics.go | 4 +- collector/info_schema_innodb_metrics_test.go | 3 +- .../info_schema_innodb_sys_tablespaces.go | 4 +- ...info_schema_innodb_sys_tablespaces_test.go | 3 +- collector/info_schema_processlist.go | 4 +- collector/info_schema_processlist_test.go | 3 +- collector/info_schema_query_response_time.go | 9 +- .../info_schema_query_response_time_test.go | 3 +- collector/info_schema_replica_host.go | 4 +- collector/info_schema_replica_host_test.go | 3 +- collector/info_schema_schemastats.go | 4 +- collector/info_schema_schemastats_test.go | 3 +- collector/info_schema_tables.go | 4 +- collector/info_schema_tablestats.go | 4 +- collector/info_schema_tablestats_test.go | 3 +- collector/info_schema_userstats.go | 4 +- collector/info_schema_userstats_test.go | 3 +- collector/instance.go | 114 ++++++++++++++++++ collector/mysql_user.go | 3 +- collector/perf_schema_events_statements.go | 4 +- .../perf_schema_events_statements_sum.go | 4 +- collector/perf_schema_events_waits.go | 4 +- collector/perf_schema_file_events.go | 4 +- collector/perf_schema_file_instances.go | 4 +- collector/perf_schema_file_instances_test.go | 3 +- collector/perf_schema_index_io_waits.go | 4 +- collector/perf_schema_index_io_waits_test.go | 3 +- collector/perf_schema_memory_events.go | 4 +- collector/perf_schema_memory_events_test.go | 3 +- ...ma_replication_applier_status_by_worker.go | 4 +- ...plication_applier_status_by_worker_test.go | 3 +- ...f_schema_replication_group_member_stats.go | 3 +- ...ema_replication_group_member_stats_test.go | 3 +- .../perf_schema_replication_group_members.go | 6 +- ...f_schema_replication_group_members_test.go | 9 +- collector/perf_schema_table_io_waits.go | 4 +- collector/perf_schema_table_lock_waits.go | 4 +- collector/scraper.go | 3 +- collector/slave_hosts.go | 3 +- collector/slave_hosts_test.go | 9 +- collector/slave_status.go | 3 +- collector/slave_status_test.go | 3 +- collector/sys_user_summary.go | 5 +- collector/sys_user_summary_test.go | 10 +- go.mod | 1 + go.sum | 2 + 67 files changed, 267 insertions(+), 156 deletions(-) create mode 100644 collector/instance.go diff --git a/collector/binlog.go b/collector/binlog.go index 1cf76b951..7c5c9495e 100644 --- a/collector/binlog.go +++ b/collector/binlog.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "fmt" "strconv" "strings" @@ -72,8 +71,9 @@ func (ScrapeBinlogSize) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeBinlogSize) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeBinlogSize) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var logBin uint8 + db := instance.getDB() err := db.QueryRowContext(ctx, logbinQuery).Scan(&logBin) if err != nil { return err diff --git a/collector/binlog_test.go b/collector/binlog_test.go index bfe8f382b..e85efb67c 100644 --- a/collector/binlog_test.go +++ b/collector/binlog_test.go @@ -31,6 +31,8 @@ func TestScrapeBinlogSize(t *testing.T) { } defer db.Close() + inst := &instance{db: db} + mock.ExpectQuery(logbinQuery).WillReturnRows(sqlmock.NewRows([]string{""}).AddRow(1)) columns := []string{"Log_name", "File_size"} @@ -42,7 +44,7 @@ func TestScrapeBinlogSize(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeBinlogSize{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeBinlogSize{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/engine_innodb.go b/collector/engine_innodb.go index 9b1a33c2c..206038170 100644 --- a/collector/engine_innodb.go +++ b/collector/engine_innodb.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "regexp" "strconv" "strings" @@ -52,7 +51,8 @@ func (ScrapeEngineInnodbStatus) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeEngineInnodbStatus) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeEngineInnodbStatus) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() rows, err := db.QueryContext(ctx, engineInnodbStatusQuery) if err != nil { return err diff --git a/collector/engine_innodb_test.go b/collector/engine_innodb_test.go index bf19caf65..07817f1a1 100644 --- a/collector/engine_innodb_test.go +++ b/collector/engine_innodb_test.go @@ -152,10 +152,10 @@ END OF INNODB MONITOR OUTPUT rows := sqlmock.NewRows(columns).AddRow("InnoDB", "", sample) mock.ExpectQuery(sanitizeQuery(engineInnodbStatusQuery)).WillReturnRows(rows) - + inst := &instance{db: db} ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeEngineInnodbStatus{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeEngineInnodbStatus{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/engine_tokudb.go b/collector/engine_tokudb.go index d4e89adc4..52c411c8d 100644 --- a/collector/engine_tokudb.go +++ b/collector/engine_tokudb.go @@ -50,7 +50,8 @@ func (ScrapeEngineTokudbStatus) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeEngineTokudbStatus) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeEngineTokudbStatus) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() tokudbRows, err := db.QueryContext(ctx, engineTokudbStatusQuery) if err != nil { return err diff --git a/collector/engine_tokudb_test.go b/collector/engine_tokudb_test.go index 627aa31fd..d08446d3d 100644 --- a/collector/engine_tokudb_test.go +++ b/collector/engine_tokudb_test.go @@ -46,6 +46,7 @@ func TestScrapeEngineTokudbStatus(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"Type", "Name", "Status"} rows := sqlmock.NewRows(columns). @@ -59,7 +60,7 @@ func TestScrapeEngineTokudbStatus(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeEngineTokudbStatus{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeEngineTokudbStatus{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/exporter.go b/collector/exporter.go index 0587f1d98..2f12e3f96 100644 --- a/collector/exporter.go +++ b/collector/exporter.go @@ -15,10 +15,7 @@ package collector import ( "context" - "database/sql" "fmt" - "regexp" - "strconv" "strings" "sync" "time" @@ -38,18 +35,12 @@ const ( // SQL queries and parameters. const ( - versionQuery = `SELECT @@version` - // System variable params formatting. // See: https://github.com/go-sql-driver/mysql#system-variables sessionSettingsParam = `log_slow_filter=%27tmp_table_on_disk,filesort_on_disk%27` timeoutParam = `lock_wait_timeout=%d` ) -var ( - versionRE = regexp.MustCompile(`^\d+\.\d+`) -) - // Tunable flags. var ( exporterLockTimeout = kingpin.Flag( @@ -92,6 +83,7 @@ type Exporter struct { logger log.Logger dsn string scrapers []Scraper + instance *instance } // New returns a new MySQL exporter for the provided DSN. @@ -135,27 +127,23 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) { func (e *Exporter) scrape(ctx context.Context, ch chan<- prometheus.Metric) float64 { var err error scrapeTime := time.Now() - db, err := sql.Open("mysql", e.dsn) + instance, err := newInstance(e.dsn) if err != nil { level.Error(e.logger).Log("msg", "Error opening connection to database", "err", err) return 0.0 } - defer db.Close() - - // By design exporter should use maximum one connection per request. - db.SetMaxOpenConns(1) - db.SetMaxIdleConns(1) - // Set max lifetime for a connection. - db.SetConnMaxLifetime(1 * time.Minute) + defer instance.Close() + e.instance = instance - if err := db.PingContext(ctx); err != nil { + if err := instance.Ping(); err != nil { level.Error(e.logger).Log("msg", "Error pinging mysqld", "err", err) return 0.0 } ch <- prometheus.MustNewConstMetric(mysqlScrapeDurationSeconds, prometheus.GaugeValue, time.Since(scrapeTime).Seconds(), "connection") - version := getMySQLVersion(db, e.logger) + version := instance.versionMajorMinor + var wg sync.WaitGroup defer wg.Wait() for _, scraper := range e.scrapers { @@ -169,7 +157,7 @@ func (e *Exporter) scrape(ctx context.Context, ch chan<- prometheus.Metric) floa label := "collect." + scraper.Name() scrapeTime := time.Now() collectorSuccess := 1.0 - if err := scraper.Scrape(ctx, db, ch, log.With(e.logger, "scraper", scraper.Name())); err != nil { + if err := scraper.Scrape(ctx, instance, ch, log.With(e.logger, "scraper", scraper.Name())); err != nil { level.Error(e.logger).Log("msg", "Error from scraper", "scraper", scraper.Name(), "target", e.getTargetFromDsn(), "err", err) collectorSuccess = 0.0 } @@ -189,19 +177,3 @@ func (e *Exporter) getTargetFromDsn() string { } return dsnConfig.Addr } - -func getMySQLVersion(db *sql.DB, logger log.Logger) float64 { - var versionStr string - var versionNum float64 - if err := db.QueryRow(versionQuery).Scan(&versionStr); err == nil { - versionNum, _ = strconv.ParseFloat(versionRE.FindString(versionStr), 64) - } else { - level.Debug(logger).Log("msg", "Error querying version", "err", err) - } - // If we can't match/parse the version, set it some big value that matches all versions. - if versionNum == 0 { - level.Debug(logger).Log("msg", "Error parsing version string", "version", versionStr) - versionNum = 999 - } - return versionNum -} diff --git a/collector/exporter_test.go b/collector/exporter_test.go index 7eb8ba4c6..3f742263a 100644 --- a/collector/exporter_test.go +++ b/collector/exporter_test.go @@ -15,12 +15,9 @@ package collector import ( "context" - "database/sql" - "os" "testing" "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" "github.com/smartystreets/goconvey/convey" @@ -68,20 +65,3 @@ func TestExporter(t *testing.T) { } }) } - -func TestGetMySQLVersion(t *testing.T) { - if testing.Short() { - t.Skip("-short is passed, skipping test") - } - - logger := log.NewLogfmtLogger(os.Stderr) - logger = level.NewFilter(logger, level.AllowDebug()) - - convey.Convey("Version parsing", t, func() { - db, err := sql.Open("mysql", dsn) - convey.So(err, convey.ShouldBeNil) - defer db.Close() - - convey.So(getMySQLVersion(db, logger), convey.ShouldBeBetweenOrEqual, 5.6, 12.0) - }) -} diff --git a/collector/global_status.go b/collector/global_status.go index 21920a714..c0a131ead 100644 --- a/collector/global_status.go +++ b/collector/global_status.go @@ -99,7 +99,8 @@ func (ScrapeGlobalStatus) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeGlobalStatus) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeGlobalStatus) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() globalStatusRows, err := db.QueryContext(ctx, globalStatusQuery) if err != nil { return err diff --git a/collector/global_status_test.go b/collector/global_status_test.go index 14456c919..eea8fd58f 100644 --- a/collector/global_status_test.go +++ b/collector/global_status_test.go @@ -30,6 +30,7 @@ func TestScrapeGlobalStatus(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"Variable_name", "Value"} rows := sqlmock.NewRows(columns). @@ -63,7 +64,7 @@ func TestScrapeGlobalStatus(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeGlobalStatus{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeGlobalStatus{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/global_variables.go b/collector/global_variables.go index b2717fb96..6b286ce13 100644 --- a/collector/global_variables.go +++ b/collector/global_variables.go @@ -138,7 +138,8 @@ func (ScrapeGlobalVariables) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeGlobalVariables) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeGlobalVariables) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() globalVariablesRows, err := db.QueryContext(ctx, globalVariablesQuery) if err != nil { return err diff --git a/collector/global_variables_test.go b/collector/global_variables_test.go index 4f0ab36f2..1bab0a9d6 100644 --- a/collector/global_variables_test.go +++ b/collector/global_variables_test.go @@ -30,6 +30,7 @@ func TestScrapeGlobalVariables(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"Variable_name", "Value"} rows := sqlmock.NewRows(columns). @@ -52,7 +53,7 @@ func TestScrapeGlobalVariables(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeGlobalVariables{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeGlobalVariables{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/heartbeat.go b/collector/heartbeat.go index 7bc5fc588..6b32048fa 100644 --- a/collector/heartbeat.go +++ b/collector/heartbeat.go @@ -100,7 +100,8 @@ func nowExpr() string { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeHeartbeat) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeHeartbeat) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() query := fmt.Sprintf(heartbeatQuery, nowExpr(), *collectHeartbeatDatabase, *collectHeartbeatTable) heartbeatRows, err := db.QueryContext(ctx, query) if err != nil { diff --git a/collector/heartbeat_test.go b/collector/heartbeat_test.go index 48e35925c..1d553bc35 100644 --- a/collector/heartbeat_test.go +++ b/collector/heartbeat_test.go @@ -65,6 +65,7 @@ func TestScrapeHeartbeat(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} rows := sqlmock.NewRows(tt.Columns). AddRow("1487597613.001320", "1487598113.448042", 1) @@ -72,7 +73,7 @@ func TestScrapeHeartbeat(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeHeartbeat{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeHeartbeat{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_auto_increment.go b/collector/info_schema_auto_increment.go index d52ebd27d..ed40f33af 100644 --- a/collector/info_schema_auto_increment.go +++ b/collector/info_schema_auto_increment.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" @@ -70,7 +69,8 @@ func (ScrapeAutoIncrementColumns) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeAutoIncrementColumns) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeAutoIncrementColumns) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() autoIncrementRows, err := db.QueryContext(ctx, infoSchemaAutoIncrementQuery) if err != nil { return err diff --git a/collector/info_schema_clientstats.go b/collector/info_schema_clientstats.go index 619fb4a00..64afe35a8 100644 --- a/collector/info_schema_clientstats.go +++ b/collector/info_schema_clientstats.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "fmt" "strings" @@ -161,8 +160,9 @@ func (ScrapeClientStat) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeClientStat) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeClientStat) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var varName, varVal string + db := instance.getDB() err := db.QueryRowContext(ctx, userstatCheckQuery).Scan(&varName, &varVal) if err != nil { level.Debug(logger).Log("msg", "Detailed client stats are not available.") diff --git a/collector/info_schema_clientstats_test.go b/collector/info_schema_clientstats_test.go index c77e24013..cd14ad1a1 100644 --- a/collector/info_schema_clientstats_test.go +++ b/collector/info_schema_clientstats_test.go @@ -30,6 +30,7 @@ func TestScrapeClientStat(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} mock.ExpectQuery(sanitizeQuery(userstatCheckQuery)).WillReturnRows(sqlmock.NewRows([]string{"Variable_name", "Value"}). AddRow("userstat", "ON")) @@ -41,7 +42,7 @@ func TestScrapeClientStat(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeClientStat{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeClientStat{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_innodb_cmp.go b/collector/info_schema_innodb_cmp.go index 2c8dfa4ca..2db730622 100644 --- a/collector/info_schema_innodb_cmp.go +++ b/collector/info_schema_innodb_cmp.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" @@ -77,7 +76,8 @@ func (ScrapeInnodbCmp) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeInnodbCmp) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeInnodbCmp) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() informationSchemaInnodbCmpRows, err := db.QueryContext(ctx, innodbCmpQuery) if err != nil { return err diff --git a/collector/info_schema_innodb_cmp_test.go b/collector/info_schema_innodb_cmp_test.go index 2143af09c..26e19fee7 100644 --- a/collector/info_schema_innodb_cmp_test.go +++ b/collector/info_schema_innodb_cmp_test.go @@ -30,6 +30,7 @@ func TestScrapeInnodbCmp(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"page_size", "compress_ops", "compress_ops_ok", "compress_time", "uncompress_ops", "uncompress_time"} rows := sqlmock.NewRows(columns). @@ -38,7 +39,7 @@ func TestScrapeInnodbCmp(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeInnodbCmp{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeInnodbCmp{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_innodb_cmpmem.go b/collector/info_schema_innodb_cmpmem.go index 61f4938a1..607bd9f53 100644 --- a/collector/info_schema_innodb_cmpmem.go +++ b/collector/info_schema_innodb_cmpmem.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" @@ -72,7 +71,8 @@ func (ScrapeInnodbCmpMem) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeInnodbCmpMem) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeInnodbCmpMem) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() informationSchemaInnodbCmpMemRows, err := db.QueryContext(ctx, innodbCmpMemQuery) if err != nil { return err diff --git a/collector/info_schema_innodb_cmpmem_test.go b/collector/info_schema_innodb_cmpmem_test.go index 346b2d235..77558eb69 100644 --- a/collector/info_schema_innodb_cmpmem_test.go +++ b/collector/info_schema_innodb_cmpmem_test.go @@ -30,6 +30,7 @@ func TestScrapeInnodbCmpMem(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"page_size", "buffer_pool", "pages_used", "pages_free", "relocation_ops", "relocation_time"} rows := sqlmock.NewRows(columns). @@ -38,7 +39,7 @@ func TestScrapeInnodbCmpMem(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeInnodbCmpMem{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeInnodbCmpMem{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_innodb_metrics.go b/collector/info_schema_innodb_metrics.go index f077dd5a1..4abb0a8fb 100644 --- a/collector/info_schema_innodb_metrics.go +++ b/collector/info_schema_innodb_metrics.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "errors" "fmt" "regexp" @@ -93,10 +92,11 @@ func (ScrapeInnodbMetrics) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeInnodbMetrics) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeInnodbMetrics) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var enabledColumnName string var query string + db := instance.getDB() err := db.QueryRowContext(ctx, infoSchemaInnodbMetricsEnabledColumnQuery).Scan(&enabledColumnName) if err != nil { return err diff --git a/collector/info_schema_innodb_metrics_test.go b/collector/info_schema_innodb_metrics_test.go index 43b0de243..ea8b295ca 100644 --- a/collector/info_schema_innodb_metrics_test.go +++ b/collector/info_schema_innodb_metrics_test.go @@ -31,6 +31,7 @@ func TestScrapeInnodbMetrics(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} enabledColumnName := []string{"COLUMN_NAME"} rows := sqlmock.NewRows(enabledColumnName). @@ -53,7 +54,7 @@ func TestScrapeInnodbMetrics(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeInnodbMetrics{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeInnodbMetrics{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_innodb_sys_tablespaces.go b/collector/info_schema_innodb_sys_tablespaces.go index 29218926c..0e06095a5 100644 --- a/collector/info_schema_innodb_sys_tablespaces.go +++ b/collector/info_schema_innodb_sys_tablespaces.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "errors" "fmt" @@ -85,9 +84,10 @@ func (ScrapeInfoSchemaInnodbTablespaces) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeInfoSchemaInnodbTablespaces) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeInfoSchemaInnodbTablespaces) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var tablespacesTablename string var query string + db := instance.getDB() err := db.QueryRowContext(ctx, innodbTablespacesTablenameQuery).Scan(&tablespacesTablename) if err != nil { return err diff --git a/collector/info_schema_innodb_sys_tablespaces_test.go b/collector/info_schema_innodb_sys_tablespaces_test.go index 055532873..30bdb5218 100644 --- a/collector/info_schema_innodb_sys_tablespaces_test.go +++ b/collector/info_schema_innodb_sys_tablespaces_test.go @@ -31,6 +31,7 @@ func TestScrapeInfoSchemaInnodbTablespaces(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"TABLE_NAME"} rows := sqlmock.NewRows(columns). @@ -47,7 +48,7 @@ func TestScrapeInfoSchemaInnodbTablespaces(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeInfoSchemaInnodbTablespaces{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeInfoSchemaInnodbTablespaces{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_processlist.go b/collector/info_schema_processlist.go index 0d97161c3..80d2c4931 100755 --- a/collector/info_schema_processlist.go +++ b/collector/info_schema_processlist.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "fmt" "reflect" "sort" @@ -97,11 +96,12 @@ func (ScrapeProcesslist) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeProcesslist) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeProcesslist) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { processQuery := fmt.Sprintf( infoSchemaProcesslistQuery, *processlistMinTime, ) + db := instance.getDB() processlistRows, err := db.QueryContext(ctx, processQuery) if err != nil { return err diff --git a/collector/info_schema_processlist_test.go b/collector/info_schema_processlist_test.go index 6bcd0108d..beaa349c5 100644 --- a/collector/info_schema_processlist_test.go +++ b/collector/info_schema_processlist_test.go @@ -40,6 +40,7 @@ func TestScrapeProcesslist(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} query := fmt.Sprintf(infoSchemaProcesslistQuery, 0) columns := []string{"user", "host", "command", "state", "processes", "seconds"} @@ -56,7 +57,7 @@ func TestScrapeProcesslist(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeProcesslist{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeProcesslist{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_query_response_time.go b/collector/info_schema_query_response_time.go index bf5b14df1..f163eccb1 100644 --- a/collector/info_schema_query_response_time.go +++ b/collector/info_schema_query_response_time.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "strconv" "strings" @@ -56,7 +55,8 @@ var ( } ) -func processQueryResponseTimeTable(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, query string, i int) error { +func processQueryResponseTimeTable(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, query string, i int) error { + db := instance.getDB() queryDistributionRows, err := db.QueryContext(ctx, query) if err != nil { return err @@ -119,8 +119,9 @@ func (ScrapeQueryResponseTime) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeQueryResponseTime) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeQueryResponseTime) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var queryStats uint8 + db := instance.getDB() err := db.QueryRowContext(ctx, queryResponseCheckQuery).Scan(&queryStats) if err != nil { level.Debug(logger).Log("msg", "Query response time distribution is not available.") @@ -132,7 +133,7 @@ func (ScrapeQueryResponseTime) Scrape(ctx context.Context, db *sql.DB, ch chan<- } for i, query := range queryResponseTimeQueries { - err := processQueryResponseTimeTable(ctx, db, ch, query, i) + err := processQueryResponseTimeTable(ctx, instance, ch, query, i) // The first query should not fail if query_response_time_stats is ON, // unlike the other two when the read/write tables exist only with Percona Server 5.6/5.7. if i == 0 && err != nil { diff --git a/collector/info_schema_query_response_time_test.go b/collector/info_schema_query_response_time_test.go index 8c766686e..bf342f9c9 100644 --- a/collector/info_schema_query_response_time_test.go +++ b/collector/info_schema_query_response_time_test.go @@ -30,6 +30,7 @@ func TestScrapeQueryResponseTime(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} mock.ExpectQuery(queryResponseCheckQuery).WillReturnRows(sqlmock.NewRows([]string{""}).AddRow(1)) @@ -52,7 +53,7 @@ func TestScrapeQueryResponseTime(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeQueryResponseTime{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeQueryResponseTime{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_replica_host.go b/collector/info_schema_replica_host.go index 106bbbd0b..78a7e5e4c 100644 --- a/collector/info_schema_replica_host.go +++ b/collector/info_schema_replica_host.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/go-kit/log/level" @@ -84,7 +83,8 @@ func (ScrapeReplicaHost) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeReplicaHost) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeReplicaHost) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() replicaHostRows, err := db.QueryContext(ctx, replicaHostQuery) if err != nil { if mysqlErr, ok := err.(*MySQL.MySQLError); ok { // Now the error number is accessible directly diff --git a/collector/info_schema_replica_host_test.go b/collector/info_schema_replica_host_test.go index 08d881b60..1bc56bb04 100644 --- a/collector/info_schema_replica_host_test.go +++ b/collector/info_schema_replica_host_test.go @@ -30,6 +30,7 @@ func TestScrapeReplicaHost(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"SERVER_ID", "ROLE", "CPU", "MASTER_SLAVE_LATENCY_IN_MICROSECONDS", "REPLICA_LAG_IN_MILLISECONDS", "LOG_STREAM_SPEED_IN_KiB_PER_SECOND", "CURRENT_REPLAY_LATENCY_IN_MICROSECONDS"} rows := sqlmock.NewRows(columns). @@ -39,7 +40,7 @@ func TestScrapeReplicaHost(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeReplicaHost{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeReplicaHost{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_schemastats.go b/collector/info_schema_schemastats.go index 64ff6528e..2825e3efd 100644 --- a/collector/info_schema_schemastats.go +++ b/collector/info_schema_schemastats.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/go-kit/log/level" @@ -72,9 +71,10 @@ func (ScrapeSchemaStat) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeSchemaStat) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeSchemaStat) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var varName, varVal string + db := instance.getDB() err := db.QueryRowContext(ctx, userstatCheckQuery).Scan(&varName, &varVal) if err != nil { level.Debug(logger).Log("msg", "Detailed schema stats are not available.") diff --git a/collector/info_schema_schemastats_test.go b/collector/info_schema_schemastats_test.go index bc57fc6f9..4d90b1d41 100644 --- a/collector/info_schema_schemastats_test.go +++ b/collector/info_schema_schemastats_test.go @@ -29,6 +29,7 @@ func TestScrapeSchemaStat(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} mock.ExpectQuery(sanitizeQuery(userstatCheckQuery)).WillReturnRows(sqlmock.NewRows([]string{"Variable_name", "Value"}). AddRow("userstat", "ON")) @@ -41,7 +42,7 @@ func TestScrapeSchemaStat(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeSchemaStat{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeSchemaStat{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_tables.go b/collector/info_schema_tables.go index f526f4d6d..d5537561b 100644 --- a/collector/info_schema_tables.go +++ b/collector/info_schema_tables.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "fmt" "strings" @@ -97,8 +96,9 @@ func (ScrapeTableSchema) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeTableSchema) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeTableSchema) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var dbList []string + db := instance.getDB() if *tableSchemaDatabases == "*" { dbListRows, err := db.QueryContext(ctx, dbListQuery) if err != nil { diff --git a/collector/info_schema_tablestats.go b/collector/info_schema_tablestats.go index b4d964df5..29fabb912 100644 --- a/collector/info_schema_tablestats.go +++ b/collector/info_schema_tablestats.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/go-kit/log/level" @@ -72,8 +71,9 @@ func (ScrapeTableStat) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeTableStat) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeTableStat) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var varName, varVal string + db := instance.getDB() err := db.QueryRowContext(ctx, userstatCheckQuery).Scan(&varName, &varVal) if err != nil { level.Debug(logger).Log("msg", "Detailed table stats are not available.") diff --git a/collector/info_schema_tablestats_test.go b/collector/info_schema_tablestats_test.go index 9b6786414..b50abc628 100644 --- a/collector/info_schema_tablestats_test.go +++ b/collector/info_schema_tablestats_test.go @@ -29,6 +29,7 @@ func TestScrapeTableStat(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} mock.ExpectQuery(sanitizeQuery(userstatCheckQuery)).WillReturnRows(sqlmock.NewRows([]string{"Variable_name", "Value"}). AddRow("userstat", "ON")) @@ -42,7 +43,7 @@ func TestScrapeTableStat(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeTableStat{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeTableStat{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/info_schema_userstats.go b/collector/info_schema_userstats.go index 329d3b0a5..c8c55e07d 100644 --- a/collector/info_schema_userstats.go +++ b/collector/info_schema_userstats.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "fmt" "strings" @@ -157,8 +156,9 @@ func (ScrapeUserStat) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeUserStat) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeUserStat) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var varName, varVal string + db := instance.getDB() err := db.QueryRowContext(ctx, userstatCheckQuery).Scan(&varName, &varVal) if err != nil { level.Debug(logger).Log("msg", "Detailed user stats are not available.") diff --git a/collector/info_schema_userstats_test.go b/collector/info_schema_userstats_test.go index 99f54ebc4..57292c298 100644 --- a/collector/info_schema_userstats_test.go +++ b/collector/info_schema_userstats_test.go @@ -30,6 +30,7 @@ func TestScrapeUserStat(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} mock.ExpectQuery(sanitizeQuery(userstatCheckQuery)).WillReturnRows(sqlmock.NewRows([]string{"Variable_name", "Value"}). AddRow("userstat", "ON")) @@ -41,7 +42,7 @@ func TestScrapeUserStat(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeUserStat{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeUserStat{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/instance.go b/collector/instance.go new file mode 100644 index 000000000..e03f29aee --- /dev/null +++ b/collector/instance.go @@ -0,0 +1,114 @@ +// Copyright 2024 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 ( + "database/sql" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/blang/semver/v4" +) + +const ( + MYSQL = "mysql" + MARIADB = "mariadb" +) + +type instance struct { + db *sql.DB + flavor string + version semver.Version + versionMajorMinor float64 +} + +func newInstance(dsn string) (*instance, error) { + i := &instance{} + db, err := sql.Open("mysql", dsn) + if err != nil { + return nil, err + } + db.SetMaxOpenConns(1) + db.SetMaxIdleConns(1) + i.db = db + + version, versionString, err := queryVersion(db) + if err != nil { + db.Close() + return nil, err + } + + i.version = version + + versionMajorMinor, err := strconv.ParseFloat(fmt.Sprintf("%d.%d", i.version.Major, i.version.Minor), 64) + if err != nil { + db.Close() + return nil, err + } + + i.versionMajorMinor = versionMajorMinor + + if strings.Contains(strings.ToLower(versionString), "mariadb") { + i.flavor = MARIADB + } else { + i.flavor = MYSQL + } + + return i, nil +} + +func (i *instance) getDB() *sql.DB { + return i.db +} + +func (i *instance) Close() error { + return i.db.Close() +} + +// Ping checks connection availability and possibly invalidates the connection if it fails. +func (i *instance) Ping() error { + if err := i.db.Ping(); err != nil { + if cerr := i.Close(); cerr != nil { + return err + } + return err + } + return nil +} + +// The result of SELECT version() is something like: +// for MariaDB: "10.5.17-MariaDB-1:10.5.17+maria~ubu2004-log" +// for MySQL: "8.0.36-28.1" +var versionRegex = regexp.MustCompile(`^((\d+)(\.\d+)(\.\d+))`) + +func queryVersion(db *sql.DB) (semver.Version, string, error) { + var version string + err := db.QueryRow("SELECT @@version;").Scan(&version) + if err != nil { + return semver.Version{}, version, err + } + + matches := versionRegex.FindStringSubmatch(version) + if len(matches) > 1 { + parsedVersion, err := semver.ParseTolerant(matches[1]) + if err != nil { + return semver.Version{}, version, fmt.Errorf("could not parse version from %q", matches[1]) + } + return parsedVersion, version, nil + } + + return semver.Version{}, version, fmt.Errorf("could not parse version from %q", version) +} diff --git a/collector/mysql_user.go b/collector/mysql_user.go index 50891a32b..b91db67cb 100644 --- a/collector/mysql_user.go +++ b/collector/mysql_user.go @@ -120,7 +120,8 @@ func (ScrapeUser) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeUser) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeUser) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() var ( userRows *sql.Rows err error diff --git a/collector/perf_schema_events_statements.go b/collector/perf_schema_events_statements.go index 568bde7c8..058995cb8 100644 --- a/collector/perf_schema_events_statements.go +++ b/collector/perf_schema_events_statements.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "fmt" "github.com/alecthomas/kingpin/v2" @@ -168,13 +167,14 @@ func (ScrapePerfEventsStatements) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfEventsStatements) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfEventsStatements) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { perfQuery := fmt.Sprintf( perfEventsStatementsQuery, *perfEventsStatementsDigestTextLimit, *perfEventsStatementsTimeLimit, *perfEventsStatementsLimit, ) + db := instance.getDB() // Timers here are returned in picoseconds. perfSchemaEventsStatementsRows, err := db.QueryContext(ctx, perfQuery) if err != nil { diff --git a/collector/perf_schema_events_statements_sum.go b/collector/perf_schema_events_statements_sum.go index e079199ba..fbb27af05 100644 --- a/collector/perf_schema_events_statements_sum.go +++ b/collector/perf_schema_events_statements_sum.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" @@ -177,7 +176,8 @@ func (ScrapePerfEventsStatementsSum) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfEventsStatementsSum) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfEventsStatementsSum) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() // Timers here are returned in picoseconds. perfEventsStatementsSumRows, err := db.QueryContext(ctx, perfEventsStatementsSumQuery) if err != nil { diff --git a/collector/perf_schema_events_waits.go b/collector/perf_schema_events_waits.go index 30df7cfa1..e485af0f6 100644 --- a/collector/perf_schema_events_waits.go +++ b/collector/perf_schema_events_waits.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" @@ -61,7 +60,8 @@ func (ScrapePerfEventsWaits) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfEventsWaits) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfEventsWaits) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() // Timers here are returned in picoseconds. perfSchemaEventsWaitsRows, err := db.QueryContext(ctx, perfEventsWaitsQuery) if err != nil { diff --git a/collector/perf_schema_file_events.go b/collector/perf_schema_file_events.go index fa9b6ce14..d2c1d1eda 100644 --- a/collector/perf_schema_file_events.go +++ b/collector/perf_schema_file_events.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" @@ -70,7 +69,8 @@ func (ScrapePerfFileEvents) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfFileEvents) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfFileEvents) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() // Timers here are returned in picoseconds. perfSchemaFileEventsRows, err := db.QueryContext(ctx, perfFileEventsQuery) if err != nil { diff --git a/collector/perf_schema_file_instances.go b/collector/perf_schema_file_instances.go index 4443a62ec..ce308f339 100644 --- a/collector/perf_schema_file_instances.go +++ b/collector/perf_schema_file_instances.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "strings" "github.com/alecthomas/kingpin/v2" @@ -80,7 +79,8 @@ func (ScrapePerfFileInstances) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfFileInstances) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfFileInstances) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() // Timers here are returned in picoseconds. perfSchemaFileInstancesRows, err := db.QueryContext(ctx, perfFileInstancesQuery, *performanceSchemaFileInstancesFilter) if err != nil { diff --git a/collector/perf_schema_file_instances_test.go b/collector/perf_schema_file_instances_test.go index c1485e5b7..9750dc0d6 100644 --- a/collector/perf_schema_file_instances_test.go +++ b/collector/perf_schema_file_instances_test.go @@ -37,6 +37,7 @@ func TestScrapePerfFileInstances(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"FILE_NAME", "EVENT_NAME", "COUNT_READ", "COUNT_WRITE", "SUM_NUMBER_OF_BYTES_READ", "SUM_NUMBER_OF_BYTES_WRITE"} @@ -48,7 +49,7 @@ func TestScrapePerfFileInstances(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapePerfFileInstances{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapePerfFileInstances{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { panic(fmt.Sprintf("error calling function on test: %s", err)) } close(ch) diff --git a/collector/perf_schema_index_io_waits.go b/collector/perf_schema_index_io_waits.go index 16cb203e6..24b9bf979 100644 --- a/collector/perf_schema_index_io_waits.go +++ b/collector/perf_schema_index_io_waits.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" @@ -64,7 +63,8 @@ func (ScrapePerfIndexIOWaits) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfIndexIOWaits) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfIndexIOWaits) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() perfSchemaIndexWaitsRows, err := db.QueryContext(ctx, perfIndexIOWaitsQuery) if err != nil { return err diff --git a/collector/perf_schema_index_io_waits_test.go b/collector/perf_schema_index_io_waits_test.go index 5ba9e5129..31c09da64 100644 --- a/collector/perf_schema_index_io_waits_test.go +++ b/collector/perf_schema_index_io_waits_test.go @@ -30,6 +30,7 @@ func TestScrapePerfIndexIOWaits(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"OBJECT_SCHEMA", "OBJECT_NAME", "INDEX_NAME", "COUNT_FETCH", "COUNT_INSERT", "COUNT_UPDATE", "COUNT_DELETE", "SUM_TIMER_FETCH", "SUM_TIMER_INSERT", "SUM_TIMER_UPDATE", "SUM_TIMER_DELETE"} rows := sqlmock.NewRows(columns). @@ -40,7 +41,7 @@ func TestScrapePerfIndexIOWaits(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapePerfIndexIOWaits{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapePerfIndexIOWaits{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/perf_schema_memory_events.go b/collector/perf_schema_memory_events.go index 288ecdd65..f4040793f 100644 --- a/collector/perf_schema_memory_events.go +++ b/collector/perf_schema_memory_events.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "strings" "github.com/alecthomas/kingpin/v2" @@ -79,7 +78,8 @@ func (ScrapePerfMemoryEvents) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfMemoryEvents) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfMemoryEvents) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() perfSchemaMemoryEventsRows, err := db.QueryContext(ctx, perfMemoryEventsQuery) if err != nil { return err diff --git a/collector/perf_schema_memory_events_test.go b/collector/perf_schema_memory_events_test.go index 1baa0ead7..53cacbe91 100644 --- a/collector/perf_schema_memory_events_test.go +++ b/collector/perf_schema_memory_events_test.go @@ -37,6 +37,7 @@ func TestScrapePerfMemoryEvents(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{ "EVENT_NAME", @@ -54,7 +55,7 @@ func TestScrapePerfMemoryEvents(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapePerfMemoryEvents{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapePerfMemoryEvents{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { panic(fmt.Sprintf("error calling function on test: %s", err)) } close(ch) diff --git a/collector/perf_schema_replication_applier_status_by_worker.go b/collector/perf_schema_replication_applier_status_by_worker.go index c74571bcc..59fac4b10 100644 --- a/collector/perf_schema_replication_applier_status_by_worker.go +++ b/collector/perf_schema_replication_applier_status_by_worker.go @@ -15,7 +15,6 @@ package collector import ( "context" - "database/sql" "time" "github.com/go-kit/log" @@ -101,7 +100,8 @@ func (ScrapePerfReplicationApplierStatsByWorker) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfReplicationApplierStatsByWorker) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfReplicationApplierStatsByWorker) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() perfReplicationApplierStatsByWorkerRows, err := db.QueryContext(ctx, perfReplicationApplierStatsByWorkerQuery) if err != nil { return err diff --git a/collector/perf_schema_replication_applier_status_by_worker_test.go b/collector/perf_schema_replication_applier_status_by_worker_test.go index cf638c0c5..2858933ee 100644 --- a/collector/perf_schema_replication_applier_status_by_worker_test.go +++ b/collector/perf_schema_replication_applier_status_by_worker_test.go @@ -31,6 +31,7 @@ func TestScrapePerfReplicationApplierStatsByWorker(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{ "CHANNEL_NAME", @@ -54,7 +55,7 @@ func TestScrapePerfReplicationApplierStatsByWorker(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapePerfReplicationApplierStatsByWorker{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapePerfReplicationApplierStatsByWorker{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/perf_schema_replication_group_member_stats.go b/collector/perf_schema_replication_group_member_stats.go index 32bec2ef3..94d4fa228 100644 --- a/collector/perf_schema_replication_group_member_stats.go +++ b/collector/perf_schema_replication_group_member_stats.go @@ -79,7 +79,8 @@ func (ScrapePerfReplicationGroupMemberStats) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfReplicationGroupMemberStats) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfReplicationGroupMemberStats) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() rows, err := db.QueryContext(ctx, perfReplicationGroupMemberStatsQuery) if err != nil { return err diff --git a/collector/perf_schema_replication_group_member_stats_test.go b/collector/perf_schema_replication_group_member_stats_test.go index 0559d293f..570f39b75 100644 --- a/collector/perf_schema_replication_group_member_stats_test.go +++ b/collector/perf_schema_replication_group_member_stats_test.go @@ -30,6 +30,7 @@ func TestScrapePerfReplicationGroupMemberStats(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{ "CHANNEL_NAME", @@ -66,7 +67,7 @@ func TestScrapePerfReplicationGroupMemberStats(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapePerfReplicationGroupMemberStats{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapePerfReplicationGroupMemberStats{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/perf_schema_replication_group_members.go b/collector/perf_schema_replication_group_members.go index a62db5e1c..1623a10bb 100644 --- a/collector/perf_schema_replication_group_members.go +++ b/collector/perf_schema_replication_group_members.go @@ -16,9 +16,10 @@ package collector import ( "context" "database/sql" + "strings" + "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" - "strings" ) const perfReplicationGroupMembersQuery = ` @@ -44,7 +45,8 @@ func (ScrapePerfReplicationGroupMembers) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfReplicationGroupMembers) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfReplicationGroupMembers) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() perfReplicationGroupMembersRows, err := db.QueryContext(ctx, perfReplicationGroupMembersQuery) if err != nil { return err diff --git a/collector/perf_schema_replication_group_members_test.go b/collector/perf_schema_replication_group_members_test.go index f660e3aec..c47aa9bac 100644 --- a/collector/perf_schema_replication_group_members_test.go +++ b/collector/perf_schema_replication_group_members_test.go @@ -15,12 +15,13 @@ package collector import ( "context" + "testing" + "github.com/DATA-DOG/go-sqlmock" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/smartystreets/goconvey/convey" - "testing" ) func TestScrapePerfReplicationGroupMembers(t *testing.T) { @@ -29,6 +30,7 @@ func TestScrapePerfReplicationGroupMembers(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{ "CHANNEL_NAME", @@ -49,7 +51,7 @@ func TestScrapePerfReplicationGroupMembers(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapePerfReplicationGroupMembers{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapePerfReplicationGroupMembers{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) @@ -82,6 +84,7 @@ func TestScrapePerfReplicationGroupMembersMySQL57(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{ "CHANNEL_NAME", @@ -100,7 +103,7 @@ func TestScrapePerfReplicationGroupMembersMySQL57(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapePerfReplicationGroupMembers{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapePerfReplicationGroupMembers{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/perf_schema_table_io_waits.go b/collector/perf_schema_table_io_waits.go index ccd9d3723..a74657b39 100644 --- a/collector/perf_schema_table_io_waits.go +++ b/collector/perf_schema_table_io_waits.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" @@ -65,7 +64,8 @@ func (ScrapePerfTableIOWaits) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfTableIOWaits) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfTableIOWaits) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() perfSchemaTableWaitsRows, err := db.QueryContext(ctx, perfTableIOWaitsQuery) if err != nil { return err diff --git a/collector/perf_schema_table_lock_waits.go b/collector/perf_schema_table_lock_waits.go index 0ff8d0a20..81b5d9f91 100644 --- a/collector/perf_schema_table_lock_waits.go +++ b/collector/perf_schema_table_lock_waits.go @@ -17,7 +17,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" @@ -94,7 +93,8 @@ func (ScrapePerfTableLockWaits) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapePerfTableLockWaits) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapePerfTableLockWaits) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + db := instance.getDB() perfSchemaTableLockWaitsRows, err := db.QueryContext(ctx, perfTableLockWaitsQuery) if err != nil { return err diff --git a/collector/scraper.go b/collector/scraper.go index e77fd8a35..a73819fb5 100644 --- a/collector/scraper.go +++ b/collector/scraper.go @@ -15,7 +15,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" _ "github.com/go-sql-driver/mysql" @@ -35,5 +34,5 @@ type Scraper interface { Version() float64 // Scrape collects data from database connection and sends it over channel as prometheus metric. - Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error + Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error } diff --git a/collector/slave_hosts.go b/collector/slave_hosts.go index b95110e13..eff61ff8f 100644 --- a/collector/slave_hosts.go +++ b/collector/slave_hosts.go @@ -63,11 +63,12 @@ func (ScrapeSlaveHosts) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeSlaveHosts) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeSlaveHosts) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var ( slaveHostsRows *sql.Rows err error ) + db := instance.getDB() // Try the both syntax for MySQL 8.0 and MySQL 8.4 if slaveHostsRows, err = db.QueryContext(ctx, slaveHostsQuery); err != nil { if slaveHostsRows, err = db.QueryContext(ctx, showReplicasQuery); err != nil { diff --git a/collector/slave_hosts_test.go b/collector/slave_hosts_test.go index e0d4e6592..58db11298 100644 --- a/collector/slave_hosts_test.go +++ b/collector/slave_hosts_test.go @@ -30,6 +30,7 @@ func TestScrapeSlaveHostsOldFormat(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"Server_id", "Host", "Port", "Rpl_recovery_rank", "Master_id"} rows := sqlmock.NewRows(columns). @@ -39,7 +40,7 @@ func TestScrapeSlaveHostsOldFormat(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeSlaveHosts{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeSlaveHosts{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) @@ -68,6 +69,7 @@ func TestScrapeSlaveHostsNewFormat(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"Server_id", "Host", "Port", "Master_id", "Slave_UUID"} rows := sqlmock.NewRows(columns). @@ -77,7 +79,7 @@ func TestScrapeSlaveHostsNewFormat(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeSlaveHosts{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeSlaveHosts{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) @@ -106,6 +108,7 @@ func TestScrapeSlaveHostsWithoutSlaveUuid(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"Server_id", "Host", "Port", "Master_id"} rows := sqlmock.NewRows(columns). @@ -115,7 +118,7 @@ func TestScrapeSlaveHostsWithoutSlaveUuid(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeSlaveHosts{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeSlaveHosts{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/slave_status.go b/collector/slave_status.go index b7984657f..703dad4f4 100644 --- a/collector/slave_status.go +++ b/collector/slave_status.go @@ -69,11 +69,12 @@ func (ScrapeSlaveStatus) Version() float64 { } // Scrape collects data from database connection and sends it over channel as prometheus metric. -func (ScrapeSlaveStatus) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeSlaveStatus) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { var ( slaveStatusRows *sql.Rows err error ) + db := instance.getDB() // Try the both syntax for MySQL/Percona and MariaDB for _, query := range slaveStatusQueries { slaveStatusRows, err = db.QueryContext(ctx, query) diff --git a/collector/slave_status_test.go b/collector/slave_status_test.go index c3830bc96..31324f9c8 100644 --- a/collector/slave_status_test.go +++ b/collector/slave_status_test.go @@ -30,6 +30,7 @@ func TestScrapeSlaveStatus(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{"Master_Host", "Read_Master_Log_Pos", "Slave_IO_Running", "Slave_SQL_Running", "Seconds_Behind_Master"} rows := sqlmock.NewRows(columns). @@ -38,7 +39,7 @@ func TestScrapeSlaveStatus(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeSlaveStatus{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeSlaveStatus{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/collector/sys_user_summary.go b/collector/sys_user_summary.go index 16a1c6f02..a2ab948b8 100644 --- a/collector/sys_user_summary.go +++ b/collector/sys_user_summary.go @@ -15,7 +15,6 @@ package collector import ( "context" - "database/sql" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" @@ -99,7 +98,9 @@ func (ScrapeSysUserSummary) Version() float64 { } // Scrape the information from sys.user_summary, creating a metric for each value of each row, labeled with the user -func (ScrapeSysUserSummary) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error { +func (ScrapeSysUserSummary) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error { + + db := instance.getDB() userSummaryRows, err := db.QueryContext(ctx, sysUserSummaryQuery) if err != nil { diff --git a/collector/sys_user_summary_test.go b/collector/sys_user_summary_test.go index 277dcccc5..ad6d1d705 100644 --- a/collector/sys_user_summary_test.go +++ b/collector/sys_user_summary_test.go @@ -16,14 +16,15 @@ package collector import ( "context" "database/sql/driver" + "regexp" + "strconv" + "testing" + "github.com/DATA-DOG/go-sqlmock" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/smartystreets/goconvey/convey" - "regexp" - "strconv" - "testing" ) func TestScrapeSysUserSummary(t *testing.T) { @@ -33,6 +34,7 @@ func TestScrapeSysUserSummary(t *testing.T) { t.Fatalf("error opening a stub database connection: %s", err) } defer db.Close() + inst := &instance{db: db} columns := []string{ "user", @@ -111,7 +113,7 @@ func TestScrapeSysUserSummary(t *testing.T) { ch := make(chan prometheus.Metric) go func() { - if err = (ScrapeSysUserSummary{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + if err = (ScrapeSysUserSummary{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil { t.Errorf("error calling function on test: %s", err) } close(ch) diff --git a/go.mod b/go.mod index 43b6cbdbc..216edfc8f 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.21 require ( github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/alecthomas/kingpin/v2 v2.4.0 + github.com/blang/semver/v4 v4.0.0 github.com/go-kit/log v0.2.1 github.com/go-sql-driver/mysql v1.8.1 github.com/google/go-cmp v0.6.0 diff --git a/go.sum b/go.sum index 82e1f4ef1..6df7d4a13 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAu github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=