master
go 134 lines 3.13 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package mysql
4
5 import (
6 "context"
7 "errors"
8 "strings"
9 )
10
11 const (
12 queryShowGlobalStatus = "SHOW GLOBAL STATUS;"
13 queryShowGlobalStatusProbe = `SHOW GLOBAL STATUS
14 WHERE VARIABLE_NAME IN ('Threads_connected', 'wsrep_received', 'qcache_hits');`
15 )
16
17 func (c *Collector) collectGlobalStatus(ctx context.Context, state *collectRunState) error {
18 // MariaDB: https://mariadb.com/kb/en/server-status-variables/
19 // MySQL: https://dev.mysql.com/doc/refman/8.0/en/server-status-variable-reference.html
20 q := queryShowGlobalStatus
21 c.Debugf("executing query: '%s'", q)
22
23 var name string
24 _, err := c.collectQuery(ctx, q, func(column, value string, _ bool) {
25 switch column {
26 case "Variable_name":
27 name = value
28 case "Value":
29 metricName := strings.ToLower(name)
30
31 if isGaleraMetric(metricName) && !c.galeraDetected {
32 return
33 }
34 if isQCacheMetric(metricName) && !c.qcacheDetected {
35 return
36 }
37 if isBinLogMetric(metricName) && c.varLogBin == "OFF" {
38 return
39 }
40 if isMyISAMKeyMetric(metricName) && strings.Contains(c.varDisabledStorageEngine, "MyISAM") {
41 return
42 }
43
44 switch metricName {
45 case "wsrep_connected":
46 parsedValue := parseInt(convertWsrepConnected(value))
47 c.mx.set("wsrep_connected", parsedValue)
48 case "wsrep_ready":
49 parsedValue := parseInt(convertWsrepReady(value))
50 c.mx.set("wsrep_ready", parsedValue)
51 case "wsrep_local_state":
52 c.mx.setWsrepLocalState(value)
53 case "wsrep_cluster_status":
54 c.mx.setWsrepClusterStatus(value)
55 default:
56 parsedValue := parseInt(value)
57 c.mx.set(metricName, parsedValue)
58
59 switch metricName {
60 case "connections":
61 state.connections = parsedValue
62 case "threads_created":
63 state.threadsCreated = parsedValue
64 }
65 }
66 }
67 })
68 return err
69 }
70
71 func (c *Collector) probeGlobalStatus(ctx context.Context) error {
72 q := queryShowGlobalStatusProbe
73 c.Debugf("executing query: '%s'", q)
74
75 var hasRows bool
76 _, err := c.collectQuery(ctx, q, func(column, value string, lineEnd bool) {
77 if column == "Variable_name" {
78 switch value {
79 case "wsrep_received":
80 c.galeraDetected = true
81 case "qcache_hits":
82 c.qcacheDetected = true
83 }
84 }
85 hasRows = hasRows || lineEnd
86 })
87 if err != nil {
88 return err
89 }
90 if !hasRows {
91 return errors.New("global status probe returned no rows")
92 }
93 return nil
94 }
95
96 func isGaleraMetric(name string) bool {
97 return strings.HasPrefix(name, "wsrep_")
98 }
99
100 func isBinLogMetric(name string) bool {
101 return strings.HasPrefix(name, "binlog_")
102 }
103
104 func isMyISAMKeyMetric(name string) bool {
105 return strings.HasPrefix(name, "key_")
106 }
107
108 func isQCacheMetric(name string) bool {
109 return strings.HasPrefix(name, "qcache_")
110 }
111
112 func convertWsrepConnected(val string) string {
113 // https://www.percona.com/doc/percona-xtradb-cluster/LATEST/wsrep-status-index.html#wsrep_connected
114 switch val {
115 case "OFF":
116 return "0"
117 case "ON":
118 return "1"
119 default:
120 return "-1"
121 }
122 }
123
124 func convertWsrepReady(val string) string {
125 // https://www.percona.com/doc/percona-xtradb-cluster/LATEST/wsrep-status-index.html#wsrep_ready
126 switch val {
127 case "OFF":
128 return "0"
129 case "ON":
130 return "1"
131 default:
132 return "-1"
133 }
134 }