Skip to content

Commit 50ed93b

Browse files
committed
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 <[email protected]>
1 parent dd8afce commit 50ed93b

File tree

67 files changed

+267
-156
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+267
-156
lines changed

collector/binlog.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package collector
1717

1818
import (
1919
"context"
20-
"database/sql"
2120
"fmt"
2221
"strconv"
2322
"strings"
@@ -72,8 +71,9 @@ func (ScrapeBinlogSize) Version() float64 {
7271
}
7372

7473
// Scrape collects data from database connection and sends it over channel as prometheus metric.
75-
func (ScrapeBinlogSize) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error {
74+
func (ScrapeBinlogSize) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error {
7675
var logBin uint8
76+
db := instance.getDB()
7777
err := db.QueryRowContext(ctx, logbinQuery).Scan(&logBin)
7878
if err != nil {
7979
return err

collector/binlog_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ func TestScrapeBinlogSize(t *testing.T) {
3131
}
3232
defer db.Close()
3333

34+
inst := &instance{db: db}
35+
3436
mock.ExpectQuery(logbinQuery).WillReturnRows(sqlmock.NewRows([]string{""}).AddRow(1))
3537

3638
columns := []string{"Log_name", "File_size"}
@@ -42,7 +44,7 @@ func TestScrapeBinlogSize(t *testing.T) {
4244

4345
ch := make(chan prometheus.Metric)
4446
go func() {
45-
if err = (ScrapeBinlogSize{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil {
47+
if err = (ScrapeBinlogSize{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil {
4648
t.Errorf("error calling function on test: %s", err)
4749
}
4850
close(ch)

collector/engine_innodb.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package collector
1717

1818
import (
1919
"context"
20-
"database/sql"
2120
"regexp"
2221
"strconv"
2322
"strings"
@@ -52,7 +51,8 @@ func (ScrapeEngineInnodbStatus) Version() float64 {
5251
}
5352

5453
// Scrape collects data from database connection and sends it over channel as prometheus metric.
55-
func (ScrapeEngineInnodbStatus) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error {
54+
func (ScrapeEngineInnodbStatus) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error {
55+
db := instance.getDB()
5656
rows, err := db.QueryContext(ctx, engineInnodbStatusQuery)
5757
if err != nil {
5858
return err

collector/engine_innodb_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,10 @@ END OF INNODB MONITOR OUTPUT
152152
rows := sqlmock.NewRows(columns).AddRow("InnoDB", "", sample)
153153

154154
mock.ExpectQuery(sanitizeQuery(engineInnodbStatusQuery)).WillReturnRows(rows)
155-
155+
inst := &instance{db: db}
156156
ch := make(chan prometheus.Metric)
157157
go func() {
158-
if err = (ScrapeEngineInnodbStatus{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil {
158+
if err = (ScrapeEngineInnodbStatus{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil {
159159
t.Errorf("error calling function on test: %s", err)
160160
}
161161
close(ch)

collector/engine_tokudb.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ func (ScrapeEngineTokudbStatus) Version() float64 {
5050
}
5151

5252
// Scrape collects data from database connection and sends it over channel as prometheus metric.
53-
func (ScrapeEngineTokudbStatus) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error {
53+
func (ScrapeEngineTokudbStatus) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error {
54+
db := instance.getDB()
5455
tokudbRows, err := db.QueryContext(ctx, engineTokudbStatusQuery)
5556
if err != nil {
5657
return err

collector/engine_tokudb_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func TestScrapeEngineTokudbStatus(t *testing.T) {
4646
t.Fatalf("error opening a stub database connection: %s", err)
4747
}
4848
defer db.Close()
49+
inst := &instance{db: db}
4950

5051
columns := []string{"Type", "Name", "Status"}
5152
rows := sqlmock.NewRows(columns).
@@ -59,7 +60,7 @@ func TestScrapeEngineTokudbStatus(t *testing.T) {
5960

6061
ch := make(chan prometheus.Metric)
6162
go func() {
62-
if err = (ScrapeEngineTokudbStatus{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil {
63+
if err = (ScrapeEngineTokudbStatus{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil {
6364
t.Errorf("error calling function on test: %s", err)
6465
}
6566
close(ch)

collector/exporter.go

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ package collector
1515

1616
import (
1717
"context"
18-
"database/sql"
1918
"fmt"
20-
"regexp"
21-
"strconv"
2219
"strings"
2320
"sync"
2421
"time"
@@ -38,18 +35,12 @@ const (
3835

3936
// SQL queries and parameters.
4037
const (
41-
versionQuery = `SELECT @@version`
42-
4338
// System variable params formatting.
4439
// See: https://github.com/go-sql-driver/mysql#system-variables
4540
sessionSettingsParam = `log_slow_filter=%27tmp_table_on_disk,filesort_on_disk%27`
4641
timeoutParam = `lock_wait_timeout=%d`
4742
)
4843

49-
var (
50-
versionRE = regexp.MustCompile(`^\d+\.\d+`)
51-
)
52-
5344
// Tunable flags.
5445
var (
5546
exporterLockTimeout = kingpin.Flag(
@@ -92,6 +83,7 @@ type Exporter struct {
9283
logger log.Logger
9384
dsn string
9485
scrapers []Scraper
86+
instance *instance
9587
}
9688

9789
// New returns a new MySQL exporter for the provided DSN.
@@ -135,27 +127,23 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
135127
func (e *Exporter) scrape(ctx context.Context, ch chan<- prometheus.Metric) float64 {
136128
var err error
137129
scrapeTime := time.Now()
138-
db, err := sql.Open("mysql", e.dsn)
130+
instance, err := newInstance(e.dsn)
139131
if err != nil {
140132
level.Error(e.logger).Log("msg", "Error opening connection to database", "err", err)
141133
return 0.0
142134
}
143-
defer db.Close()
144-
145-
// By design exporter should use maximum one connection per request.
146-
db.SetMaxOpenConns(1)
147-
db.SetMaxIdleConns(1)
148-
// Set max lifetime for a connection.
149-
db.SetConnMaxLifetime(1 * time.Minute)
135+
defer instance.Close()
136+
e.instance = instance
150137

151-
if err := db.PingContext(ctx); err != nil {
138+
if err := instance.Ping(); err != nil {
152139
level.Error(e.logger).Log("msg", "Error pinging mysqld", "err", err)
153140
return 0.0
154141
}
155142

156143
ch <- prometheus.MustNewConstMetric(mysqlScrapeDurationSeconds, prometheus.GaugeValue, time.Since(scrapeTime).Seconds(), "connection")
157144

158-
version := getMySQLVersion(db, e.logger)
145+
version := instance.versionMajorMinor
146+
159147
var wg sync.WaitGroup
160148
defer wg.Wait()
161149
for _, scraper := range e.scrapers {
@@ -169,7 +157,7 @@ func (e *Exporter) scrape(ctx context.Context, ch chan<- prometheus.Metric) floa
169157
label := "collect." + scraper.Name()
170158
scrapeTime := time.Now()
171159
collectorSuccess := 1.0
172-
if err := scraper.Scrape(ctx, db, ch, log.With(e.logger, "scraper", scraper.Name())); err != nil {
160+
if err := scraper.Scrape(ctx, instance, ch, log.With(e.logger, "scraper", scraper.Name())); err != nil {
173161
level.Error(e.logger).Log("msg", "Error from scraper", "scraper", scraper.Name(), "target", e.getTargetFromDsn(), "err", err)
174162
collectorSuccess = 0.0
175163
}
@@ -189,19 +177,3 @@ func (e *Exporter) getTargetFromDsn() string {
189177
}
190178
return dsnConfig.Addr
191179
}
192-
193-
func getMySQLVersion(db *sql.DB, logger log.Logger) float64 {
194-
var versionStr string
195-
var versionNum float64
196-
if err := db.QueryRow(versionQuery).Scan(&versionStr); err == nil {
197-
versionNum, _ = strconv.ParseFloat(versionRE.FindString(versionStr), 64)
198-
} else {
199-
level.Debug(logger).Log("msg", "Error querying version", "err", err)
200-
}
201-
// If we can't match/parse the version, set it some big value that matches all versions.
202-
if versionNum == 0 {
203-
level.Debug(logger).Log("msg", "Error parsing version string", "version", versionStr)
204-
versionNum = 999
205-
}
206-
return versionNum
207-
}

collector/exporter_test.go

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,9 @@ package collector
1515

1616
import (
1717
"context"
18-
"database/sql"
19-
"os"
2018
"testing"
2119

2220
"github.com/go-kit/log"
23-
"github.com/go-kit/log/level"
2421
"github.com/prometheus/client_golang/prometheus"
2522
"github.com/prometheus/common/model"
2623
"github.com/smartystreets/goconvey/convey"
@@ -68,20 +65,3 @@ func TestExporter(t *testing.T) {
6865
}
6966
})
7067
}
71-
72-
func TestGetMySQLVersion(t *testing.T) {
73-
if testing.Short() {
74-
t.Skip("-short is passed, skipping test")
75-
}
76-
77-
logger := log.NewLogfmtLogger(os.Stderr)
78-
logger = level.NewFilter(logger, level.AllowDebug())
79-
80-
convey.Convey("Version parsing", t, func() {
81-
db, err := sql.Open("mysql", dsn)
82-
convey.So(err, convey.ShouldBeNil)
83-
defer db.Close()
84-
85-
convey.So(getMySQLVersion(db, logger), convey.ShouldBeBetweenOrEqual, 5.6, 12.0)
86-
})
87-
}

collector/global_status.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ func (ScrapeGlobalStatus) Version() float64 {
9999
}
100100

101101
// Scrape collects data from database connection and sends it over channel as prometheus metric.
102-
func (ScrapeGlobalStatus) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error {
102+
func (ScrapeGlobalStatus) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger log.Logger) error {
103+
db := instance.getDB()
103104
globalStatusRows, err := db.QueryContext(ctx, globalStatusQuery)
104105
if err != nil {
105106
return err

collector/global_status_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func TestScrapeGlobalStatus(t *testing.T) {
3030
t.Fatalf("error opening a stub database connection: %s", err)
3131
}
3232
defer db.Close()
33+
inst := &instance{db: db}
3334

3435
columns := []string{"Variable_name", "Value"}
3536
rows := sqlmock.NewRows(columns).
@@ -63,7 +64,7 @@ func TestScrapeGlobalStatus(t *testing.T) {
6364

6465
ch := make(chan prometheus.Metric)
6566
go func() {
66-
if err = (ScrapeGlobalStatus{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil {
67+
if err = (ScrapeGlobalStatus{}).Scrape(context.Background(), inst, ch, log.NewNopLogger()); err != nil {
6768
t.Errorf("error calling function on test: %s", err)
6869
}
6970
close(ch)

0 commit comments

Comments
 (0)