@aetherframework/database 1.0.9 → 1.1.1
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/package.json +1 -2
- package/src/DatabaseManager.js +0 -565
- package/src/core/ConnectionManager.js +0 -351
- package/src/core/DatabaseFactory.js +0 -188
- package/src/core/MongoQueryBuilder.js +0 -576
- package/src/core/PluginManager.js +0 -968
- package/src/core/QueryBuilder.js +0 -4398
- package/src/core/TransactionManager.js +0 -40
- package/src/drivers/clickhouse-driver.js +0 -272
- package/src/drivers/index.js +0 -273
- package/src/drivers/mongodb-driver.js +0 -87
- package/src/drivers/mssql-driver.js +0 -117
- package/src/drivers/mysql-driver.js +0 -169
- package/src/drivers/oracle-driver.js +0 -101
- package/src/drivers/postgres-driver.js +0 -234
- package/src/drivers/redis-driver.js +0 -52
- package/src/drivers/sqlite-driver.js +0 -67
- package/src/middleware/connection-pool.js +0 -455
- package/src/middleware/performance-monitor.js +0 -652
- package/src/middleware/query-cache.js +0 -500
- package/src/middleware/query-logger.js +0 -262
- package/src/plugins/AuditPlugin.js +0 -447
- package/src/plugins/BasePlugin.js +0 -418
- package/src/plugins/BatchOperationPlugin.js +0 -165
- package/src/plugins/CachePlugin.js +0 -407
- package/src/plugins/CtePlugin.js +0 -523
- package/src/plugins/DistributedPlugin.js +0 -543
- package/src/plugins/EncryptionPlugin.js +0 -211
- package/src/plugins/FullTextSearchPlugin.js +0 -164
- package/src/plugins/GeospatialPlugin.js +0 -219
- package/src/plugins/GraphQLPlugin.js +0 -162
- package/src/plugins/HookPlugin.js +0 -211
- package/src/plugins/JsonPlugin.js +0 -366
- package/src/plugins/OptimisticLockPlugin.js +0 -374
- package/src/plugins/PerformancePlugin.js +0 -175
- package/src/plugins/ResiliencePlugin.js +0 -114
- package/src/plugins/ShardingPlugin.js +0 -227
- package/src/plugins/SoftDeletePlugin.js +0 -258
- package/src/plugins/SyncPlugin.js +0 -373
- package/src/plugins/VersioningPlugin.js +0 -314
- package/src/plugins/WindowFunctionPlugin.js +0 -343
- package/src/utils/config-loader.js +0 -632
- package/src/utils/error-handler.js +0 -724
- package/src/utils/migration-runner.js +0 -1066
|
@@ -1,652 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license MIT
|
|
3
|
-
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
-
* SPDX-License-Identifier: MIT
|
|
5
|
-
* @module @aetherframework/database/middleware/performance-monitor
|
|
6
|
-
*/
|
|
7
|
-
import { EventEmitter } from 'events';
|
|
8
|
-
|
|
9
|
-
class PerformanceMonitor extends EventEmitter {
|
|
10
|
-
constructor(options = {}) {
|
|
11
|
-
super();
|
|
12
|
-
this.options = {
|
|
13
|
-
enabled: options.enabled !== false,
|
|
14
|
-
slowQueryThreshold: options.slowQueryThreshold || 1000, // ms
|
|
15
|
-
maxQueryHistory: options.maxQueryHistory || 1000,
|
|
16
|
-
collectMetrics: options.collectMetrics !== false,
|
|
17
|
-
metricsInterval: options.metricsInterval || 60000, // 1 minute
|
|
18
|
-
alertThresholds: {
|
|
19
|
-
slowQueriesPerMinute: options.alertThresholds?.slowQueriesPerMinute || 10,
|
|
20
|
-
errorRate: options.alertThresholds?.errorRate || 0.1, // 10%
|
|
21
|
-
connectionErrors: options.alertThresholds?.connectionErrors || 5,
|
|
22
|
-
...options.alertThresholds
|
|
23
|
-
},
|
|
24
|
-
...options
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
this.queries = [];
|
|
28
|
-
this.slowQueries = [];
|
|
29
|
-
this.errors = [];
|
|
30
|
-
this.metrics = {
|
|
31
|
-
totalQueries: 0,
|
|
32
|
-
successfulQueries: 0,
|
|
33
|
-
failedQueries: 0,
|
|
34
|
-
slowQueries: 0,
|
|
35
|
-
totalQueryTime: 0,
|
|
36
|
-
avgQueryTime: 0,
|
|
37
|
-
maxQueryTime: 0,
|
|
38
|
-
minQueryTime: Infinity,
|
|
39
|
-
connections: new Map(),
|
|
40
|
-
alerts: []
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
this.startMetricsCollection();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Monitor query execution
|
|
48
|
-
* @param {Object} query - Query object
|
|
49
|
-
* @param {Function} execute - Query execution function
|
|
50
|
-
* @returns {Promise<Object>} Query result
|
|
51
|
-
*/
|
|
52
|
-
async monitor(query, execute) {
|
|
53
|
-
if (!this.options.enabled) {
|
|
54
|
-
return execute(query);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const startTime = Date.now();
|
|
58
|
-
const startMemory = process.memoryUsage();
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
const result = await execute(query);
|
|
62
|
-
const endTime = Date.now();
|
|
63
|
-
const endMemory = process.memoryUsage();
|
|
64
|
-
|
|
65
|
-
const duration = endTime - startTime;
|
|
66
|
-
const memoryDiff = endMemory.heapUsed - startMemory.heapUsed;
|
|
67
|
-
|
|
68
|
-
this.recordQuery(query, duration, true, null, memoryDiff);
|
|
69
|
-
|
|
70
|
-
// Check for slow query
|
|
71
|
-
if (duration > this.options.slowQueryThreshold) {
|
|
72
|
-
this.recordSlowQuery(query, duration);
|
|
73
|
-
this.emit('slow-query', { query, duration, threshold: this.options.slowQueryThreshold });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return result;
|
|
77
|
-
} catch (error) {
|
|
78
|
-
const endTime = Date.now();
|
|
79
|
-
const duration = endTime - startTime;
|
|
80
|
-
|
|
81
|
-
this.recordQuery(query, duration, false, error);
|
|
82
|
-
this.recordError(query, error, duration);
|
|
83
|
-
|
|
84
|
-
this.emit('query-error', { query, error, duration });
|
|
85
|
-
throw error;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Record query execution
|
|
91
|
-
* @param {Object} query - Query object
|
|
92
|
-
* @param {number} duration - Execution duration in ms
|
|
93
|
-
* @param {boolean} success - Whether query succeeded
|
|
94
|
-
* @param {Error|null} error - Error object if failed
|
|
95
|
-
* @param {number} memoryDiff - Memory difference in bytes
|
|
96
|
-
*/
|
|
97
|
-
recordQuery(query, duration, success, error = null, memoryDiff = 0) {
|
|
98
|
-
const queryRecord = {
|
|
99
|
-
timestamp: new Date().toISOString(),
|
|
100
|
-
sql: query.sql.substring(0, 200) + (query.sql.length > 200 ? '...' : ''),
|
|
101
|
-
params: query.params,
|
|
102
|
-
duration,
|
|
103
|
-
success,
|
|
104
|
-
error: error ? error.message : null,
|
|
105
|
-
connection: query.connectionName || 'default',
|
|
106
|
-
type: query.type || 'query',
|
|
107
|
-
memoryDiff
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// Add to query history
|
|
111
|
-
this.queries.push(queryRecord);
|
|
112
|
-
if (this.queries.length > this.options.maxQueryHistory) {
|
|
113
|
-
this.queries.shift();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Update metrics
|
|
117
|
-
this.metrics.totalQueries++;
|
|
118
|
-
if (success) {
|
|
119
|
-
this.metrics.successfulQueries++;
|
|
120
|
-
} else {
|
|
121
|
-
this.metrics.failedQueries++;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
this.metrics.totalQueryTime += duration;
|
|
125
|
-
this.metrics.avgQueryTime = this.metrics.totalQueryTime / this.metrics.totalQueries;
|
|
126
|
-
this.metrics.maxQueryTime = Math.max(this.metrics.maxQueryTime, duration);
|
|
127
|
-
this.metrics.minQueryTime = Math.min(this.metrics.minQueryTime, duration);
|
|
128
|
-
|
|
129
|
-
// Update connection metrics
|
|
130
|
-
const connectionName = query.connectionName || 'default';
|
|
131
|
-
if (!this.metrics.connections.has(connectionName)) {
|
|
132
|
-
this.metrics.connections.set(connectionName, {
|
|
133
|
-
totalQueries: 0,
|
|
134
|
-
successfulQueries: 0,
|
|
135
|
-
failedQueries: 0,
|
|
136
|
-
totalQueryTime: 0,
|
|
137
|
-
avgQueryTime: 0
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const connMetrics = this.metrics.connections.get(connectionName);
|
|
142
|
-
connMetrics.totalQueries++;
|
|
143
|
-
if (success) {
|
|
144
|
-
connMetrics.successfulQueries++;
|
|
145
|
-
} else {
|
|
146
|
-
connMetrics.failedQueries++;
|
|
147
|
-
}
|
|
148
|
-
connMetrics.totalQueryTime += duration;
|
|
149
|
-
connMetrics.avgQueryTime = connMetrics.totalQueryTime / connMetrics.totalQueries;
|
|
150
|
-
|
|
151
|
-
this.emit('query-recorded', queryRecord);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Record slow query
|
|
156
|
-
* @param {Object} query - Query object
|
|
157
|
-
* @param {number} duration - Execution duration in ms
|
|
158
|
-
*/
|
|
159
|
-
recordSlowQuery(query, duration) {
|
|
160
|
-
const slowQueryRecord = {
|
|
161
|
-
timestamp: new Date().toISOString(),
|
|
162
|
-
sql: query.sql.substring(0, 200) + (query.sql.length > 200 ? '...' : ''),
|
|
163
|
-
params: query.params,
|
|
164
|
-
duration,
|
|
165
|
-
threshold: this.options.slowQueryThreshold,
|
|
166
|
-
connection: query.connectionName || 'default',
|
|
167
|
-
type: query.type || 'query'
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
this.slowQueries.push(slowQueryRecord);
|
|
171
|
-
this.metrics.slowQueries++;
|
|
172
|
-
|
|
173
|
-
// Check alert threshold
|
|
174
|
-
const slowQueriesLastMinute = this.getSlowQueriesLastMinute();
|
|
175
|
-
if (slowQueriesLastMinute > this.options.alertThresholds.slowQueriesPerMinute) {
|
|
176
|
-
this.triggerAlert('slow-queries', {
|
|
177
|
-
message: `High number of slow queries detected: ${slowQueriesLastMinute} in the last minute`,
|
|
178
|
-
threshold: this.options.alertThresholds.slowQueriesPerMinute,
|
|
179
|
-
actual: slowQueriesLastMinute,
|
|
180
|
-
queries: this.slowQueries.slice(-10) // Last 10 slow queries
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Record error
|
|
187
|
-
* @param {Object} query - Query object
|
|
188
|
-
* @param {Error} error - Error object
|
|
189
|
-
* @param {number} duration - Execution duration in ms
|
|
190
|
-
*/
|
|
191
|
-
recordError(query, error, duration) {
|
|
192
|
-
const errorRecord = {
|
|
193
|
-
timestamp: new Date().toISOString(),
|
|
194
|
-
sql: query.sql.substring(0, 200) + (query.sql.length > 200 ? '...' : ''),
|
|
195
|
-
params: query.params,
|
|
196
|
-
duration,
|
|
197
|
-
error: error.message,
|
|
198
|
-
stack: error.stack,
|
|
199
|
-
connection: query.connectionName || 'default',
|
|
200
|
-
type: query.type || 'query'
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
this.errors.push(errorRecord);
|
|
204
|
-
|
|
205
|
-
// Check error rate alert
|
|
206
|
-
const errorRate = this.getErrorRate();
|
|
207
|
-
if (errorRate > this.options.alertThresholds.errorRate) {
|
|
208
|
-
this.triggerAlert('error-rate', {
|
|
209
|
-
message: `High error rate detected: ${(errorRate * 100).toFixed(2)}%`,
|
|
210
|
-
threshold: this.options.alertThresholds.errorRate,
|
|
211
|
-
actual: errorRate,
|
|
212
|
-
errors: this.errors.slice(-10) // Last 10 errors
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Get slow queries from last minute
|
|
219
|
-
* @returns {number} Number of slow queries
|
|
220
|
-
*/
|
|
221
|
-
getSlowQueriesLastMinute() {
|
|
222
|
-
const oneMinuteAgo = Date.now() - 60000;
|
|
223
|
-
return this.slowQueries.filter(q =>
|
|
224
|
-
new Date(q.timestamp).getTime() > oneMinuteAgo
|
|
225
|
-
).length;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Get error rate
|
|
230
|
-
* @returns {number} Error rate (0-1)
|
|
231
|
-
*/
|
|
232
|
-
getErrorRate() {
|
|
233
|
-
if (this.metrics.totalQueries === 0) {
|
|
234
|
-
return 0;
|
|
235
|
-
}
|
|
236
|
-
return this.metrics.failedQueries / this.metrics.totalQueries;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Trigger alert
|
|
241
|
-
* @param {string} type - Alert type
|
|
242
|
-
* @param {Object} data - Alert data
|
|
243
|
-
*/
|
|
244
|
-
triggerAlert(type, data) {
|
|
245
|
-
const alert = {
|
|
246
|
-
type,
|
|
247
|
-
timestamp: new Date().toISOString(),
|
|
248
|
-
...data
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
this.metrics.alerts.push(alert);
|
|
252
|
-
if (this.metrics.alerts.length > 100) {
|
|
253
|
-
this.metrics.alerts.shift();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
this.emit('alert', alert);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Start metrics collection
|
|
261
|
-
*/
|
|
262
|
-
startMetricsCollection() {
|
|
263
|
-
if (!this.options.collectMetrics) {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
setInterval(() => {
|
|
268
|
-
this.collectSystemMetrics();
|
|
269
|
-
}, this.options.metricsInterval);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Collect system metrics
|
|
274
|
-
*/
|
|
275
|
-
collectSystemMetrics() {
|
|
276
|
-
const memoryUsage = process.memoryUsage();
|
|
277
|
-
const cpuUsage = process.cpuUsage();
|
|
278
|
-
|
|
279
|
-
const systemMetrics = {
|
|
280
|
-
timestamp: new Date().toISOString(),
|
|
281
|
-
memory: {
|
|
282
|
-
rss: memoryUsage.rss,
|
|
283
|
-
heapTotal: memoryUsage.heapTotal,
|
|
284
|
-
heapUsed: memoryUsage.heapUsed,
|
|
285
|
-
external: memoryUsage.external,
|
|
286
|
-
arrayBuffers: memoryUsage.arrayBuffers
|
|
287
|
-
},
|
|
288
|
-
cpu: {
|
|
289
|
-
user: cpuUsage.user,
|
|
290
|
-
system: cpuUsage.system
|
|
291
|
-
},
|
|
292
|
-
uptime: process.uptime(),
|
|
293
|
-
queries: {
|
|
294
|
-
total: this.metrics.totalQueries,
|
|
295
|
-
successful: this.metrics.successfulQueries,
|
|
296
|
-
failed: this.metrics.failedQueries,
|
|
297
|
-
slow: this.metrics.slowQueries,
|
|
298
|
-
avgTime: this.metrics.avgQueryTime,
|
|
299
|
-
maxTime: this.metrics.maxQueryTime,
|
|
300
|
-
minTime: this.metrics.minQueryTime === Infinity ? 0 : this.metrics.minQueryTime
|
|
301
|
-
},
|
|
302
|
-
connections: Object.fromEntries(this.metrics.connections)
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
this.emit('metrics-collected', systemMetrics);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Get performance metrics
|
|
310
|
-
* @returns {Object} Performance metrics
|
|
311
|
-
*/
|
|
312
|
-
getMetrics() {
|
|
313
|
-
const now = Date.now();
|
|
314
|
-
const oneMinuteAgo = now - 60000;
|
|
315
|
-
const fiveMinutesAgo = now - 300000;
|
|
316
|
-
const oneHourAgo = now - 3600000;
|
|
317
|
-
|
|
318
|
-
const queriesLastMinute = this.queries.filter(q =>
|
|
319
|
-
new Date(q.timestamp).getTime() > oneMinuteAgo
|
|
320
|
-
);
|
|
321
|
-
const queriesLastFiveMinutes = this.queries.filter(q =>
|
|
322
|
-
new Date(q.timestamp).getTime() > fiveMinutesAgo
|
|
323
|
-
);
|
|
324
|
-
const queriesLastHour = this.queries.filter(q =>
|
|
325
|
-
new Date(q.timestamp).getTime() > oneHourAgo
|
|
326
|
-
);
|
|
327
|
-
|
|
328
|
-
const slowQueriesLastMinute = this.slowQueries.filter(q =>
|
|
329
|
-
new Date(q.timestamp).getTime() > oneMinuteAgo
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
const errorsLastMinute = this.errors.filter(e =>
|
|
333
|
-
new Date(e.timestamp).getTime() > oneMinuteAgo
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
return {
|
|
337
|
-
summary: {
|
|
338
|
-
totalQueries: this.metrics.totalQueries,
|
|
339
|
-
successfulQueries: this.metrics.successfulQueries,
|
|
340
|
-
failedQueries: this.metrics.failedQueries,
|
|
341
|
-
slowQueries: this.metrics.slowQueries,
|
|
342
|
-
errorRate: this.getErrorRate(),
|
|
343
|
-
avgQueryTime: this.metrics.avgQueryTime,
|
|
344
|
-
maxQueryTime: this.metrics.maxQueryTime,
|
|
345
|
-
minQueryTime: this.metrics.minQueryTime === Infinity ? 0 : this.metrics.minQueryTime
|
|
346
|
-
},
|
|
347
|
-
recent: {
|
|
348
|
-
lastMinute: {
|
|
349
|
-
queries: queriesLastMinute.length,
|
|
350
|
-
slowQueries: slowQueriesLastMinute.length,
|
|
351
|
-
errors: errorsLastMinute.length,
|
|
352
|
-
avgDuration: queriesLastMinute.length > 0
|
|
353
|
-
? queriesLastMinute.reduce((sum, q) => sum + q.duration, 0) / queriesLastMinute.length
|
|
354
|
-
: 0
|
|
355
|
-
},
|
|
356
|
-
lastFiveMinutes: {
|
|
357
|
-
queries: queriesLastFiveMinutes.length,
|
|
358
|
-
avgDuration: queriesLastFiveMinutes.length > 0
|
|
359
|
-
? queriesLastFiveMinutes.reduce((sum, q) => sum + q.duration, 0) / queriesLastFiveMinutes.length
|
|
360
|
-
: 0
|
|
361
|
-
},
|
|
362
|
-
lastHour: {
|
|
363
|
-
queries: queriesLastHour.length,
|
|
364
|
-
avgDuration: queriesLastHour.length > 0
|
|
365
|
-
? queriesLastHour.reduce((sum, q) => sum + q.duration, 0) / queriesLastHour.length
|
|
366
|
-
: 0
|
|
367
|
-
}
|
|
368
|
-
},
|
|
369
|
-
connections: Object.fromEntries(this.metrics.connections),
|
|
370
|
-
alerts: this.metrics.alerts.slice(-10), // Last 10 alerts
|
|
371
|
-
settings: {
|
|
372
|
-
slowQueryThreshold: this.options.slowQueryThreshold,
|
|
373
|
-
maxQueryHistory: this.options.maxQueryHistory,
|
|
374
|
-
alertThresholds: this.options.alertThresholds
|
|
375
|
-
},
|
|
376
|
-
timestamp: new Date().toISOString()
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Get slow queries
|
|
382
|
-
* @param {number} limit - Maximum number of queries to return
|
|
383
|
-
* @returns {Array} Slow queries
|
|
384
|
-
*/
|
|
385
|
-
getSlowQueries(limit = 50) {
|
|
386
|
-
return this.slowQueries.slice(-limit).reverse();
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Get recent errors
|
|
391
|
-
* @param {number} limit - Maximum number of errors to return
|
|
392
|
-
* @returns {Array} Recent errors
|
|
393
|
-
*/
|
|
394
|
-
getRecentErrors(limit = 50) {
|
|
395
|
-
return this.errors.slice(-limit).reverse();
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Get query history
|
|
400
|
-
* @param {Object} filters - Filter options
|
|
401
|
-
* @param {number} limit - Maximum number of queries to return
|
|
402
|
-
* @returns {Array} Query history
|
|
403
|
-
*/
|
|
404
|
-
getQueryHistory(filters = {}, limit = 100) {
|
|
405
|
-
let filtered = [...this.queries];
|
|
406
|
-
|
|
407
|
-
if (filters.connection) {
|
|
408
|
-
filtered = filtered.filter(q => q.connection === filters.connection);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (filters.type) {
|
|
412
|
-
filtered = filtered.filter(q => q.type === filters.type);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if (filters.success !== undefined) {
|
|
416
|
-
filtered = filtered.filter(q => q.success === filters.success);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
if (filters.minDuration) {
|
|
420
|
-
filtered = filtered.filter(q => q.duration >= filters.minDuration);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
if (filters.maxDuration) {
|
|
424
|
-
filtered = filtered.filter(q => q.duration <= filters.maxDuration);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (filters.startTime) {
|
|
428
|
-
const start = new Date(filters.startTime).getTime();
|
|
429
|
-
filtered = filtered.filter(q => new Date(q.timestamp).getTime() >= start);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
if (filters.endTime) {
|
|
433
|
-
const end = new Date(filters.endTime).getTime();
|
|
434
|
-
filtered = filtered.filter(q => new Date(q.timestamp).getTime() <= end);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
return filtered.slice(-limit).reverse();
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Generate performance report
|
|
442
|
-
* @returns {Object} Performance report
|
|
443
|
-
*/
|
|
444
|
-
generateReport() {
|
|
445
|
-
const metrics = this.getMetrics();
|
|
446
|
-
const report = {
|
|
447
|
-
timestamp: new Date().toISOString(),
|
|
448
|
-
summary: metrics.summary,
|
|
449
|
-
recommendations: [],
|
|
450
|
-
issues: []
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
// Generate recommendations
|
|
454
|
-
if (metrics.summary.errorRate > 0.05) {
|
|
455
|
-
report.recommendations.push('High error rate detected. Consider reviewing query patterns and database connections.');
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
if (metrics.summary.avgQueryTime > 100) {
|
|
459
|
-
report.recommendations.push('Average query time is high. Consider adding indexes or optimizing queries.');
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (metrics.recent.lastMinute.slowQueries > 5) {
|
|
463
|
-
report.recommendations.push('Multiple slow queries detected in the last minute. Consider reviewing slow query logs.');
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// Identify issues
|
|
467
|
-
if (metrics.alerts.length > 0) {
|
|
468
|
-
report.issues = metrics.alerts.map(alert => ({
|
|
469
|
-
type: alert.type,
|
|
470
|
-
message: alert.message,
|
|
471
|
-
timestamp: alert.timestamp
|
|
472
|
-
}));
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// Connection analysis
|
|
476
|
-
for (const [connection, stats] of Object.entries(metrics.connections)) {
|
|
477
|
-
if (stats.failedQueries > 0) {
|
|
478
|
-
report.issues.push({
|
|
479
|
-
type: 'connection-errors',
|
|
480
|
-
message: `Connection "${connection}" has ${stats.failedQueries} failed queries`,
|
|
481
|
-
connection,
|
|
482
|
-
failedQueries: stats.failedQueries
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
if (stats.avgQueryTime > 500) {
|
|
487
|
-
report.recommendations.push(`Connection "${connection}" has high average query time (${stats.avgQueryTime.toFixed(2)}ms). Consider optimizing queries or increasing connection pool size.`);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// Memory usage analysis
|
|
492
|
-
const memoryUsage = process.memoryUsage();
|
|
493
|
-
const memoryUsageMB = memoryUsage.heapUsed / 1024 / 1024;
|
|
494
|
-
|
|
495
|
-
if (memoryUsageMB > 500) {
|
|
496
|
-
report.issues.push({
|
|
497
|
-
type: 'high-memory-usage',
|
|
498
|
-
message: `High memory usage detected: ${memoryUsageMB.toFixed(2)} MB`,
|
|
499
|
-
memoryUsage: memoryUsageMB
|
|
500
|
-
});
|
|
501
|
-
report.recommendations.push('High memory usage detected. Consider implementing query result pagination or reducing cache size.');
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
return report;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* Reset performance monitor
|
|
509
|
-
*/
|
|
510
|
-
reset() {
|
|
511
|
-
this.queries = [];
|
|
512
|
-
this.slowQueries = [];
|
|
513
|
-
this.errors = [];
|
|
514
|
-
this.metrics = {
|
|
515
|
-
totalQueries: 0,
|
|
516
|
-
successfulQueries: 0,
|
|
517
|
-
failedQueries: 0,
|
|
518
|
-
slowQueries: 0,
|
|
519
|
-
totalQueryTime: 0,
|
|
520
|
-
avgQueryTime: 0,
|
|
521
|
-
maxQueryTime: 0,
|
|
522
|
-
minQueryTime: Infinity,
|
|
523
|
-
connections: new Map(),
|
|
524
|
-
alerts: []
|
|
525
|
-
};
|
|
526
|
-
this.emit('reset');
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Export performance data
|
|
531
|
-
* @param {string} format - Export format (json, csv)
|
|
532
|
-
* @returns {string} Exported data
|
|
533
|
-
*/
|
|
534
|
-
export(format = 'json') {
|
|
535
|
-
const data = {
|
|
536
|
-
queries: this.queries,
|
|
537
|
-
slowQueries: this.slowQueries,
|
|
538
|
-
errors: this.errors,
|
|
539
|
-
metrics: this.getMetrics(),
|
|
540
|
-
timestamp: new Date().toISOString()
|
|
541
|
-
};
|
|
542
|
-
|
|
543
|
-
switch (format.toLowerCase()) {
|
|
544
|
-
case 'csv':
|
|
545
|
-
return this.exportToCSV(data);
|
|
546
|
-
case 'json':
|
|
547
|
-
default:
|
|
548
|
-
return JSON.stringify(data, null, 2);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Export to CSV
|
|
554
|
-
* @param {Object} data - Data to export
|
|
555
|
-
* @returns {string} CSV data
|
|
556
|
-
*/
|
|
557
|
-
exportToCSV(data) {
|
|
558
|
-
const csvLines = [];
|
|
559
|
-
|
|
560
|
-
// Export queries
|
|
561
|
-
if (data.queries.length > 0) {
|
|
562
|
-
csvLines.push('=== QUERIES ===');
|
|
563
|
-
const queryHeaders = Object.keys(data.queries[0]).join(',');
|
|
564
|
-
csvLines.push(queryHeaders);
|
|
565
|
-
data.queries.forEach(query => {
|
|
566
|
-
const values = Object.values(query).map(v =>
|
|
567
|
-
typeof v === 'string' ? `"${v.replace(/"/g, '""')}"` : v
|
|
568
|
-
).join(',');
|
|
569
|
-
csvLines.push(values);
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Export slow queries
|
|
574
|
-
if (data.slowQueries.length > 0) {
|
|
575
|
-
csvLines.push('\n=== SLOW QUERIES ===');
|
|
576
|
-
const slowQueryHeaders = Object.keys(data.slowQueries[0]).join(',');
|
|
577
|
-
csvLines.push(slowQueryHeaders);
|
|
578
|
-
data.slowQueries.forEach(query => {
|
|
579
|
-
const values = Object.values(query).map(v =>
|
|
580
|
-
typeof v === 'string' ? `"${v.replace(/"/g, '""')}"` : v
|
|
581
|
-
).join(',');
|
|
582
|
-
csvLines.push(values);
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// Export errors
|
|
587
|
-
if (data.errors.length > 0) {
|
|
588
|
-
csvLines.push('\n=== ERRORS ===');
|
|
589
|
-
const errorHeaders = Object.keys(data.errors[0]).join(',');
|
|
590
|
-
csvLines.push(errorHeaders);
|
|
591
|
-
data.errors.forEach(error => {
|
|
592
|
-
const values = Object.values(error).map(v =>
|
|
593
|
-
typeof v === 'string' ? `"${v.replace(/"/g, '""')}"` : v
|
|
594
|
-
).join(',');
|
|
595
|
-
csvLines.push(values);
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
return csvLines.join('\n');
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* Get health status
|
|
604
|
-
* @returns {Object} Health status
|
|
605
|
-
*/
|
|
606
|
-
getHealthStatus() {
|
|
607
|
-
const metrics = this.getMetrics();
|
|
608
|
-
const errorRate = metrics.summary.errorRate;
|
|
609
|
-
const slowQueryRate = metrics.recent.lastMinute.slowQueries;
|
|
610
|
-
|
|
611
|
-
let status = 'healthy';
|
|
612
|
-
let issues = [];
|
|
613
|
-
|
|
614
|
-
if (errorRate > 0.1) {
|
|
615
|
-
status = 'critical';
|
|
616
|
-
issues.push(`High error rate: ${(errorRate * 100).toFixed(2)}%`);
|
|
617
|
-
} else if (errorRate > 0.05) {
|
|
618
|
-
status = 'warning';
|
|
619
|
-
issues.push(`Moderate error rate: ${(errorRate * 100).toFixed(2)}%`);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
if (slowQueryRate > 20) {
|
|
623
|
-
status = 'critical';
|
|
624
|
-
issues.push(`High number of slow queries: ${slowQueryRate} in last minute`);
|
|
625
|
-
} else if (slowQueryRate > 10) {
|
|
626
|
-
status = 'warning';
|
|
627
|
-
issues.push(`Moderate number of slow queries: ${slowQueryRate} in last minute`);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
if (metrics.summary.avgQueryTime > 1000) {
|
|
631
|
-
status = 'critical';
|
|
632
|
-
issues.push(`High average query time: ${metrics.summary.avgQueryTime.toFixed(2)}ms`);
|
|
633
|
-
} else if (metrics.summary.avgQueryTime > 500) {
|
|
634
|
-
status = 'warning';
|
|
635
|
-
issues.push(`Moderate average query time: ${metrics.summary.avgQueryTime.toFixed(2)}ms`);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
return {
|
|
639
|
-
status,
|
|
640
|
-
issues,
|
|
641
|
-
metrics: {
|
|
642
|
-
errorRate,
|
|
643
|
-
slowQueryRate,
|
|
644
|
-
avgQueryTime: metrics.summary.avgQueryTime,
|
|
645
|
-
totalQueries: metrics.summary.totalQueries,
|
|
646
|
-
timestamp: new Date().toISOString()
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
export default PerformanceMonitor;
|