@berthojoris/mcp-mysql-server 1.10.3 → 1.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -7
- package/DOCUMENTATIONS.md +2 -2
- package/dist/index.d.ts +0 -99
- package/dist/mcp-server.js +0 -21
- package/dist/tools/backupRestoreTools.d.ts +1 -6
- package/dist/tools/backupRestoreTools.js +99 -97
- package/dist/tools/constraintTools.d.ts +4 -11
- package/dist/tools/constraintTools.js +114 -115
- package/dist/tools/crudTools.d.ts +2 -6
- package/dist/tools/crudTools.js +186 -189
- package/dist/tools/dataExportTools.d.ts +0 -7
- package/dist/tools/dataExportTools.js +0 -15
- package/dist/tools/databaseTools.d.ts +1 -4
- package/dist/tools/databaseTools.js +29 -33
- package/dist/tools/ddlTools.d.ts +1 -5
- package/dist/tools/ddlTools.js +68 -53
- package/dist/tools/functionTools.d.ts +3 -9
- package/dist/tools/functionTools.js +111 -104
- package/dist/tools/indexTools.d.ts +3 -8
- package/dist/tools/indexTools.js +99 -95
- package/dist/tools/maintenanceTools.d.ts +2 -10
- package/dist/tools/maintenanceTools.js +66 -80
- package/dist/tools/migrationTools.d.ts +0 -5
- package/dist/tools/migrationTools.js +56 -24
- package/dist/tools/performanceTools.d.ts +1 -11
- package/dist/tools/performanceTools.js +278 -267
- package/dist/tools/processTools.d.ts +4 -13
- package/dist/tools/processTools.js +78 -80
- package/dist/tools/queryTools.d.ts +0 -2
- package/dist/tools/queryTools.js +0 -4
- package/dist/tools/schemaVersioningTools.d.ts +0 -9
- package/dist/tools/schemaVersioningTools.js +167 -166
- package/dist/tools/storedProcedureTools.d.ts +2 -4
- package/dist/tools/storedProcedureTools.js +143 -134
- package/dist/tools/transactionTools.d.ts +2 -3
- package/dist/tools/transactionTools.js +28 -29
- package/dist/tools/triggerTools.d.ts +3 -8
- package/dist/tools/triggerTools.js +98 -85
- package/dist/tools/utilityTools.d.ts +0 -1
- package/dist/tools/utilityTools.js +0 -2
- package/dist/tools/viewTools.d.ts +7 -13
- package/dist/tools/viewTools.js +100 -93
- package/package.json +1 -1
|
@@ -17,28 +17,28 @@ class PerformanceTools {
|
|
|
17
17
|
try {
|
|
18
18
|
const metrics = {};
|
|
19
19
|
// Get query performance metrics
|
|
20
|
-
const perfQuery = `
|
|
21
|
-
SELECT
|
|
22
|
-
SUM(TIMER_WAIT) / 1000000000000 as total_execution_time_sec,
|
|
23
|
-
SUM(LOCK_TIME) / 1000000000000 as total_lock_time_sec,
|
|
24
|
-
SUM(ROWS_EXAMINED) as total_rows_examined,
|
|
25
|
-
SUM(ROWS_SENT) as total_rows_sent,
|
|
26
|
-
SUM(ROWS_AFFECTED) as total_rows_affected,
|
|
27
|
-
SUM(SELECT_FULL_JOIN) as full_table_scans,
|
|
28
|
-
SUM(NO_INDEX_USED) as queries_without_indexes,
|
|
29
|
-
SUM(NO_GOOD_INDEX_USED) as queries_with_bad_indexes,
|
|
30
|
-
COUNT(*) as total_queries
|
|
31
|
-
FROM performance_schema.events_statements_summary_global_by_event_name
|
|
32
|
-
WHERE EVENT_NAME LIKE 'statement/sql/%'
|
|
20
|
+
const perfQuery = `
|
|
21
|
+
SELECT
|
|
22
|
+
SUM(TIMER_WAIT) / 1000000000000 as total_execution_time_sec,
|
|
23
|
+
SUM(LOCK_TIME) / 1000000000000 as total_lock_time_sec,
|
|
24
|
+
SUM(ROWS_EXAMINED) as total_rows_examined,
|
|
25
|
+
SUM(ROWS_SENT) as total_rows_sent,
|
|
26
|
+
SUM(ROWS_AFFECTED) as total_rows_affected,
|
|
27
|
+
SUM(SELECT_FULL_JOIN) as full_table_scans,
|
|
28
|
+
SUM(NO_INDEX_USED) as queries_without_indexes,
|
|
29
|
+
SUM(NO_GOOD_INDEX_USED) as queries_with_bad_indexes,
|
|
30
|
+
COUNT(*) as total_queries
|
|
31
|
+
FROM performance_schema.events_statements_summary_global_by_event_name
|
|
32
|
+
WHERE EVENT_NAME LIKE 'statement/sql/%'
|
|
33
33
|
`;
|
|
34
34
|
const perfResult = await this.db.query(perfQuery);
|
|
35
35
|
metrics.query_performance = perfResult[0] || {};
|
|
36
36
|
// Get connection metrics
|
|
37
|
-
const connQuery = `
|
|
38
|
-
SHOW GLOBAL STATUS WHERE Variable_name IN (
|
|
39
|
-
'Threads_connected', 'Threads_running', 'Threads_created',
|
|
40
|
-
'Connections', 'Max_used_connections', 'Aborted_connects', 'Aborted_clients'
|
|
41
|
-
)
|
|
37
|
+
const connQuery = `
|
|
38
|
+
SHOW GLOBAL STATUS WHERE Variable_name IN (
|
|
39
|
+
'Threads_connected', 'Threads_running', 'Threads_created',
|
|
40
|
+
'Connections', 'Max_used_connections', 'Aborted_connects', 'Aborted_clients'
|
|
41
|
+
)
|
|
42
42
|
`;
|
|
43
43
|
const connResult = await this.db.query(connQuery);
|
|
44
44
|
metrics.connections = {};
|
|
@@ -46,11 +46,11 @@ class PerformanceTools {
|
|
|
46
46
|
metrics.connections[row.Variable_name.toLowerCase()] = row.Value;
|
|
47
47
|
}
|
|
48
48
|
// Get table cache metrics
|
|
49
|
-
const cacheQuery = `
|
|
50
|
-
SHOW GLOBAL STATUS WHERE Variable_name IN (
|
|
51
|
-
'Open_tables', 'Opened_tables', 'Table_open_cache_hits',
|
|
52
|
-
'Table_open_cache_misses', 'Table_open_cache_overflows'
|
|
53
|
-
)
|
|
49
|
+
const cacheQuery = `
|
|
50
|
+
SHOW GLOBAL STATUS WHERE Variable_name IN (
|
|
51
|
+
'Open_tables', 'Opened_tables', 'Table_open_cache_hits',
|
|
52
|
+
'Table_open_cache_misses', 'Table_open_cache_overflows'
|
|
53
|
+
)
|
|
54
54
|
`;
|
|
55
55
|
const cacheResult = await this.db.query(cacheQuery);
|
|
56
56
|
metrics.table_cache = {};
|
|
@@ -58,15 +58,15 @@ class PerformanceTools {
|
|
|
58
58
|
metrics.table_cache[row.Variable_name.toLowerCase()] = row.Value;
|
|
59
59
|
}
|
|
60
60
|
// Get InnoDB metrics
|
|
61
|
-
const innodbQuery = `
|
|
62
|
-
SHOW GLOBAL STATUS WHERE Variable_name LIKE 'Innodb_%'
|
|
63
|
-
AND Variable_name IN (
|
|
64
|
-
'Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests',
|
|
65
|
-
'Innodb_buffer_pool_pages_total', 'Innodb_buffer_pool_pages_free',
|
|
66
|
-
'Innodb_buffer_pool_pages_data', 'Innodb_buffer_pool_pages_dirty',
|
|
67
|
-
'Innodb_row_lock_waits', 'Innodb_row_lock_time', 'Innodb_rows_read',
|
|
68
|
-
'Innodb_rows_inserted', 'Innodb_rows_updated', 'Innodb_rows_deleted'
|
|
69
|
-
)
|
|
61
|
+
const innodbQuery = `
|
|
62
|
+
SHOW GLOBAL STATUS WHERE Variable_name LIKE 'Innodb_%'
|
|
63
|
+
AND Variable_name IN (
|
|
64
|
+
'Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests',
|
|
65
|
+
'Innodb_buffer_pool_pages_total', 'Innodb_buffer_pool_pages_free',
|
|
66
|
+
'Innodb_buffer_pool_pages_data', 'Innodb_buffer_pool_pages_dirty',
|
|
67
|
+
'Innodb_row_lock_waits', 'Innodb_row_lock_time', 'Innodb_rows_read',
|
|
68
|
+
'Innodb_rows_inserted', 'Innodb_rows_updated', 'Innodb_rows_deleted'
|
|
69
|
+
)
|
|
70
70
|
`;
|
|
71
71
|
const innodbResult = await this.db.query(innodbQuery);
|
|
72
72
|
metrics.innodb = {};
|
|
@@ -74,14 +74,16 @@ class PerformanceTools {
|
|
|
74
74
|
metrics.innodb[row.Variable_name.toLowerCase()] = row.Value;
|
|
75
75
|
}
|
|
76
76
|
// Calculate buffer pool hit ratio
|
|
77
|
-
if (metrics.innodb.innodb_buffer_pool_read_requests &&
|
|
77
|
+
if (metrics.innodb.innodb_buffer_pool_read_requests &&
|
|
78
|
+
metrics.innodb.innodb_buffer_pool_reads) {
|
|
78
79
|
const requests = parseFloat(metrics.innodb.innodb_buffer_pool_read_requests);
|
|
79
80
|
const reads = parseFloat(metrics.innodb.innodb_buffer_pool_reads);
|
|
80
|
-
metrics.innodb.buffer_pool_hit_ratio =
|
|
81
|
+
metrics.innodb.buffer_pool_hit_ratio =
|
|
82
|
+
(((requests - reads) / requests) * 100).toFixed(2) + "%";
|
|
81
83
|
}
|
|
82
84
|
// Get slow query metrics
|
|
83
|
-
const slowQuery = `
|
|
84
|
-
SHOW GLOBAL STATUS WHERE Variable_name IN ('Slow_queries', 'Questions')
|
|
85
|
+
const slowQuery = `
|
|
86
|
+
SHOW GLOBAL STATUS WHERE Variable_name IN ('Slow_queries', 'Questions')
|
|
85
87
|
`;
|
|
86
88
|
const slowResult = await this.db.query(slowQuery);
|
|
87
89
|
metrics.slow_queries = {};
|
|
@@ -92,19 +94,18 @@ class PerformanceTools {
|
|
|
92
94
|
if (metrics.slow_queries.questions && metrics.slow_queries.slow_queries) {
|
|
93
95
|
const total = parseFloat(metrics.slow_queries.questions);
|
|
94
96
|
const slow = parseFloat(metrics.slow_queries.slow_queries);
|
|
95
|
-
metrics.slow_queries.slow_query_percentage =
|
|
97
|
+
metrics.slow_queries.slow_query_percentage =
|
|
98
|
+
((slow / total) * 100).toFixed(4) + "%";
|
|
96
99
|
}
|
|
97
100
|
return {
|
|
98
|
-
status:
|
|
101
|
+
status: "success",
|
|
99
102
|
data: metrics,
|
|
100
|
-
queryLog: this.db.getFormattedQueryLogs(5)
|
|
101
103
|
};
|
|
102
104
|
}
|
|
103
105
|
catch (error) {
|
|
104
106
|
return {
|
|
105
|
-
status:
|
|
107
|
+
status: "error",
|
|
106
108
|
error: error.message,
|
|
107
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
108
109
|
};
|
|
109
110
|
}
|
|
110
111
|
}
|
|
@@ -115,38 +116,39 @@ class PerformanceTools {
|
|
|
115
116
|
try {
|
|
116
117
|
const limit = params?.limit || 10;
|
|
117
118
|
if (!Number.isInteger(limit) || limit <= 0 || limit > 100) {
|
|
118
|
-
return {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
ROUND(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
119
|
+
return {
|
|
120
|
+
status: "error",
|
|
121
|
+
error: "Limit must be a positive integer between 1 and 100",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const query = `
|
|
125
|
+
SELECT
|
|
126
|
+
DIGEST_TEXT as query_pattern,
|
|
127
|
+
COUNT_STAR as execution_count,
|
|
128
|
+
ROUND(AVG_TIMER_WAIT / 1000000000000, 6) as avg_execution_time_sec,
|
|
129
|
+
ROUND(MAX_TIMER_WAIT / 1000000000000, 6) as max_execution_time_sec,
|
|
130
|
+
ROUND(SUM_TIMER_WAIT / 1000000000000, 6) as total_execution_time_sec,
|
|
131
|
+
ROUND(SUM_LOCK_TIME / 1000000000000, 6) as total_lock_time_sec,
|
|
132
|
+
SUM_ROWS_EXAMINED as rows_examined,
|
|
133
|
+
SUM_ROWS_SENT as rows_sent,
|
|
134
|
+
SUM_ROWS_AFFECTED as rows_affected,
|
|
135
|
+
FIRST_SEEN,
|
|
136
|
+
LAST_SEEN
|
|
137
|
+
FROM performance_schema.events_statements_summary_by_digest
|
|
138
|
+
WHERE DIGEST_TEXT IS NOT NULL
|
|
139
|
+
ORDER BY SUM_TIMER_WAIT DESC
|
|
140
|
+
LIMIT ${limit}
|
|
137
141
|
`;
|
|
138
142
|
const results = await this.db.query(query);
|
|
139
143
|
return {
|
|
140
|
-
status:
|
|
144
|
+
status: "success",
|
|
141
145
|
data: results,
|
|
142
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
143
146
|
};
|
|
144
147
|
}
|
|
145
148
|
catch (error) {
|
|
146
149
|
return {
|
|
147
|
-
status:
|
|
150
|
+
status: "error",
|
|
148
151
|
error: error.message,
|
|
149
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
150
152
|
};
|
|
151
153
|
}
|
|
152
154
|
}
|
|
@@ -157,36 +159,37 @@ class PerformanceTools {
|
|
|
157
159
|
try {
|
|
158
160
|
const limit = params?.limit || 10;
|
|
159
161
|
if (!Number.isInteger(limit) || limit <= 0 || limit > 100) {
|
|
160
|
-
return {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
162
|
+
return {
|
|
163
|
+
status: "error",
|
|
164
|
+
error: "Limit must be a positive integer between 1 and 100",
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const query = `
|
|
168
|
+
SELECT
|
|
169
|
+
DIGEST_TEXT as query_pattern,
|
|
170
|
+
COUNT_STAR as execution_count,
|
|
171
|
+
ROUND(AVG_TIMER_WAIT / 1000000000000, 6) as avg_execution_time_sec,
|
|
172
|
+
ROUND(MAX_TIMER_WAIT / 1000000000000, 6) as max_execution_time_sec,
|
|
173
|
+
ROUND(SUM_TIMER_WAIT / 1000000000000, 6) as total_execution_time_sec,
|
|
174
|
+
SUM_ROWS_EXAMINED as rows_examined,
|
|
175
|
+
SUM_ROWS_SENT as rows_sent,
|
|
176
|
+
FIRST_SEEN,
|
|
177
|
+
LAST_SEEN
|
|
178
|
+
FROM performance_schema.events_statements_summary_by_digest
|
|
179
|
+
WHERE DIGEST_TEXT IS NOT NULL
|
|
180
|
+
ORDER BY COUNT_STAR DESC
|
|
181
|
+
LIMIT ${limit}
|
|
177
182
|
`;
|
|
178
183
|
const results = await this.db.query(query);
|
|
179
184
|
return {
|
|
180
|
-
status:
|
|
185
|
+
status: "success",
|
|
181
186
|
data: results,
|
|
182
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
183
187
|
};
|
|
184
188
|
}
|
|
185
189
|
catch (error) {
|
|
186
190
|
return {
|
|
187
|
-
status:
|
|
191
|
+
status: "error",
|
|
188
192
|
error: error.message,
|
|
189
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
190
193
|
};
|
|
191
194
|
}
|
|
192
195
|
}
|
|
@@ -198,42 +201,46 @@ class PerformanceTools {
|
|
|
198
201
|
const limit = params?.limit || 10;
|
|
199
202
|
const thresholdSec = params?.threshold_seconds || 1;
|
|
200
203
|
if (!Number.isInteger(limit) || limit <= 0 || limit > 100) {
|
|
201
|
-
return {
|
|
204
|
+
return {
|
|
205
|
+
status: "error",
|
|
206
|
+
error: "Limit must be a positive integer between 1 and 100",
|
|
207
|
+
};
|
|
202
208
|
}
|
|
203
|
-
if (typeof thresholdSec !==
|
|
204
|
-
return {
|
|
209
|
+
if (typeof thresholdSec !== "number" || thresholdSec <= 0) {
|
|
210
|
+
return {
|
|
211
|
+
status: "error",
|
|
212
|
+
error: "Threshold must be a positive number",
|
|
213
|
+
};
|
|
205
214
|
}
|
|
206
215
|
const thresholdPico = thresholdSec * 1000000000000;
|
|
207
|
-
const query = `
|
|
208
|
-
SELECT
|
|
209
|
-
DIGEST_TEXT as query_pattern,
|
|
210
|
-
COUNT_STAR as execution_count,
|
|
211
|
-
ROUND(AVG_TIMER_WAIT / 1000000000000, 6) as avg_execution_time_sec,
|
|
212
|
-
ROUND(MAX_TIMER_WAIT / 1000000000000, 6) as max_execution_time_sec,
|
|
213
|
-
ROUND(SUM_TIMER_WAIT / 1000000000000, 6) as total_execution_time_sec,
|
|
214
|
-
ROUND(SUM_LOCK_TIME / 1000000000000, 6) as total_lock_time_sec,
|
|
215
|
-
SUM_ROWS_EXAMINED as rows_examined,
|
|
216
|
-
SUM_ROWS_SENT as rows_sent,
|
|
217
|
-
SUM_NO_INDEX_USED as no_index_used_count,
|
|
218
|
-
FIRST_SEEN,
|
|
219
|
-
LAST_SEEN
|
|
220
|
-
FROM performance_schema.events_statements_summary_by_digest
|
|
221
|
-
WHERE DIGEST_TEXT IS NOT NULL AND AVG_TIMER_WAIT > ${thresholdPico}
|
|
222
|
-
ORDER BY AVG_TIMER_WAIT DESC
|
|
223
|
-
LIMIT ${limit}
|
|
216
|
+
const query = `
|
|
217
|
+
SELECT
|
|
218
|
+
DIGEST_TEXT as query_pattern,
|
|
219
|
+
COUNT_STAR as execution_count,
|
|
220
|
+
ROUND(AVG_TIMER_WAIT / 1000000000000, 6) as avg_execution_time_sec,
|
|
221
|
+
ROUND(MAX_TIMER_WAIT / 1000000000000, 6) as max_execution_time_sec,
|
|
222
|
+
ROUND(SUM_TIMER_WAIT / 1000000000000, 6) as total_execution_time_sec,
|
|
223
|
+
ROUND(SUM_LOCK_TIME / 1000000000000, 6) as total_lock_time_sec,
|
|
224
|
+
SUM_ROWS_EXAMINED as rows_examined,
|
|
225
|
+
SUM_ROWS_SENT as rows_sent,
|
|
226
|
+
SUM_NO_INDEX_USED as no_index_used_count,
|
|
227
|
+
FIRST_SEEN,
|
|
228
|
+
LAST_SEEN
|
|
229
|
+
FROM performance_schema.events_statements_summary_by_digest
|
|
230
|
+
WHERE DIGEST_TEXT IS NOT NULL AND AVG_TIMER_WAIT > ${thresholdPico}
|
|
231
|
+
ORDER BY AVG_TIMER_WAIT DESC
|
|
232
|
+
LIMIT ${limit}
|
|
224
233
|
`;
|
|
225
234
|
const results = await this.db.query(query);
|
|
226
235
|
return {
|
|
227
|
-
status:
|
|
236
|
+
status: "success",
|
|
228
237
|
data: results,
|
|
229
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
230
238
|
};
|
|
231
239
|
}
|
|
232
240
|
catch (error) {
|
|
233
241
|
return {
|
|
234
|
-
status:
|
|
242
|
+
status: "error",
|
|
235
243
|
error: error.message,
|
|
236
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
237
244
|
};
|
|
238
245
|
}
|
|
239
246
|
}
|
|
@@ -245,23 +252,26 @@ class PerformanceTools {
|
|
|
245
252
|
const limit = params?.limit || 20;
|
|
246
253
|
const schema = params?.table_schema;
|
|
247
254
|
if (!Number.isInteger(limit) || limit <= 0 || limit > 100) {
|
|
248
|
-
return {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
255
|
+
return {
|
|
256
|
+
status: "error",
|
|
257
|
+
error: "Limit must be a positive integer between 1 and 100",
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
let query = `
|
|
261
|
+
SELECT
|
|
262
|
+
OBJECT_SCHEMA as table_schema,
|
|
263
|
+
OBJECT_NAME as table_name,
|
|
264
|
+
COUNT_READ as read_operations,
|
|
265
|
+
COUNT_WRITE as write_operations,
|
|
266
|
+
COUNT_FETCH as fetch_operations,
|
|
267
|
+
COUNT_INSERT as insert_operations,
|
|
268
|
+
COUNT_UPDATE as update_operations,
|
|
269
|
+
COUNT_DELETE as delete_operations,
|
|
270
|
+
ROUND(SUM_TIMER_READ / 1000000000000, 6) as total_read_time_sec,
|
|
271
|
+
ROUND(SUM_TIMER_WRITE / 1000000000000, 6) as total_write_time_sec,
|
|
272
|
+
ROUND(SUM_TIMER_FETCH / 1000000000000, 6) as total_fetch_time_sec
|
|
273
|
+
FROM performance_schema.table_io_waits_summary_by_table
|
|
274
|
+
WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
|
|
265
275
|
`;
|
|
266
276
|
if (schema) {
|
|
267
277
|
query += ` AND OBJECT_SCHEMA = '${schema.replace(/'/g, "''")}'`;
|
|
@@ -269,16 +279,14 @@ class PerformanceTools {
|
|
|
269
279
|
query += ` ORDER BY (COUNT_READ + COUNT_WRITE) DESC LIMIT ${limit}`;
|
|
270
280
|
const results = await this.db.query(query);
|
|
271
281
|
return {
|
|
272
|
-
status:
|
|
282
|
+
status: "success",
|
|
273
283
|
data: results,
|
|
274
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
275
284
|
};
|
|
276
285
|
}
|
|
277
286
|
catch (error) {
|
|
278
287
|
return {
|
|
279
|
-
status:
|
|
288
|
+
status: "error",
|
|
280
289
|
error: error.message,
|
|
281
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
282
290
|
};
|
|
283
291
|
}
|
|
284
292
|
}
|
|
@@ -290,23 +298,26 @@ class PerformanceTools {
|
|
|
290
298
|
const limit = params?.limit || 20;
|
|
291
299
|
const schema = params?.table_schema;
|
|
292
300
|
if (!Number.isInteger(limit) || limit <= 0 || limit > 100) {
|
|
293
|
-
return {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
301
|
+
return {
|
|
302
|
+
status: "error",
|
|
303
|
+
error: "Limit must be a positive integer between 1 and 100",
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
let query = `
|
|
307
|
+
SELECT
|
|
308
|
+
OBJECT_SCHEMA as table_schema,
|
|
309
|
+
OBJECT_NAME as table_name,
|
|
310
|
+
INDEX_NAME as index_name,
|
|
311
|
+
COUNT_STAR as usage_count,
|
|
312
|
+
COUNT_READ as read_count,
|
|
313
|
+
COUNT_WRITE as write_count,
|
|
314
|
+
COUNT_FETCH as fetch_count,
|
|
315
|
+
COUNT_INSERT as insert_count,
|
|
316
|
+
COUNT_UPDATE as update_count,
|
|
317
|
+
COUNT_DELETE as delete_count
|
|
318
|
+
FROM performance_schema.table_io_waits_summary_by_index_usage
|
|
319
|
+
WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
|
|
320
|
+
AND INDEX_NAME IS NOT NULL
|
|
310
321
|
`;
|
|
311
322
|
if (schema) {
|
|
312
323
|
query += ` AND OBJECT_SCHEMA = '${schema.replace(/'/g, "''")}'`;
|
|
@@ -314,16 +325,14 @@ class PerformanceTools {
|
|
|
314
325
|
query += ` ORDER BY COUNT_STAR DESC LIMIT ${limit}`;
|
|
315
326
|
const results = await this.db.query(query);
|
|
316
327
|
return {
|
|
317
|
-
status:
|
|
328
|
+
status: "success",
|
|
318
329
|
data: results,
|
|
319
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
320
330
|
};
|
|
321
331
|
}
|
|
322
332
|
catch (error) {
|
|
323
333
|
return {
|
|
324
|
-
status:
|
|
334
|
+
status: "error",
|
|
325
335
|
error: error.message,
|
|
326
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
327
336
|
};
|
|
328
337
|
}
|
|
329
338
|
}
|
|
@@ -333,26 +342,26 @@ class PerformanceTools {
|
|
|
333
342
|
async getUnusedIndexes(params) {
|
|
334
343
|
try {
|
|
335
344
|
const schema = params?.table_schema;
|
|
336
|
-
let query = `
|
|
337
|
-
SELECT
|
|
338
|
-
t.TABLE_SCHEMA as table_schema,
|
|
339
|
-
t.TABLE_NAME as table_name,
|
|
340
|
-
s.INDEX_NAME as index_name,
|
|
341
|
-
s.COLUMN_NAME as column_name,
|
|
342
|
-
s.SEQ_IN_INDEX as sequence_in_index,
|
|
343
|
-
s.NON_UNIQUE as is_non_unique
|
|
344
|
-
FROM information_schema.STATISTICS s
|
|
345
|
-
LEFT JOIN performance_schema.table_io_waits_summary_by_index_usage p
|
|
346
|
-
ON s.TABLE_SCHEMA = p.OBJECT_SCHEMA
|
|
347
|
-
AND s.TABLE_NAME = p.OBJECT_NAME
|
|
348
|
-
AND s.INDEX_NAME = p.INDEX_NAME
|
|
349
|
-
JOIN information_schema.TABLES t
|
|
350
|
-
ON s.TABLE_SCHEMA = t.TABLE_SCHEMA
|
|
351
|
-
AND s.TABLE_NAME = t.TABLE_NAME
|
|
352
|
-
WHERE s.TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
|
|
353
|
-
AND s.INDEX_NAME != 'PRIMARY'
|
|
354
|
-
AND (p.INDEX_NAME IS NULL OR p.COUNT_STAR = 0)
|
|
355
|
-
AND t.TABLE_TYPE = 'BASE TABLE'
|
|
345
|
+
let query = `
|
|
346
|
+
SELECT
|
|
347
|
+
t.TABLE_SCHEMA as table_schema,
|
|
348
|
+
t.TABLE_NAME as table_name,
|
|
349
|
+
s.INDEX_NAME as index_name,
|
|
350
|
+
s.COLUMN_NAME as column_name,
|
|
351
|
+
s.SEQ_IN_INDEX as sequence_in_index,
|
|
352
|
+
s.NON_UNIQUE as is_non_unique
|
|
353
|
+
FROM information_schema.STATISTICS s
|
|
354
|
+
LEFT JOIN performance_schema.table_io_waits_summary_by_index_usage p
|
|
355
|
+
ON s.TABLE_SCHEMA = p.OBJECT_SCHEMA
|
|
356
|
+
AND s.TABLE_NAME = p.OBJECT_NAME
|
|
357
|
+
AND s.INDEX_NAME = p.INDEX_NAME
|
|
358
|
+
JOIN information_schema.TABLES t
|
|
359
|
+
ON s.TABLE_SCHEMA = t.TABLE_SCHEMA
|
|
360
|
+
AND s.TABLE_NAME = t.TABLE_NAME
|
|
361
|
+
WHERE s.TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
|
|
362
|
+
AND s.INDEX_NAME != 'PRIMARY'
|
|
363
|
+
AND (p.INDEX_NAME IS NULL OR p.COUNT_STAR = 0)
|
|
364
|
+
AND t.TABLE_TYPE = 'BASE TABLE'
|
|
356
365
|
`;
|
|
357
366
|
if (schema) {
|
|
358
367
|
query += ` AND s.TABLE_SCHEMA = '${schema.replace(/'/g, "''")}'`;
|
|
@@ -360,16 +369,14 @@ class PerformanceTools {
|
|
|
360
369
|
query += ` ORDER BY s.TABLE_SCHEMA, s.TABLE_NAME, s.INDEX_NAME, s.SEQ_IN_INDEX`;
|
|
361
370
|
const results = await this.db.query(query);
|
|
362
371
|
return {
|
|
363
|
-
status:
|
|
372
|
+
status: "success",
|
|
364
373
|
data: results,
|
|
365
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
366
374
|
};
|
|
367
375
|
}
|
|
368
376
|
catch (error) {
|
|
369
377
|
return {
|
|
370
|
-
status:
|
|
378
|
+
status: "error",
|
|
371
379
|
error: error.message,
|
|
372
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
373
380
|
};
|
|
374
381
|
}
|
|
375
382
|
}
|
|
@@ -378,25 +385,25 @@ class PerformanceTools {
|
|
|
378
385
|
*/
|
|
379
386
|
async getConnectionPoolStats() {
|
|
380
387
|
try {
|
|
381
|
-
const statusQuery = `
|
|
382
|
-
SHOW GLOBAL STATUS WHERE Variable_name IN (
|
|
383
|
-
'Threads_connected', 'Threads_running', 'Threads_created', 'Threads_cached',
|
|
384
|
-
'Connections', 'Max_used_connections', 'Max_used_connections_time',
|
|
385
|
-
'Aborted_connects', 'Aborted_clients', 'Connection_errors_max_connections'
|
|
386
|
-
)
|
|
388
|
+
const statusQuery = `
|
|
389
|
+
SHOW GLOBAL STATUS WHERE Variable_name IN (
|
|
390
|
+
'Threads_connected', 'Threads_running', 'Threads_created', 'Threads_cached',
|
|
391
|
+
'Connections', 'Max_used_connections', 'Max_used_connections_time',
|
|
392
|
+
'Aborted_connects', 'Aborted_clients', 'Connection_errors_max_connections'
|
|
393
|
+
)
|
|
387
394
|
`;
|
|
388
|
-
const variablesQuery = `
|
|
389
|
-
SHOW GLOBAL VARIABLES WHERE Variable_name IN (
|
|
390
|
-
'max_connections', 'thread_cache_size', 'connect_timeout',
|
|
391
|
-
'wait_timeout', 'interactive_timeout'
|
|
392
|
-
)
|
|
395
|
+
const variablesQuery = `
|
|
396
|
+
SHOW GLOBAL VARIABLES WHERE Variable_name IN (
|
|
397
|
+
'max_connections', 'thread_cache_size', 'connect_timeout',
|
|
398
|
+
'wait_timeout', 'interactive_timeout'
|
|
399
|
+
)
|
|
393
400
|
`;
|
|
394
401
|
const statusResult = await this.db.query(statusQuery);
|
|
395
402
|
const variablesResult = await this.db.query(variablesQuery);
|
|
396
403
|
const stats = {
|
|
397
404
|
current_status: {},
|
|
398
405
|
configuration: {},
|
|
399
|
-
health_indicators: {}
|
|
406
|
+
health_indicators: {},
|
|
400
407
|
};
|
|
401
408
|
for (const row of statusResult) {
|
|
402
409
|
stats.current_status[row.Variable_name.toLowerCase()] = row.Value;
|
|
@@ -405,36 +412,40 @@ class PerformanceTools {
|
|
|
405
412
|
stats.configuration[row.Variable_name.toLowerCase()] = row.Value;
|
|
406
413
|
}
|
|
407
414
|
// Calculate health indicators
|
|
408
|
-
const threadsConnected = parseInt(stats.current_status.threads_connected ||
|
|
409
|
-
const maxConnections = parseInt(stats.configuration.max_connections ||
|
|
410
|
-
const maxUsedConnections = parseInt(stats.current_status.max_used_connections ||
|
|
415
|
+
const threadsConnected = parseInt(stats.current_status.threads_connected || "0");
|
|
416
|
+
const maxConnections = parseInt(stats.configuration.max_connections || "0");
|
|
417
|
+
const maxUsedConnections = parseInt(stats.current_status.max_used_connections || "0");
|
|
411
418
|
if (maxConnections > 0) {
|
|
412
|
-
stats.health_indicators.connection_usage_percentage =
|
|
413
|
-
|
|
414
|
-
stats.health_indicators.
|
|
419
|
+
stats.health_indicators.connection_usage_percentage =
|
|
420
|
+
((threadsConnected / maxConnections) * 100).toFixed(2) + "%";
|
|
421
|
+
stats.health_indicators.max_usage_percentage =
|
|
422
|
+
((maxUsedConnections / maxConnections) * 100).toFixed(2) + "%";
|
|
423
|
+
stats.health_indicators.available_connections =
|
|
424
|
+
maxConnections - threadsConnected;
|
|
415
425
|
}
|
|
416
426
|
// Connection efficiency
|
|
417
|
-
const totalConnections = parseInt(stats.current_status.connections ||
|
|
418
|
-
const abortedConnects = parseInt(stats.current_status.aborted_connects ||
|
|
427
|
+
const totalConnections = parseInt(stats.current_status.connections || "0");
|
|
428
|
+
const abortedConnects = parseInt(stats.current_status.aborted_connects || "0");
|
|
419
429
|
if (totalConnections > 0) {
|
|
420
|
-
stats.health_indicators.aborted_connection_percentage =
|
|
430
|
+
stats.health_indicators.aborted_connection_percentage =
|
|
431
|
+
((abortedConnects / totalConnections) * 100).toFixed(4) + "%";
|
|
421
432
|
}
|
|
422
433
|
// Thread cache efficiency
|
|
423
|
-
const threadsCreated = parseInt(stats.current_status.threads_created ||
|
|
434
|
+
const threadsCreated = parseInt(stats.current_status.threads_created || "0");
|
|
424
435
|
if (totalConnections > 0) {
|
|
425
|
-
stats.health_indicators.thread_cache_hit_rate =
|
|
436
|
+
stats.health_indicators.thread_cache_hit_rate =
|
|
437
|
+
(((totalConnections - threadsCreated) / totalConnections) *
|
|
438
|
+
100).toFixed(2) + "%";
|
|
426
439
|
}
|
|
427
440
|
return {
|
|
428
|
-
status:
|
|
441
|
+
status: "success",
|
|
429
442
|
data: stats,
|
|
430
|
-
queryLog: this.db.getFormattedQueryLogs(2)
|
|
431
443
|
};
|
|
432
444
|
}
|
|
433
445
|
catch (error) {
|
|
434
446
|
return {
|
|
435
|
-
status:
|
|
447
|
+
status: "error",
|
|
436
448
|
error: error.message,
|
|
437
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
438
449
|
};
|
|
439
450
|
}
|
|
440
451
|
}
|
|
@@ -444,14 +455,14 @@ class PerformanceTools {
|
|
|
444
455
|
async getDatabaseHealthCheck() {
|
|
445
456
|
try {
|
|
446
457
|
const health = {
|
|
447
|
-
overall_status:
|
|
458
|
+
overall_status: "healthy",
|
|
448
459
|
checks: [],
|
|
449
460
|
warnings: [],
|
|
450
|
-
errors: []
|
|
461
|
+
errors: [],
|
|
451
462
|
};
|
|
452
463
|
// Check 1: Connection usage
|
|
453
|
-
const connQuery = `
|
|
454
|
-
SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_connected', 'Max_used_connections')
|
|
464
|
+
const connQuery = `
|
|
465
|
+
SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_connected', 'Max_used_connections')
|
|
455
466
|
`;
|
|
456
467
|
const maxConnQuery = `SHOW GLOBAL VARIABLES WHERE Variable_name = 'max_connections'`;
|
|
457
468
|
const connResult = await this.db.query(connQuery);
|
|
@@ -460,9 +471,9 @@ class PerformanceTools {
|
|
|
460
471
|
let maxUsedConnections = 0;
|
|
461
472
|
let maxConnections = 0;
|
|
462
473
|
for (const row of connResult) {
|
|
463
|
-
if (row.Variable_name ===
|
|
474
|
+
if (row.Variable_name === "Threads_connected")
|
|
464
475
|
threadsConnected = parseInt(row.Value);
|
|
465
|
-
if (row.Variable_name ===
|
|
476
|
+
if (row.Variable_name === "Max_used_connections")
|
|
466
477
|
maxUsedConnections = parseInt(row.Value);
|
|
467
478
|
}
|
|
468
479
|
for (const row of maxConnResult) {
|
|
@@ -471,122 +482,124 @@ class PerformanceTools {
|
|
|
471
482
|
const connUsage = (threadsConnected / maxConnections) * 100;
|
|
472
483
|
const maxConnUsage = (maxUsedConnections / maxConnections) * 100;
|
|
473
484
|
health.checks.push({
|
|
474
|
-
name:
|
|
475
|
-
status: connUsage < 80 ?
|
|
485
|
+
name: "Connection Usage",
|
|
486
|
+
status: connUsage < 80 ? "healthy" : connUsage < 90 ? "warning" : "critical",
|
|
476
487
|
current: threadsConnected,
|
|
477
488
|
max: maxConnections,
|
|
478
|
-
usage_percentage: connUsage.toFixed(2) +
|
|
489
|
+
usage_percentage: connUsage.toFixed(2) + "%",
|
|
479
490
|
});
|
|
480
491
|
if (connUsage >= 90) {
|
|
481
|
-
health.errors.push(
|
|
482
|
-
health.overall_status =
|
|
492
|
+
health.errors.push("Connection usage is critically high (>90%)");
|
|
493
|
+
health.overall_status = "critical";
|
|
483
494
|
}
|
|
484
495
|
else if (connUsage >= 80) {
|
|
485
|
-
health.warnings.push(
|
|
486
|
-
if (health.overall_status ===
|
|
487
|
-
health.overall_status =
|
|
496
|
+
health.warnings.push("Connection usage is high (>80%)");
|
|
497
|
+
if (health.overall_status === "healthy")
|
|
498
|
+
health.overall_status = "warning";
|
|
488
499
|
}
|
|
489
500
|
// Check 2: Buffer pool hit ratio
|
|
490
|
-
const bufferQuery = `
|
|
491
|
-
SHOW GLOBAL STATUS WHERE Variable_name IN ('Innodb_buffer_pool_read_requests', 'Innodb_buffer_pool_reads')
|
|
501
|
+
const bufferQuery = `
|
|
502
|
+
SHOW GLOBAL STATUS WHERE Variable_name IN ('Innodb_buffer_pool_read_requests', 'Innodb_buffer_pool_reads')
|
|
492
503
|
`;
|
|
493
504
|
const bufferResult = await this.db.query(bufferQuery);
|
|
494
505
|
let readRequests = 0;
|
|
495
506
|
let reads = 0;
|
|
496
507
|
for (const row of bufferResult) {
|
|
497
|
-
if (row.Variable_name ===
|
|
508
|
+
if (row.Variable_name === "Innodb_buffer_pool_read_requests")
|
|
498
509
|
readRequests = parseInt(row.Value);
|
|
499
|
-
if (row.Variable_name ===
|
|
510
|
+
if (row.Variable_name === "Innodb_buffer_pool_reads")
|
|
500
511
|
reads = parseInt(row.Value);
|
|
501
512
|
}
|
|
502
513
|
const hitRatio = readRequests > 0 ? ((readRequests - reads) / readRequests) * 100 : 100;
|
|
503
514
|
health.checks.push({
|
|
504
|
-
name:
|
|
505
|
-
status: hitRatio > 95 ?
|
|
506
|
-
hit_ratio: hitRatio.toFixed(2) +
|
|
515
|
+
name: "Buffer Pool Hit Ratio",
|
|
516
|
+
status: hitRatio > 95 ? "healthy" : hitRatio > 85 ? "warning" : "critical",
|
|
517
|
+
hit_ratio: hitRatio.toFixed(2) + "%",
|
|
507
518
|
});
|
|
508
519
|
if (hitRatio <= 85) {
|
|
509
|
-
health.errors.push(
|
|
510
|
-
health.overall_status =
|
|
520
|
+
health.errors.push("Buffer pool hit ratio is too low (<85%)");
|
|
521
|
+
health.overall_status = "critical";
|
|
511
522
|
}
|
|
512
523
|
else if (hitRatio <= 95) {
|
|
513
|
-
health.warnings.push(
|
|
514
|
-
if (health.overall_status ===
|
|
515
|
-
health.overall_status =
|
|
524
|
+
health.warnings.push("Buffer pool hit ratio could be improved (<95%)");
|
|
525
|
+
if (health.overall_status === "healthy")
|
|
526
|
+
health.overall_status = "warning";
|
|
516
527
|
}
|
|
517
528
|
// Check 3: Aborted connections
|
|
518
|
-
const abortQuery = `
|
|
519
|
-
SHOW GLOBAL STATUS WHERE Variable_name IN ('Connections', 'Aborted_connects')
|
|
529
|
+
const abortQuery = `
|
|
530
|
+
SHOW GLOBAL STATUS WHERE Variable_name IN ('Connections', 'Aborted_connects')
|
|
520
531
|
`;
|
|
521
532
|
const abortResult = await this.db.query(abortQuery);
|
|
522
533
|
let totalConnections = 0;
|
|
523
534
|
let abortedConnects = 0;
|
|
524
535
|
for (const row of abortResult) {
|
|
525
|
-
if (row.Variable_name ===
|
|
536
|
+
if (row.Variable_name === "Connections")
|
|
526
537
|
totalConnections = parseInt(row.Value);
|
|
527
|
-
if (row.Variable_name ===
|
|
538
|
+
if (row.Variable_name === "Aborted_connects")
|
|
528
539
|
abortedConnects = parseInt(row.Value);
|
|
529
540
|
}
|
|
530
541
|
const abortRate = totalConnections > 0 ? (abortedConnects / totalConnections) * 100 : 0;
|
|
531
542
|
health.checks.push({
|
|
532
|
-
name:
|
|
533
|
-
status: abortRate < 1 ?
|
|
543
|
+
name: "Aborted Connections",
|
|
544
|
+
status: abortRate < 1 ? "healthy" : abortRate < 5 ? "warning" : "critical",
|
|
534
545
|
aborted: abortedConnects,
|
|
535
546
|
total: totalConnections,
|
|
536
|
-
abort_rate: abortRate.toFixed(4) +
|
|
547
|
+
abort_rate: abortRate.toFixed(4) + "%",
|
|
537
548
|
});
|
|
538
549
|
if (abortRate >= 5) {
|
|
539
|
-
health.errors.push(
|
|
540
|
-
if (health.overall_status !==
|
|
541
|
-
health.overall_status =
|
|
550
|
+
health.errors.push("High rate of aborted connections (>5%)");
|
|
551
|
+
if (health.overall_status !== "critical")
|
|
552
|
+
health.overall_status = "warning";
|
|
542
553
|
}
|
|
543
554
|
else if (abortRate >= 1) {
|
|
544
|
-
health.warnings.push(
|
|
545
|
-
if (health.overall_status ===
|
|
546
|
-
health.overall_status =
|
|
555
|
+
health.warnings.push("Elevated rate of aborted connections (>1%)");
|
|
556
|
+
if (health.overall_status === "healthy")
|
|
557
|
+
health.overall_status = "warning";
|
|
547
558
|
}
|
|
548
559
|
// Check 4: Slow queries
|
|
549
|
-
const slowQuery = `
|
|
550
|
-
SHOW GLOBAL STATUS WHERE Variable_name IN ('Questions', 'Slow_queries')
|
|
560
|
+
const slowQuery = `
|
|
561
|
+
SHOW GLOBAL STATUS WHERE Variable_name IN ('Questions', 'Slow_queries')
|
|
551
562
|
`;
|
|
552
563
|
const slowResult = await this.db.query(slowQuery);
|
|
553
564
|
let questions = 0;
|
|
554
565
|
let slowQueries = 0;
|
|
555
566
|
for (const row of slowResult) {
|
|
556
|
-
if (row.Variable_name ===
|
|
567
|
+
if (row.Variable_name === "Questions")
|
|
557
568
|
questions = parseInt(row.Value);
|
|
558
|
-
if (row.Variable_name ===
|
|
569
|
+
if (row.Variable_name === "Slow_queries")
|
|
559
570
|
slowQueries = parseInt(row.Value);
|
|
560
571
|
}
|
|
561
572
|
const slowQueryRate = questions > 0 ? (slowQueries / questions) * 100 : 0;
|
|
562
573
|
health.checks.push({
|
|
563
|
-
name:
|
|
564
|
-
status: slowQueryRate < 1
|
|
574
|
+
name: "Slow Queries",
|
|
575
|
+
status: slowQueryRate < 1
|
|
576
|
+
? "healthy"
|
|
577
|
+
: slowQueryRate < 5
|
|
578
|
+
? "warning"
|
|
579
|
+
: "critical",
|
|
565
580
|
slow_queries: slowQueries,
|
|
566
581
|
total_queries: questions,
|
|
567
|
-
slow_query_rate: slowQueryRate.toFixed(4) +
|
|
582
|
+
slow_query_rate: slowQueryRate.toFixed(4) + "%",
|
|
568
583
|
});
|
|
569
584
|
if (slowQueryRate >= 5) {
|
|
570
|
-
health.warnings.push(
|
|
571
|
-
if (health.overall_status ===
|
|
572
|
-
health.overall_status =
|
|
585
|
+
health.warnings.push("High rate of slow queries (>5%)");
|
|
586
|
+
if (health.overall_status === "healthy")
|
|
587
|
+
health.overall_status = "warning";
|
|
573
588
|
}
|
|
574
589
|
else if (slowQueryRate >= 1) {
|
|
575
|
-
health.warnings.push(
|
|
576
|
-
if (health.overall_status ===
|
|
577
|
-
health.overall_status =
|
|
590
|
+
health.warnings.push("Elevated rate of slow queries (>1%)");
|
|
591
|
+
if (health.overall_status === "healthy")
|
|
592
|
+
health.overall_status = "warning";
|
|
578
593
|
}
|
|
579
594
|
return {
|
|
580
|
-
status:
|
|
595
|
+
status: "success",
|
|
581
596
|
data: health,
|
|
582
|
-
queryLog: this.db.getFormattedQueryLogs(6)
|
|
583
597
|
};
|
|
584
598
|
}
|
|
585
599
|
catch (error) {
|
|
586
600
|
return {
|
|
587
|
-
status:
|
|
601
|
+
status: "error",
|
|
588
602
|
error: error.message,
|
|
589
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
590
603
|
};
|
|
591
604
|
}
|
|
592
605
|
}
|
|
@@ -597,25 +610,23 @@ class PerformanceTools {
|
|
|
597
610
|
try {
|
|
598
611
|
// Truncate performance schema summary tables
|
|
599
612
|
const tables = [
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
613
|
+
"events_statements_summary_by_digest",
|
|
614
|
+
"events_statements_summary_global_by_event_name",
|
|
615
|
+
"table_io_waits_summary_by_table",
|
|
616
|
+
"table_io_waits_summary_by_index_usage",
|
|
604
617
|
];
|
|
605
618
|
for (const table of tables) {
|
|
606
619
|
await this.db.query(`TRUNCATE TABLE performance_schema.${table}`);
|
|
607
620
|
}
|
|
608
621
|
return {
|
|
609
|
-
status:
|
|
610
|
-
message:
|
|
611
|
-
queryLog: this.db.getFormattedQueryLogs(tables.length)
|
|
622
|
+
status: "success",
|
|
623
|
+
message: "Performance statistics reset successfully",
|
|
612
624
|
};
|
|
613
625
|
}
|
|
614
626
|
catch (error) {
|
|
615
627
|
return {
|
|
616
|
-
status:
|
|
628
|
+
status: "error",
|
|
617
629
|
error: error.message,
|
|
618
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
619
630
|
};
|
|
620
631
|
}
|
|
621
632
|
}
|