From fb2cf7864d28121a7e981ed13d5b72865545eec0 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: - version - flavor (mariadb or mysql) Change is similar to prometheus-community/postgres_exporter#785 Signed-off-by: Vlad Gusev --- 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 1cf76b95..7c5c9495 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 bfe8f382..e85efb67 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 9b1a33c2..20603817 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 bf19caf6..07817f1a 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 d4e89adc..52c411c8 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 627aa31f..d08446d3 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 0587f1d9..2f12e3f9 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 7eb8ba4c..3f742263 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 21920a71..c0a131ea 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 14456c91..eea8fd58 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 b2717fb9..6b286ce1 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 4f0ab36f..1bab0a9d 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 7bc5fc58..6b32048f 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 48e35925..1d553bc3 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 d52ebd27..ed40f33a 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 619fb4a0..64afe35a 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 c77e2401..cd14ad1a 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 2c8dfa4c..2db73062 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 2143af09..26e19fee 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 61f4938a..607bd9f5 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 346b2d23..77558eb6 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 f077dd5a..4abb0a8f 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 43b0de24..ea8b295c 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 29218926..0e06095a 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 05553287..30bdb521 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 0d97161c..80d2c493 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 6bcd0108..beaa349c 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 bf5b14df..f163eccb 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 8c766686..bf342f9c 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 106bbbd0..78a7e5e4 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 08d881b6..1bc56bb0 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 64ff6528..2825e3ef 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 bc57fc6f..4d90b1d4 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 f526f4d6..d5537561 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 b4d964df..29fabb91 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 9b678641..b50abc62 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 329d3b0a..c8c55e07 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 99f54ebc..57292c29 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 00000000..052aee01 --- /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 ( + FlavorMySQL = "mysql" + FlavorMariaDB = "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 = FlavorMariaDB + } else { + i.flavor = FlavorMySQL + } + + 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 50891a32..b91db67c 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 568bde7c..058995cb 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 e079199b..fbb27af0 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 30df7cfa..e485af0f 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 fa9b6ce1..d2c1d1ed 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 4443a62e..ce308f33 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 c1485e5b..9750dc0d 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 16cb203e..24b9bf97 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 5ba9e512..31c09da6 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 288ecdd6..f4040793 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 1baa0ead..53cacbe9 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 c74571bc..59fac4b1 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 cf638c0c..2858933e 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 32bec2ef..94d4fa22 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 0559d293..570f39b7 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 a62db5e1..1623a10b 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 f660e3ae..c47aa9ba 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 ccd9d372..a74657b3 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 0ff8d0a2..81b5d9f9 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 e77fd8a3..a73819fb 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 b95110e1..eff61ff8 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 e0d4e659..58db1129 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 b7984657..703dad4f 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 c3830bc9..31324f9c 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 16a1c6f0..a2ab948b 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 277dcccc..ad6d1d70 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 43b6cbdb..216edfc8 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 82e1f4ef..6df7d4a1 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=