From 5132a1c7864acf9283fea5761c5290a7ba986ca1 Mon Sep 17 00:00:00 2001 From: Felix Hamme Date: Mon, 13 Sep 2021 14:34:33 +0200 Subject: [PATCH] fix mysql_slave_hosts_info for mysql 5.5 and mariadb 10.5, add unit test The "show slave hosts" command on mysql 5.5 and mariadb 10.5 returns neither a "Slave_UUID" nor a "Rpl_recovery_rank" column. Set the slave_uuid metric label to "" in this case. Signed-off-by: Felix Hamme --- collector/slave_hosts.go | 35 +++++++++++++++++++++++--------- collector/slave_hosts_test.go | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/collector/slave_hosts.go b/collector/slave_hosts.go index 536d4b78..26678dc5 100644 --- a/collector/slave_hosts.go +++ b/collector/slave_hosts.go @@ -80,25 +80,42 @@ func (ScrapeSlaveHosts) Scrape(ctx context.Context, db *sql.DB, ch chan<- promet var masterId string var slaveUuid string + columnNames, err := slaveHostsRows.Columns() + if err != nil { + return err + } + for slaveHostsRows.Next() { // Newer versions of mysql have the following // Server_id, Host, Port, Master_id, Slave_UUID // Older versions of mysql have the following // Server_id, Host, Port, Rpl_recovery_rank, Master_id - err := slaveHostsRows.Scan(&serverId, &host, &port, &rrrOrMasterId, &slaveUuidOrMasterId) + // MySQL 5.5 and MariaDB 10.5 have the following + // Server_id, Host, Port, Master_id + if len(columnNames) == 5 { + err = slaveHostsRows.Scan(&serverId, &host, &port, &rrrOrMasterId, &slaveUuidOrMasterId) + } else { + err = slaveHostsRows.Scan(&serverId, &host, &port, &rrrOrMasterId) + } if err != nil { return err } - // Check to see if slaveUuidOrMasterId resembles a UUID or not - // to find out if we are using an old version of MySQL - if _, err = uuid.FromString(slaveUuidOrMasterId); err != nil { - // We are running an older version of MySQL with no slave UUID - slaveUuid = "" - masterId = slaveUuidOrMasterId + // if a Slave_UUID or Rpl_recovery_rank field is present + if len(columnNames) == 5 { + // Check to see if slaveUuidOrMasterId resembles a UUID or not + // to find out if we are using an old version of MySQL + if _, err = uuid.FromString(slaveUuidOrMasterId); err != nil { + // We are running an older version of MySQL with no slave UUID + slaveUuid = "" + masterId = slaveUuidOrMasterId + } else { + // We are running a more recent version of MySQL + slaveUuid = slaveUuidOrMasterId + masterId = rrrOrMasterId + } } else { - // We are running a more recent version of MySQL - slaveUuid = slaveUuidOrMasterId + slaveUuid = "" masterId = rrrOrMasterId } diff --git a/collector/slave_hosts_test.go b/collector/slave_hosts_test.go index 08b1612b..9f3f3848 100644 --- a/collector/slave_hosts_test.go +++ b/collector/slave_hosts_test.go @@ -99,3 +99,41 @@ func TestScrapeSlaveHostsNewFormat(t *testing.T) { t.Errorf("there were unfulfilled exceptions: %s", err) } } + +func TestScrapeSlaveHostsWithoutSlaveUuid(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("error opening a stub database connection: %s", err) + } + defer db.Close() + + columns := []string{"Server_id", "Host", "Port", "Master_id"} + rows := sqlmock.NewRows(columns). + AddRow("192168010", "iconnect2", "3306", "192168012"). + AddRow("1921680101", "athena", "3306", "192168012") + mock.ExpectQuery(sanitizeQuery("SHOW SLAVE HOSTS")).WillReturnRows(rows) + + ch := make(chan prometheus.Metric) + go func() { + if err = (ScrapeSlaveHosts{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil { + t.Errorf("error calling function on test: %s", err) + } + close(ch) + }() + + counterExpected := []MetricResult{ + {labels: labelMap{"server_id": "192168010", "slave_host": "iconnect2", "port": "3306", "master_id": "192168012", "slave_uuid": ""}, value: 1, metricType: dto.MetricType_GAUGE}, + {labels: labelMap{"server_id": "1921680101", "slave_host": "athena", "port": "3306", "master_id": "192168012", "slave_uuid": ""}, value: 1, metricType: dto.MetricType_GAUGE}, + } + convey.Convey("Metrics comparison", t, func() { + for _, expect := range counterExpected { + got := readMetric(<-ch) + convey.So(got, convey.ShouldResemble, expect) + } + }) + + // Ensure all SQL queries were executed + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled exceptions: %s", err) + } +}