@@ -19,6 +19,8 @@ import (
19
19
"context"
20
20
"database/sql"
21
21
"fmt"
22
+ "reflect"
23
+ "sort"
22
24
"strings"
23
25
24
26
"github.com/go-kit/log"
@@ -30,16 +32,15 @@ const infoSchemaProcesslistQuery = `
30
32
SELECT
31
33
user,
32
34
SUBSTRING_INDEX(host, ':', 1) AS host,
33
- COALESCE(command,'') AS command,
34
- COALESCE(state,'') AS state,
35
- count (*) AS processes,
36
- sum (time) AS seconds
35
+ COALESCE(command, '') AS command,
36
+ COALESCE(state, '') AS state,
37
+ COUNT (*) AS processes,
38
+ SUM (time) AS seconds
37
39
FROM information_schema.processlist
38
40
WHERE ID != connection_id()
39
41
AND TIME >= %d
40
- GROUP BY user,SUBSTRING_INDEX(host, ':', 1),command,state
41
- ORDER BY null
42
- `
42
+ GROUP BY user, SUBSTRING_INDEX(host, ':', 1), command, state
43
+ `
43
44
44
45
// Tunable flags.
45
46
var (
@@ -60,104 +61,23 @@ var (
60
61
// Metric descriptors.
61
62
var (
62
63
processlistCountDesc = prometheus .NewDesc (
63
- prometheus .BuildFQName (namespace , informationSchema , "threads " ),
64
- "The number of threads (connections) split by current state." ,
65
- []string {"state" }, nil )
64
+ prometheus .BuildFQName (namespace , informationSchema , "processlist_threads " ),
65
+ "The number of threads split by current state." ,
66
+ []string {"command" , " state" }, nil )
66
67
processlistTimeDesc = prometheus .NewDesc (
67
- prometheus .BuildFQName (namespace , informationSchema , "threads_seconds " ),
68
- "The number of seconds threads (connections) have used split by current state." ,
69
- []string {"state" }, nil )
68
+ prometheus .BuildFQName (namespace , informationSchema , "processlist_seconds " ),
69
+ "The number of seconds threads have used split by current state." ,
70
+ []string {"command" , " state" }, nil )
70
71
processesByUserDesc = prometheus .NewDesc (
71
- prometheus .BuildFQName (namespace , informationSchema , "processes_by_user " ),
72
+ prometheus .BuildFQName (namespace , informationSchema , "processlist_processes_by_user " ),
72
73
"The number of processes by user." ,
73
74
[]string {"mysql_user" }, nil )
74
75
processesByHostDesc = prometheus .NewDesc (
75
- prometheus .BuildFQName (namespace , informationSchema , "processes_by_host " ),
76
+ prometheus .BuildFQName (namespace , informationSchema , "processlist_processes_by_host " ),
76
77
"The number of processes by host." ,
77
78
[]string {"client_host" }, nil )
78
79
)
79
80
80
- // whitelist for connection/process states in SHOW PROCESSLIST
81
- // tokudb uses the state column for "Queried about _______ rows"
82
- var (
83
- // TODO: might need some more keys for other MySQL versions or other storage engines
84
- // see https://dev.mysql.com/doc/refman/5.7/en/general-thread-states.html
85
- threadStateCounterMap = map [string ]uint32 {
86
- "after create" : uint32 (0 ),
87
- "altering table" : uint32 (0 ),
88
- "analyzing" : uint32 (0 ),
89
- "checking permissions" : uint32 (0 ),
90
- "checking table" : uint32 (0 ),
91
- "cleaning up" : uint32 (0 ),
92
- "closing tables" : uint32 (0 ),
93
- "converting heap to myisam" : uint32 (0 ),
94
- "copying to tmp table" : uint32 (0 ),
95
- "creating sort index" : uint32 (0 ),
96
- "creating table" : uint32 (0 ),
97
- "creating tmp table" : uint32 (0 ),
98
- "deleting" : uint32 (0 ),
99
- "executing" : uint32 (0 ),
100
- "execution of init_command" : uint32 (0 ),
101
- "end" : uint32 (0 ),
102
- "freeing items" : uint32 (0 ),
103
- "flushing tables" : uint32 (0 ),
104
- "fulltext initialization" : uint32 (0 ),
105
- "idle" : uint32 (0 ),
106
- "init" : uint32 (0 ),
107
- "killed" : uint32 (0 ),
108
- "waiting for lock" : uint32 (0 ),
109
- "logging slow query" : uint32 (0 ),
110
- "login" : uint32 (0 ),
111
- "manage keys" : uint32 (0 ),
112
- "opening tables" : uint32 (0 ),
113
- "optimizing" : uint32 (0 ),
114
- "preparing" : uint32 (0 ),
115
- "reading from net" : uint32 (0 ),
116
- "removing duplicates" : uint32 (0 ),
117
- "removing tmp table" : uint32 (0 ),
118
- "reopen tables" : uint32 (0 ),
119
- "repair by sorting" : uint32 (0 ),
120
- "repair done" : uint32 (0 ),
121
- "repair with keycache" : uint32 (0 ),
122
- "replication master" : uint32 (0 ),
123
- "rolling back" : uint32 (0 ),
124
- "searching rows for update" : uint32 (0 ),
125
- "sending data" : uint32 (0 ),
126
- "sorting for group" : uint32 (0 ),
127
- "sorting for order" : uint32 (0 ),
128
- "sorting index" : uint32 (0 ),
129
- "sorting result" : uint32 (0 ),
130
- "statistics" : uint32 (0 ),
131
- "updating" : uint32 (0 ),
132
- "waiting for tables" : uint32 (0 ),
133
- "waiting for table flush" : uint32 (0 ),
134
- "waiting on cond" : uint32 (0 ),
135
- "writing to net" : uint32 (0 ),
136
- "other" : uint32 (0 ),
137
- }
138
- threadStateMapping = map [string ]string {
139
- "user sleep" : "idle" ,
140
- "creating index" : "altering table" ,
141
- "committing alter table to storage engine" : "altering table" ,
142
- "discard or import tablespace" : "altering table" ,
143
- "rename" : "altering table" ,
144
- "setup" : "altering table" ,
145
- "renaming result table" : "altering table" ,
146
- "preparing for alter table" : "altering table" ,
147
- "copying to group table" : "copying to tmp table" ,
148
- "copy to tmp table" : "copying to tmp table" ,
149
- "query end" : "end" ,
150
- "update" : "updating" ,
151
- "updating main table" : "updating" ,
152
- "updating reference tables" : "updating" ,
153
- "system lock" : "waiting for lock" ,
154
- "user lock" : "waiting for lock" ,
155
- "table lock" : "waiting for lock" ,
156
- "deleting from main table" : "deleting" ,
157
- "deleting from reference tables" : "deleting" ,
158
- }
159
- )
160
-
161
81
// ScrapeProcesslist collects from `information_schema.processlist`.
162
82
type ScrapeProcesslist struct {}
163
83
@@ -189,83 +109,102 @@ func (ScrapeProcesslist) Scrape(ctx context.Context, db *sql.DB, ch chan<- prome
189
109
defer processlistRows .Close ()
190
110
191
111
var (
192
- user string
193
- host string
194
- command string
195
- state string
196
- processes uint32
197
- time uint32
112
+ user string
113
+ host string
114
+ command string
115
+ state string
116
+ count uint32
117
+ time uint32
198
118
)
199
- stateCounts := make (map [string ]uint32 , len (threadStateCounterMap ))
200
- stateTime := make (map [string ]uint32 , len (threadStateCounterMap ))
201
- hostCount := make (map [string ]uint32 )
202
- userCount := make (map [string ]uint32 )
203
- for k , v := range threadStateCounterMap {
204
- stateCounts [k ] = v
205
- stateTime [k ] = v
206
- }
119
+ // Define maps
120
+ stateCounts := make (map [string ]map [string ]uint32 )
121
+ stateTime := make (map [string ]map [string ]uint32 )
122
+ stateHostCounts := make (map [string ]uint32 )
123
+ stateUserCounts := make (map [string ]uint32 )
207
124
208
125
for processlistRows .Next () {
209
- err = processlistRows .Scan (& user , & host , & command , & state , & processes , & time )
126
+ err = processlistRows .Scan (& user , & host , & command , & state , & count , & time )
210
127
if err != nil {
211
128
return err
212
129
}
213
- realState := deriveThreadState (command , state )
214
- stateCounts [realState ] += processes
215
- stateTime [realState ] += time
216
- hostCount [host ] = hostCount [host ] + processes
217
- userCount [user ] = userCount [user ] + processes
218
- }
130
+ command = sanitizeState (command )
131
+ state = sanitizeState (state )
132
+ if host == "" {
133
+ host = "unknown"
134
+ }
219
135
220
- if * processesByHostFlag {
221
- for host , processes := range hostCount {
222
- ch <- prometheus .MustNewConstMetric (processesByHostDesc , prometheus .GaugeValue , float64 (processes ), host )
136
+ // Init maps
137
+ if _ , ok := stateCounts [command ]; ! ok {
138
+ stateCounts [command ] = make (map [string ]uint32 )
139
+ stateTime [command ] = make (map [string ]uint32 )
140
+ }
141
+ if _ , ok := stateCounts [command ][state ]; ! ok {
142
+ stateCounts [command ][state ] = 0
143
+ stateTime [command ][state ] = 0
144
+ }
145
+ if _ , ok := stateHostCounts [host ]; ! ok {
146
+ stateHostCounts [host ] = 0
223
147
}
148
+ if _ , ok := stateUserCounts [user ]; ! ok {
149
+ stateUserCounts [user ] = 0
150
+ }
151
+
152
+ stateCounts [command ][state ] += count
153
+ stateTime [command ][state ] += time
154
+ stateHostCounts [host ] += count
155
+ stateUserCounts [user ] += count
224
156
}
225
157
226
- if * processesByUserFlag {
227
- for user , processes := range userCount {
228
- ch <- prometheus .MustNewConstMetric (processesByUserDesc , prometheus .GaugeValue , float64 (processes ), user )
158
+ for _ , command := range sortedMapKeys (stateCounts ) {
159
+ for _ , state := range sortedMapKeys (stateCounts [command ]) {
160
+ ch <- prometheus .MustNewConstMetric (processlistCountDesc , prometheus .GaugeValue , float64 (stateCounts [command ][state ]), command , state )
161
+ ch <- prometheus .MustNewConstMetric (processlistTimeDesc , prometheus .GaugeValue , float64 (stateTime [command ][state ]), command , state )
229
162
}
230
163
}
231
164
232
- for state , processes := range stateCounts {
233
- ch <- prometheus .MustNewConstMetric (processlistCountDesc , prometheus .GaugeValue , float64 (processes ), state )
165
+ if * processesByHostFlag {
166
+ for _ , host := range sortedMapKeys (stateHostCounts ) {
167
+ ch <- prometheus .MustNewConstMetric (processesByHostDesc , prometheus .GaugeValue , float64 (stateHostCounts [host ]), host )
168
+ }
234
169
}
235
- for state , time := range stateTime {
236
- ch <- prometheus .MustNewConstMetric (processlistTimeDesc , prometheus .GaugeValue , float64 (time ), state )
170
+ if * processesByUserFlag {
171
+ for _ , user := range sortedMapKeys (stateUserCounts ) {
172
+ ch <- prometheus .MustNewConstMetric (processesByUserDesc , prometheus .GaugeValue , float64 (stateUserCounts [user ]), user )
173
+ }
237
174
}
238
175
239
176
return nil
240
177
}
241
178
242
- func deriveThreadState (command string , state string ) string {
243
- var normCmd = strings .Replace (strings .ToLower (command ), "_" , " " , - 1 )
244
- var normState = strings .Replace (strings .ToLower (state ), "_" , " " , - 1 )
245
- // check if it's already a valid state
246
- _ , knownState := threadStateCounterMap [normState ]
247
- if knownState {
248
- return normState
249
- }
250
- // check if plain mapping applies
251
- mappedState , canMap := threadStateMapping [normState ]
252
- if canMap {
253
- return mappedState
254
- }
255
- // check special waiting for XYZ lock
256
- if strings .Contains (normState , "waiting for" ) && strings .Contains (normState , "lock" ) {
257
- return "waiting for lock"
179
+ func sortedMapKeys (m interface {}) []string {
180
+ v := reflect .ValueOf (m )
181
+ keys := make ([]string , 0 , len (v .MapKeys ()))
182
+ for _ , key := range v .MapKeys () {
183
+ keys = append (keys , key .String ())
258
184
}
259
- if normCmd == "sleep" && normState == "" {
260
- return "idle"
185
+ sort .Strings (keys )
186
+ return keys
187
+ }
188
+
189
+ func sanitizeState (state string ) string {
190
+ if state == "" {
191
+ state = "unknown"
261
192
}
262
- if normCmd == "query" {
263
- return "executing"
193
+ state = strings .ToLower (state )
194
+ replacements := map [string ]string {
195
+ ";" : "" ,
196
+ "," : "" ,
197
+ ":" : "" ,
198
+ "." : "" ,
199
+ "(" : "" ,
200
+ ")" : "" ,
201
+ " " : "_" ,
202
+ "-" : "_" ,
264
203
}
265
- if normCmd == "binlog dump" {
266
- return "replication master"
204
+ for r := range replacements {
205
+ state = strings . Replace ( state , r , replacements [ r ], - 1 )
267
206
}
268
- return "other"
207
+ return state
269
208
}
270
209
271
210
// check interface
0 commit comments