Skip to content

Commit d9d5543

Browse files
authored
Merge pull request #462 from roman-vynar/gr-stats
Rewrite replication_group_member_stats collector
2 parents 5673ffc + 22f42da commit d9d5543

3 files changed

+163
-55
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
* [FEATURE]
1111

1212
* [FEATURE] Add `tls.insecure-skip-verify` flag to ignore tls verification errors (PR #417) #348
13+
* [FEATURE] Add new metrics to `replication_group_member_stats` collector to support MySQL 8.x.
14+
15+
### BREAKING CHANGES:
16+
17+
Changes related to `replication_group_member_stats` collector:
18+
* metric "transaction_in_queue" was Counter instead of Gauge
19+
* renamed 3 metrics starting with `mysql_perf_schema_transaction_` to start with `mysql_perf_schema_transactions_` to be consistent with column names
20+
* exposing only server's own stats by matching MEMBER_ID with @@server_uuid resulting "member_id" label to be dropped.
1321

1422
## 0.12.1 / 2019-07-10
1523

collector/perf_schema_replication_group_member_stats.go

+59-55
Original file line numberDiff line numberDiff line change
@@ -16,42 +16,51 @@ package collector
1616
import (
1717
"context"
1818
"database/sql"
19+
"strconv"
1920

2021
"github.com/go-kit/kit/log"
2122
"github.com/prometheus/client_golang/prometheus"
2223
)
2324

24-
const perfReplicationGroupMemeberStatsQuery = `
25-
SELECT MEMBER_ID,COUNT_TRANSACTIONS_IN_QUEUE,COUNT_TRANSACTIONS_CHECKED,COUNT_CONFLICTS_DETECTED,COUNT_TRANSACTIONS_ROWS_VALIDATING
26-
FROM performance_schema.replication_group_member_stats
27-
`
25+
const perfReplicationGroupMemberStatsQuery = `
26+
SELECT * FROM performance_schema.replication_group_member_stats WHERE MEMBER_ID=@@server_uuid
27+
`
2828

29-
// Metric descriptors.
3029
var (
31-
performanceSchemaReplicationGroupMemberStatsTransInQueueDesc = prometheus.NewDesc(
32-
prometheus.BuildFQName(namespace, performanceSchema, "transaction_in_queue"),
33-
"The number of transactions in the queue pending conflict detection checks. Once the "+
34-
"transactions have been checked for conflicts, if they pass the check, they are queued to be applied as well.",
35-
[]string{"member_id"}, nil,
36-
)
37-
performanceSchemaReplicationGroupMemberStatsTransCheckedDesc = prometheus.NewDesc(
38-
prometheus.BuildFQName(namespace, performanceSchema, "transaction_checked"),
39-
"The number of transactions that have been checked for conflicts.",
40-
[]string{"member_id"}, nil,
41-
)
42-
performanceSchemaReplicationGroupMemberStatsConflictsDetectedDesc = prometheus.NewDesc(
43-
prometheus.BuildFQName(namespace, performanceSchema, "conflicts_detected"),
44-
"The number of transactions that did not pass the conflict detection check.",
45-
[]string{"member_id"}, nil,
46-
)
47-
performanceSchemaReplicationGroupMemberStatsTransRowValidatingDesc = prometheus.NewDesc(
48-
prometheus.BuildFQName(namespace, performanceSchema, "transaction_rows_validating"),
49-
"The current size of the conflict detection database (against which each transaction is certified).",
50-
[]string{"member_id"}, nil,
51-
)
30+
// The list of columns we are interesting in.
31+
// In MySQL 5.7 these are the 4 first columns available. In MySQL 8.x all 8.
32+
perfReplicationGroupMemberStats = map[string]struct {
33+
vtype prometheus.ValueType
34+
desc *prometheus.Desc
35+
}{
36+
"COUNT_TRANSACTIONS_IN_QUEUE": {prometheus.GaugeValue,
37+
prometheus.NewDesc(prometheus.BuildFQName(namespace, performanceSchema, "transactions_in_queue"),
38+
"The number of transactions in the queue pending conflict detection checks.", nil, nil)},
39+
"COUNT_TRANSACTIONS_CHECKED": {prometheus.CounterValue,
40+
prometheus.NewDesc(prometheus.BuildFQName(namespace, performanceSchema, "transactions_checked_total"),
41+
"The number of transactions that have been checked for conflicts.", nil, nil)},
42+
"COUNT_CONFLICTS_DETECTED": {prometheus.CounterValue,
43+
prometheus.NewDesc(prometheus.BuildFQName(namespace, performanceSchema, "conflicts_detected_total"),
44+
"The number of transactions that have not passed the conflict detection check.", nil, nil)},
45+
"COUNT_TRANSACTIONS_ROWS_VALIDATING": {prometheus.CounterValue,
46+
prometheus.NewDesc(prometheus.BuildFQName(namespace, performanceSchema, "transactions_rows_validating_total"),
47+
"Number of transaction rows which can be used for certification, but have not been garbage collected.", nil, nil)},
48+
"COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE": {prometheus.GaugeValue,
49+
prometheus.NewDesc(prometheus.BuildFQName(namespace, performanceSchema, "transactions_remote_in_applier_queue"),
50+
"The number of transactions that this member has received from the replication group which are waiting to be applied.", nil, nil)},
51+
"COUNT_TRANSACTIONS_REMOTE_APPLIED": {prometheus.CounterValue,
52+
prometheus.NewDesc(prometheus.BuildFQName(namespace, performanceSchema, "transactions_remote_applied_total"),
53+
"Number of transactions this member has received from the group and applied.", nil, nil)},
54+
"COUNT_TRANSACTIONS_LOCAL_PROPOSED": {prometheus.CounterValue,
55+
prometheus.NewDesc(prometheus.BuildFQName(namespace, performanceSchema, "transactions_local_proposed_total"),
56+
"Number of transactions which originated on this member and were sent to the group.", nil, nil)},
57+
"COUNT_TRANSACTIONS_LOCAL_ROLLBACK": {prometheus.CounterValue,
58+
prometheus.NewDesc(prometheus.BuildFQName(namespace, performanceSchema, "transactions_local_rollback_total"),
59+
"Number of transactions which originated on this member and were rolled back by the group.", nil, nil)},
60+
}
5261
)
5362

54-
// ScrapeReplicationGroupMemberStats collects from `performance_schema.replication_group_member_stats`.
63+
// ScrapePerfReplicationGroupMemberStats collects from `performance_schema.replication_group_member_stats`.
5564
type ScrapePerfReplicationGroupMemberStats struct{}
5665

5766
// Name of the Scraper. Should be unique.
@@ -71,41 +80,36 @@ func (ScrapePerfReplicationGroupMemberStats) Version() float64 {
7180

7281
// Scrape collects data from database connection and sends it over channel as prometheus metric.
7382
func (ScrapePerfReplicationGroupMemberStats) Scrape(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric, logger log.Logger) error {
74-
perfReplicationGroupMemeberStatsRows, err := db.QueryContext(ctx, perfReplicationGroupMemeberStatsQuery)
83+
rows, err := db.QueryContext(ctx, perfReplicationGroupMemberStatsQuery)
7584
if err != nil {
7685
return err
7786
}
78-
defer perfReplicationGroupMemeberStatsRows.Close()
87+
defer rows.Close()
7988

80-
var (
81-
memberId string
82-
countTransactionsInQueue, countTransactionsChecked uint64
83-
countConflictsDetected, countTransactionsRowsValidating uint64
84-
)
89+
var columnNames []string
90+
if columnNames, err = rows.Columns(); err != nil {
91+
return err
92+
}
8593

86-
for perfReplicationGroupMemeberStatsRows.Next() {
87-
if err := perfReplicationGroupMemeberStatsRows.Scan(
88-
&memberId, &countTransactionsInQueue, &countTransactionsChecked,
89-
&countConflictsDetected, &countTransactionsRowsValidating,
90-
); err != nil {
94+
var scanArgs = make([]interface{}, len(columnNames))
95+
for i := range scanArgs {
96+
scanArgs[i] = &sql.RawBytes{}
97+
}
98+
99+
for rows.Next() {
100+
if err := rows.Scan(scanArgs...); err != nil {
91101
return err
92102
}
93-
ch <- prometheus.MustNewConstMetric(
94-
performanceSchemaReplicationGroupMemberStatsTransInQueueDesc, prometheus.CounterValue, float64(countTransactionsInQueue),
95-
memberId,
96-
)
97-
ch <- prometheus.MustNewConstMetric(
98-
performanceSchemaReplicationGroupMemberStatsTransCheckedDesc, prometheus.CounterValue, float64(countTransactionsChecked),
99-
memberId,
100-
)
101-
ch <- prometheus.MustNewConstMetric(
102-
performanceSchemaReplicationGroupMemberStatsConflictsDetectedDesc, prometheus.CounterValue, float64(countConflictsDetected),
103-
memberId,
104-
)
105-
ch <- prometheus.MustNewConstMetric(
106-
performanceSchemaReplicationGroupMemberStatsTransRowValidatingDesc, prometheus.CounterValue, float64(countTransactionsRowsValidating),
107-
memberId,
108-
)
103+
104+
for i, columnName := range columnNames {
105+
if metric, ok := perfReplicationGroupMemberStats[columnName]; ok {
106+
value, err := strconv.ParseFloat(string(*scanArgs[i].(*sql.RawBytes)), 64)
107+
if err != nil {
108+
return err
109+
}
110+
ch <- prometheus.MustNewConstMetric(metric.desc, metric.vtype, value)
111+
}
112+
}
109113
}
110114
return nil
111115
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright 2018 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package collector
15+
16+
import (
17+
"context"
18+
"testing"
19+
20+
"github.com/DATA-DOG/go-sqlmock"
21+
"github.com/go-kit/kit/log"
22+
"github.com/prometheus/client_golang/prometheus"
23+
dto "github.com/prometheus/client_model/go"
24+
"github.com/smartystreets/goconvey/convey"
25+
)
26+
27+
func TestScrapePerfReplicationGroupMemberStats(t *testing.T) {
28+
db, mock, err := sqlmock.New()
29+
if err != nil {
30+
t.Fatalf("error opening a stub database connection: %s", err)
31+
}
32+
defer db.Close()
33+
34+
columns := []string{
35+
"CHANNEL_NAME",
36+
"VIEW_ID",
37+
"MEMBER_ID",
38+
"COUNT_TRANSACTIONS_IN_QUEUE",
39+
"COUNT_TRANSACTIONS_CHECKED",
40+
"COUNT_CONFLICTS_DETECTED",
41+
"COUNT_TRANSACTIONS_ROWS_VALIDATING",
42+
"TRANSACTIONS_COMMITTED_ALL_MEMBERS",
43+
"LAST_CONFLICT_FREE_TRANSACTION",
44+
"COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE",
45+
"COUNT_TRANSACTIONS_REMOTE_APPLIED",
46+
"COUNT_TRANSACTIONS_LOCAL_PROPOSED",
47+
"COUNT_TRANSACTIONS_LOCAL_ROLLBACK",
48+
}
49+
rows := sqlmock.NewRows(columns).
50+
AddRow(
51+
"group_replication_applier",
52+
"15813535259046852:43",
53+
"e14c4f71-025f-11ea-b800-0620049edbec",
54+
float64(0),
55+
float64(7389775),
56+
float64(1),
57+
float64(48),
58+
"0515b3c2-f59f-11e9-881b-0620049edbec:1-15270987,\n8f782839-34f7-11e7-a774-060ac4f023ae:4-39:2387-161606",
59+
"0515b3c2-f59f-11e9-881b-0620049edbec:15271011",
60+
float64(2),
61+
float64(22),
62+
float64(7389759),
63+
float64(7),
64+
)
65+
mock.ExpectQuery(sanitizeQuery(perfReplicationGroupMemberStatsQuery)).WillReturnRows(rows)
66+
67+
ch := make(chan prometheus.Metric)
68+
go func() {
69+
if err = (ScrapePerfReplicationGroupMemberStats{}).Scrape(context.Background(), db, ch, log.NewNopLogger()); err != nil {
70+
t.Errorf("error calling function on test: %s", err)
71+
}
72+
close(ch)
73+
}()
74+
75+
expected := []MetricResult{
76+
{labels: labelMap{}, value: 0, metricType: dto.MetricType_GAUGE},
77+
{labels: labelMap{}, value: float64(7389775), metricType: dto.MetricType_COUNTER},
78+
{labels: labelMap{}, value: float64(1), metricType: dto.MetricType_COUNTER},
79+
{labels: labelMap{}, value: float64(48), metricType: dto.MetricType_COUNTER},
80+
{labels: labelMap{}, value: 2, metricType: dto.MetricType_GAUGE},
81+
{labels: labelMap{}, value: float64(22), metricType: dto.MetricType_COUNTER},
82+
{labels: labelMap{}, value: float64(7389759), metricType: dto.MetricType_COUNTER},
83+
{labels: labelMap{}, value: float64(7), metricType: dto.MetricType_COUNTER},
84+
}
85+
convey.Convey("Metrics comparison", t, func() {
86+
for _, expect := range expected {
87+
got := readMetric(<-ch)
88+
convey.So(expect, convey.ShouldResemble, got)
89+
}
90+
})
91+
92+
// Ensure all SQL queries were executed
93+
if err := mock.ExpectationsWereMet(); err != nil {
94+
t.Errorf("there were unfulfilled exceptions: %s", err)
95+
}
96+
}

0 commit comments

Comments
 (0)