claude-flow-novice 1.5.12 → 1.5.14
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/.claude/agents/analysis/code-review/analyze-code-quality.md +160 -177
- package/.claude/agents/architecture/system-design/arch-system-design.md +118 -153
- package/.claude-flow-novice/dist/mcp/auth.js +347 -0
- package/.claude-flow-novice/dist/mcp/claude-code-wrapper.js +717 -0
- package/.claude-flow-novice/dist/mcp/claude-flow-tools.js +1365 -0
- package/.claude-flow-novice/dist/mcp/client.js +201 -0
- package/.claude-flow-novice/dist/mcp/index.js +192 -0
- package/.claude-flow-novice/dist/mcp/integrate-wrapper.js +85 -0
- package/.claude-flow-novice/dist/mcp/lifecycle-manager.js +348 -0
- package/.claude-flow-novice/dist/mcp/load-balancer.js +386 -0
- package/.claude-flow-novice/dist/mcp/mcp-config-manager.js +1362 -0
- package/.claude-flow-novice/dist/mcp/mcp-server-novice-simplified.js +583 -0
- package/.claude-flow-novice/dist/mcp/mcp-server-novice.js +723 -0
- package/.claude-flow-novice/dist/mcp/mcp-server-sdk.js +649 -0
- package/.claude-flow-novice/dist/mcp/mcp-server.js +2256 -0
- package/.claude-flow-novice/dist/mcp/orchestration-integration.js +800 -0
- package/.claude-flow-novice/dist/mcp/performance-monitor.js +489 -0
- package/.claude-flow-novice/dist/mcp/protocol-manager.js +376 -0
- package/.claude-flow-novice/dist/mcp/router.js +220 -0
- package/.claude-flow-novice/dist/mcp/ruv-swarm-tools.js +671 -0
- package/.claude-flow-novice/dist/mcp/ruv-swarm-wrapper.js +254 -0
- package/.claude-flow-novice/dist/mcp/server-with-wrapper.js +32 -0
- package/.claude-flow-novice/dist/mcp/server-wrapper-mode.js +26 -0
- package/.claude-flow-novice/dist/mcp/server.js +539 -0
- package/.claude-flow-novice/dist/mcp/session-manager.js +338 -0
- package/.claude-flow-novice/dist/mcp/sparc-modes.js +455 -0
- package/.claude-flow-novice/dist/mcp/swarm-tools.js +903 -0
- package/.claude-flow-novice/dist/mcp/tools.js +426 -0
- package/.claude-flow-novice/dist/src/cli/commands/swarm.js +23 -1
- package/.claude-flow-novice/dist/src/cli/commands/swarm.js.map +1 -1
- package/.claude-flow-novice/dist/src/cli/simple-commands/init/templates/CLAUDE.md +42 -102
- package/.claude-flow-novice/dist/src/config/web-portal-config.js +2 -1
- package/.claude-flow-novice/dist/src/config/web-portal-config.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/swarm-coordinator-factory.js +36 -0
- package/.claude-flow-novice/dist/src/coordination/swarm-coordinator-factory.js.map +1 -0
- package/.claude-flow-novice/dist/src/preferences/user-preference-manager.js +371 -0
- package/.claude-flow-novice/dist/src/preferences/user-preference-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/index.js +12 -0
- package/.claude-flow-novice/dist/src/validators/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/swarm-init-validator.js +261 -0
- package/.claude-flow-novice/dist/src/validators/swarm-init-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-batching-validator.js +204 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-batching-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-integration.js +189 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/portal-server.js +12 -5
- package/.claude-flow-novice/dist/src/web/portal-server.js.map +1 -1
- package/config/hooks/post-edit-pipeline.js +231 -10
- package/package.json +4 -2
- package/scripts/src/web/frontend/.claude-flow/metrics/agent-metrics.json +1 -0
- package/scripts/src/web/frontend/.claude-flow/metrics/performance.json +9 -0
- package/scripts/src/web/frontend/.claude-flow/metrics/task-metrics.json +10 -0
- package/src/cli/simple-commands/init/templates/CLAUDE.md +4 -1
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Performance Monitoring and Optimization
|
|
3
|
+
*/ import { EventEmitter } from 'node:events';
|
|
4
|
+
import { performance } from 'node:perf_hooks';
|
|
5
|
+
/**
|
|
6
|
+
* MCP Performance Monitor
|
|
7
|
+
* Provides comprehensive performance monitoring, alerting, and optimization suggestions
|
|
8
|
+
*/ export class MCPPerformanceMonitor extends EventEmitter {
|
|
9
|
+
logger;
|
|
10
|
+
requestMetrics = new Map();
|
|
11
|
+
historicalMetrics = [];
|
|
12
|
+
responseTimes = [];
|
|
13
|
+
alertRules = new Map();
|
|
14
|
+
activeAlerts = new Map();
|
|
15
|
+
optimizationSuggestions = [];
|
|
16
|
+
metricsTimer;
|
|
17
|
+
alertCheckTimer;
|
|
18
|
+
cleanupTimer;
|
|
19
|
+
config = {
|
|
20
|
+
metricsInterval: 10000,
|
|
21
|
+
alertCheckInterval: 5000,
|
|
22
|
+
maxHistorySize: 1000,
|
|
23
|
+
maxResponseTimeHistory: 10000,
|
|
24
|
+
cleanupInterval: 300000,
|
|
25
|
+
requestTimeout: 30000
|
|
26
|
+
};
|
|
27
|
+
constructor(logger){
|
|
28
|
+
super(), this.logger = logger;
|
|
29
|
+
this.setupDefaultAlertRules();
|
|
30
|
+
this.startMonitoring();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Record the start of a request
|
|
34
|
+
*/ recordRequestStart(request, session) {
|
|
35
|
+
const requestId = `${request.id}_${Date.now()}`;
|
|
36
|
+
const metrics = {
|
|
37
|
+
id: requestId,
|
|
38
|
+
method: request.method,
|
|
39
|
+
sessionId: session.id,
|
|
40
|
+
startTime: performance.now(),
|
|
41
|
+
requestSize: this.calculateRequestSize(request)
|
|
42
|
+
};
|
|
43
|
+
this.requestMetrics.set(requestId, metrics);
|
|
44
|
+
this.logger.debug('Request started', {
|
|
45
|
+
requestId,
|
|
46
|
+
method: request.method,
|
|
47
|
+
sessionId: session.id
|
|
48
|
+
});
|
|
49
|
+
return requestId;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Record the completion of a request
|
|
53
|
+
*/ recordRequestEnd(requestId, response, error) {
|
|
54
|
+
const metrics = this.requestMetrics.get(requestId);
|
|
55
|
+
if (!metrics) {
|
|
56
|
+
this.logger.warn('Request metrics not found', {
|
|
57
|
+
requestId
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const endTime = performance.now();
|
|
62
|
+
const duration = endTime - metrics.startTime;
|
|
63
|
+
metrics.endTime = endTime;
|
|
64
|
+
metrics.duration = duration;
|
|
65
|
+
metrics.success = !error;
|
|
66
|
+
metrics.error = error?.message;
|
|
67
|
+
metrics.responseSize = response ? this.calculateResponseSize(response) : 0;
|
|
68
|
+
// Add to response time history
|
|
69
|
+
this.responseTimes.push(duration);
|
|
70
|
+
if (this.responseTimes.length > this.config.maxResponseTimeHistory) {
|
|
71
|
+
this.responseTimes.shift();
|
|
72
|
+
}
|
|
73
|
+
this.logger.debug('Request completed', {
|
|
74
|
+
requestId,
|
|
75
|
+
duration,
|
|
76
|
+
success: metrics.success,
|
|
77
|
+
error: metrics.error
|
|
78
|
+
});
|
|
79
|
+
this.emit('requestCompleted', metrics);
|
|
80
|
+
// Remove from active metrics after some time
|
|
81
|
+
setTimeout(()=>{
|
|
82
|
+
this.requestMetrics.delete(requestId);
|
|
83
|
+
}, 60000); // Keep for 1 minute
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get current performance metrics
|
|
87
|
+
*/ getCurrentMetrics() {
|
|
88
|
+
const now = Date.now();
|
|
89
|
+
const completedRequests = Array.from(this.requestMetrics.values()).filter((m)=>m.endTime !== undefined);
|
|
90
|
+
const successfulRequests = completedRequests.filter((m)=>m.success);
|
|
91
|
+
const errorRate = completedRequests.length > 0 ? (completedRequests.length - successfulRequests.length) / completedRequests.length * 100 : 0;
|
|
92
|
+
// Calculate response time percentiles
|
|
93
|
+
const sortedTimes = [
|
|
94
|
+
...this.responseTimes
|
|
95
|
+
].sort((a, b)=>a - b);
|
|
96
|
+
const p50 = this.getPercentile(sortedTimes, 0.5);
|
|
97
|
+
const p95 = this.getPercentile(sortedTimes, 0.95);
|
|
98
|
+
const p99 = this.getPercentile(sortedTimes, 0.99);
|
|
99
|
+
// Calculate throughput (requests per second over last minute)
|
|
100
|
+
const oneMinuteAgo = now - 60000;
|
|
101
|
+
const recentRequests = completedRequests.filter((m)=>m.endTime && m.startTime + oneMinuteAgo > 0);
|
|
102
|
+
const throughput = recentRequests.length / 60;
|
|
103
|
+
const memUsage = process.memoryUsage();
|
|
104
|
+
const cpuUsage = process.cpuUsage();
|
|
105
|
+
const metrics = {
|
|
106
|
+
requestCount: completedRequests.length,
|
|
107
|
+
averageResponseTime: this.responseTimes.length > 0 ? this.responseTimes.reduce((a, b)=>a + b, 0) / this.responseTimes.length : 0,
|
|
108
|
+
minResponseTime: sortedTimes.length > 0 ? sortedTimes[0] : 0,
|
|
109
|
+
maxResponseTime: sortedTimes.length > 0 ? sortedTimes[sortedTimes.length - 1] : 0,
|
|
110
|
+
p50ResponseTime: p50,
|
|
111
|
+
p95ResponseTime: p95,
|
|
112
|
+
p99ResponseTime: p99,
|
|
113
|
+
errorRate,
|
|
114
|
+
throughput,
|
|
115
|
+
activeConnections: this.requestMetrics.size,
|
|
116
|
+
memoryUsage: {
|
|
117
|
+
heapUsed: memUsage.heapUsed,
|
|
118
|
+
heapTotal: memUsage.heapTotal,
|
|
119
|
+
external: memUsage.external,
|
|
120
|
+
rss: memUsage.rss
|
|
121
|
+
},
|
|
122
|
+
cpuUsage: {
|
|
123
|
+
user: cpuUsage.user / 1000000,
|
|
124
|
+
system: cpuUsage.system / 1000000
|
|
125
|
+
},
|
|
126
|
+
timestamp: new Date()
|
|
127
|
+
};
|
|
128
|
+
return metrics;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get historical metrics
|
|
132
|
+
*/ getHistoricalMetrics(limit) {
|
|
133
|
+
return limit ? this.historicalMetrics.slice(-limit) : [
|
|
134
|
+
...this.historicalMetrics
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Add custom alert rule
|
|
139
|
+
*/ addAlertRule(rule) {
|
|
140
|
+
this.alertRules.set(rule.id, rule);
|
|
141
|
+
this.logger.info('Alert rule added', {
|
|
142
|
+
id: rule.id,
|
|
143
|
+
name: rule.name,
|
|
144
|
+
metric: rule.metric,
|
|
145
|
+
threshold: rule.threshold
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Remove alert rule
|
|
150
|
+
*/ removeAlertRule(ruleId) {
|
|
151
|
+
this.alertRules.delete(ruleId);
|
|
152
|
+
// Resolve any active alerts for this rule
|
|
153
|
+
for (const [alertId, alert] of this.activeAlerts.entries()){
|
|
154
|
+
if (alert.ruleId === ruleId) {
|
|
155
|
+
this.resolveAlert(alertId);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
this.logger.info('Alert rule removed', {
|
|
159
|
+
ruleId
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get active alerts
|
|
164
|
+
*/ getActiveAlerts() {
|
|
165
|
+
return Array.from(this.activeAlerts.values());
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get optimization suggestions
|
|
169
|
+
*/ getOptimizationSuggestions() {
|
|
170
|
+
return [
|
|
171
|
+
...this.optimizationSuggestions
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get performance summary
|
|
176
|
+
*/ getPerformanceSummary() {
|
|
177
|
+
const current = this.getCurrentMetrics();
|
|
178
|
+
const trends = this.calculateTrends();
|
|
179
|
+
return {
|
|
180
|
+
current,
|
|
181
|
+
trends,
|
|
182
|
+
alerts: this.activeAlerts.size,
|
|
183
|
+
suggestions: this.optimizationSuggestions.length
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Resolve an alert
|
|
188
|
+
*/ resolveAlert(alertId) {
|
|
189
|
+
const alert = this.activeAlerts.get(alertId);
|
|
190
|
+
if (alert) {
|
|
191
|
+
alert.resolvedAt = new Date();
|
|
192
|
+
this.activeAlerts.delete(alertId);
|
|
193
|
+
this.logger.info('Alert resolved', {
|
|
194
|
+
alertId,
|
|
195
|
+
ruleName: alert.ruleName,
|
|
196
|
+
duration: alert.resolvedAt.getTime() - alert.triggeredAt.getTime()
|
|
197
|
+
});
|
|
198
|
+
this.emit('alertResolved', alert);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Clear all optimization suggestions
|
|
203
|
+
*/ clearOptimizationSuggestions() {
|
|
204
|
+
this.optimizationSuggestions = [];
|
|
205
|
+
this.logger.info('Optimization suggestions cleared');
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Stop monitoring
|
|
209
|
+
*/ stop() {
|
|
210
|
+
if (this.metricsTimer) {
|
|
211
|
+
clearInterval(this.metricsTimer);
|
|
212
|
+
this.metricsTimer = undefined;
|
|
213
|
+
}
|
|
214
|
+
if (this.alertCheckTimer) {
|
|
215
|
+
clearInterval(this.alertCheckTimer);
|
|
216
|
+
this.alertCheckTimer = undefined;
|
|
217
|
+
}
|
|
218
|
+
if (this.cleanupTimer) {
|
|
219
|
+
clearInterval(this.cleanupTimer);
|
|
220
|
+
this.cleanupTimer = undefined;
|
|
221
|
+
}
|
|
222
|
+
this.logger.info('Performance monitoring stopped');
|
|
223
|
+
}
|
|
224
|
+
startMonitoring() {
|
|
225
|
+
// Collect metrics periodically
|
|
226
|
+
this.metricsTimer = setInterval(()=>{
|
|
227
|
+
const metrics = this.getCurrentMetrics();
|
|
228
|
+
this.historicalMetrics.push(metrics);
|
|
229
|
+
// Keep only recent history
|
|
230
|
+
if (this.historicalMetrics.length > this.config.maxHistorySize) {
|
|
231
|
+
this.historicalMetrics.shift();
|
|
232
|
+
}
|
|
233
|
+
this.emit('metricsCollected', metrics);
|
|
234
|
+
}, this.config.metricsInterval);
|
|
235
|
+
// Check alerts periodically
|
|
236
|
+
this.alertCheckTimer = setInterval(()=>{
|
|
237
|
+
this.checkAlerts();
|
|
238
|
+
}, this.config.alertCheckInterval);
|
|
239
|
+
// Cleanup old data
|
|
240
|
+
this.cleanupTimer = setInterval(()=>{
|
|
241
|
+
this.cleanup();
|
|
242
|
+
this.generateOptimizationSuggestions();
|
|
243
|
+
}, this.config.cleanupInterval);
|
|
244
|
+
this.logger.info('Performance monitoring started');
|
|
245
|
+
}
|
|
246
|
+
setupDefaultAlertRules() {
|
|
247
|
+
const defaultRules = [
|
|
248
|
+
{
|
|
249
|
+
id: 'high_response_time',
|
|
250
|
+
name: 'High Response Time',
|
|
251
|
+
metric: 'averageResponseTime',
|
|
252
|
+
operator: 'gt',
|
|
253
|
+
threshold: 5000,
|
|
254
|
+
duration: 30000,
|
|
255
|
+
enabled: true,
|
|
256
|
+
severity: 'medium',
|
|
257
|
+
actions: [
|
|
258
|
+
'log',
|
|
259
|
+
'notify'
|
|
260
|
+
]
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
id: 'high_error_rate',
|
|
264
|
+
name: 'High Error Rate',
|
|
265
|
+
metric: 'errorRate',
|
|
266
|
+
operator: 'gt',
|
|
267
|
+
threshold: 10,
|
|
268
|
+
duration: 60000,
|
|
269
|
+
enabled: true,
|
|
270
|
+
severity: 'high',
|
|
271
|
+
actions: [
|
|
272
|
+
'log',
|
|
273
|
+
'notify',
|
|
274
|
+
'alert'
|
|
275
|
+
]
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: 'low_throughput',
|
|
279
|
+
name: 'Low Throughput',
|
|
280
|
+
metric: 'throughput',
|
|
281
|
+
operator: 'lt',
|
|
282
|
+
threshold: 1,
|
|
283
|
+
duration: 120000,
|
|
284
|
+
enabled: true,
|
|
285
|
+
severity: 'medium',
|
|
286
|
+
actions: [
|
|
287
|
+
'log',
|
|
288
|
+
'notify'
|
|
289
|
+
]
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
id: 'high_memory_usage',
|
|
293
|
+
name: 'High Memory Usage',
|
|
294
|
+
metric: 'memoryUsage.heapUsed',
|
|
295
|
+
operator: 'gt',
|
|
296
|
+
threshold: 1024 * 1024 * 1024,
|
|
297
|
+
duration: 300000,
|
|
298
|
+
enabled: true,
|
|
299
|
+
severity: 'high',
|
|
300
|
+
actions: [
|
|
301
|
+
'log',
|
|
302
|
+
'notify',
|
|
303
|
+
'alert'
|
|
304
|
+
]
|
|
305
|
+
}
|
|
306
|
+
];
|
|
307
|
+
for (const rule of defaultRules){
|
|
308
|
+
this.alertRules.set(rule.id, rule);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
checkAlerts() {
|
|
312
|
+
const metrics = this.getCurrentMetrics();
|
|
313
|
+
for (const rule of this.alertRules.values()){
|
|
314
|
+
if (!rule.enabled) continue;
|
|
315
|
+
const value = this.getMetricValue(metrics, rule.metric);
|
|
316
|
+
const triggered = this.evaluateCondition(value, rule.operator, rule.threshold);
|
|
317
|
+
const existingAlert = Array.from(this.activeAlerts.values()).find((a)=>a.ruleId === rule.id && !a.resolvedAt);
|
|
318
|
+
if (triggered && !existingAlert) {
|
|
319
|
+
// Create new alert
|
|
320
|
+
const alert = {
|
|
321
|
+
id: `alert_${rule.id}_${Date.now()}`,
|
|
322
|
+
ruleId: rule.id,
|
|
323
|
+
ruleName: rule.name,
|
|
324
|
+
severity: rule.severity,
|
|
325
|
+
message: `${rule.name}: ${rule.metric} is ${value} (threshold: ${rule.threshold})`,
|
|
326
|
+
triggeredAt: new Date(),
|
|
327
|
+
currentValue: value,
|
|
328
|
+
threshold: rule.threshold
|
|
329
|
+
};
|
|
330
|
+
this.activeAlerts.set(alert.id, alert);
|
|
331
|
+
this.logger.warn('Alert triggered', {
|
|
332
|
+
alertId: alert.id,
|
|
333
|
+
ruleName: rule.name,
|
|
334
|
+
metric: rule.metric,
|
|
335
|
+
value,
|
|
336
|
+
threshold: rule.threshold
|
|
337
|
+
});
|
|
338
|
+
this.emit('alertTriggered', alert);
|
|
339
|
+
} else if (!triggered && existingAlert) {
|
|
340
|
+
// Resolve existing alert
|
|
341
|
+
this.resolveAlert(existingAlert.id);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
getMetricValue(metrics, path) {
|
|
346
|
+
const parts = path.split('.');
|
|
347
|
+
let value = metrics;
|
|
348
|
+
for (const part of parts){
|
|
349
|
+
value = value?.[part];
|
|
350
|
+
if (value === undefined) break;
|
|
351
|
+
}
|
|
352
|
+
return typeof value === 'number' ? value : 0;
|
|
353
|
+
}
|
|
354
|
+
evaluateCondition(value, operator, threshold) {
|
|
355
|
+
switch(operator){
|
|
356
|
+
case 'gt':
|
|
357
|
+
return value > threshold;
|
|
358
|
+
case 'gte':
|
|
359
|
+
return value >= threshold;
|
|
360
|
+
case 'lt':
|
|
361
|
+
return value < threshold;
|
|
362
|
+
case 'lte':
|
|
363
|
+
return value <= threshold;
|
|
364
|
+
case 'eq':
|
|
365
|
+
return value === threshold;
|
|
366
|
+
default:
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
getPercentile(sortedArray, percentile) {
|
|
371
|
+
if (sortedArray.length === 0) return 0;
|
|
372
|
+
const index = Math.ceil(sortedArray.length * percentile) - 1;
|
|
373
|
+
return sortedArray[Math.max(0, Math.min(index, sortedArray.length - 1))];
|
|
374
|
+
}
|
|
375
|
+
calculateTrends() {
|
|
376
|
+
const recentMetrics = this.historicalMetrics.slice(-10); // Last 10 data points
|
|
377
|
+
if (recentMetrics.length < 2) {
|
|
378
|
+
return {
|
|
379
|
+
responseTime: 'stable',
|
|
380
|
+
throughput: 'stable',
|
|
381
|
+
errorRate: 'stable'
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
const first = recentMetrics[0];
|
|
385
|
+
const last = recentMetrics[recentMetrics.length - 1];
|
|
386
|
+
return {
|
|
387
|
+
responseTime: this.getTrend(first.averageResponseTime, last.averageResponseTime, true),
|
|
388
|
+
throughput: this.getTrend(first.throughput, last.throughput, false),
|
|
389
|
+
errorRate: this.getTrend(first.errorRate, last.errorRate, true)
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
getTrend(oldValue, newValue, lowerIsBetter) {
|
|
393
|
+
const change = (newValue - oldValue) / oldValue;
|
|
394
|
+
const threshold = 0.1; // 10% change threshold
|
|
395
|
+
if (Math.abs(change) < threshold) {
|
|
396
|
+
return 'stable';
|
|
397
|
+
}
|
|
398
|
+
const improving = lowerIsBetter ? change < 0 : change > 0;
|
|
399
|
+
return improving ? 'improving' : 'degrading';
|
|
400
|
+
}
|
|
401
|
+
generateOptimizationSuggestions() {
|
|
402
|
+
const metrics = this.getCurrentMetrics();
|
|
403
|
+
const suggestions = [];
|
|
404
|
+
// High response time suggestion
|
|
405
|
+
if (metrics.averageResponseTime > 2000) {
|
|
406
|
+
suggestions.push({
|
|
407
|
+
id: `opt_response_time_${Date.now()}`,
|
|
408
|
+
type: 'performance',
|
|
409
|
+
priority: 'high',
|
|
410
|
+
title: 'Optimize Response Time',
|
|
411
|
+
description: 'Average response time is above 2 seconds',
|
|
412
|
+
impact: 'Improve user experience and system throughput',
|
|
413
|
+
implementation: 'Consider implementing caching, optimizing database queries, or adding connection pooling',
|
|
414
|
+
estimatedImprovement: '30-50% response time reduction',
|
|
415
|
+
detectedAt: new Date(),
|
|
416
|
+
metrics: {
|
|
417
|
+
averageResponseTime: metrics.averageResponseTime
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
// High memory usage suggestion
|
|
422
|
+
if (metrics.memoryUsage.heapUsed > 512 * 1024 * 1024) {
|
|
423
|
+
// 512MB
|
|
424
|
+
suggestions.push({
|
|
425
|
+
id: `opt_memory_${Date.now()}`,
|
|
426
|
+
type: 'memory',
|
|
427
|
+
priority: 'medium',
|
|
428
|
+
title: 'Optimize Memory Usage',
|
|
429
|
+
description: 'Heap memory usage is high',
|
|
430
|
+
impact: 'Prevent memory leaks and improve stability',
|
|
431
|
+
implementation: 'Review memory usage patterns, implement object pooling, or add garbage collection tuning',
|
|
432
|
+
estimatedImprovement: '20-30% memory reduction',
|
|
433
|
+
detectedAt: new Date(),
|
|
434
|
+
metrics: {
|
|
435
|
+
heapUsed: metrics.memoryUsage.heapUsed
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
// Low throughput suggestion
|
|
440
|
+
if (metrics.throughput < 5 && metrics.requestCount > 100) {
|
|
441
|
+
suggestions.push({
|
|
442
|
+
id: `opt_throughput_${Date.now()}`,
|
|
443
|
+
type: 'throughput',
|
|
444
|
+
priority: 'medium',
|
|
445
|
+
title: 'Improve Throughput',
|
|
446
|
+
description: 'Request throughput is below optimal levels',
|
|
447
|
+
impact: 'Handle more concurrent requests efficiently',
|
|
448
|
+
implementation: 'Consider horizontal scaling, load balancing, or request batching',
|
|
449
|
+
estimatedImprovement: '2-3x throughput increase',
|
|
450
|
+
detectedAt: new Date(),
|
|
451
|
+
metrics: {
|
|
452
|
+
throughput: metrics.throughput
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
// Add only new suggestions
|
|
457
|
+
for (const suggestion of suggestions){
|
|
458
|
+
const exists = this.optimizationSuggestions.some((s)=>s.type === suggestion.type && s.title === suggestion.title);
|
|
459
|
+
if (!exists) {
|
|
460
|
+
this.optimizationSuggestions.push(suggestion);
|
|
461
|
+
this.emit('optimizationSuggestion', suggestion);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// Keep only recent suggestions (last 24 hours)
|
|
465
|
+
const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
466
|
+
this.optimizationSuggestions = this.optimizationSuggestions.filter((s)=>s.detectedAt > dayAgo);
|
|
467
|
+
}
|
|
468
|
+
cleanup() {
|
|
469
|
+
const now = Date.now();
|
|
470
|
+
// Clean up old request metrics
|
|
471
|
+
for (const [id, metrics] of this.requestMetrics.entries()){
|
|
472
|
+
if (now - metrics.startTime > this.config.requestTimeout) {
|
|
473
|
+
this.requestMetrics.delete(id);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
// Clean up old response times
|
|
477
|
+
if (this.responseTimes.length > this.config.maxResponseTimeHistory) {
|
|
478
|
+
this.responseTimes = this.responseTimes.slice(-this.config.maxResponseTimeHistory);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
calculateRequestSize(request) {
|
|
482
|
+
return JSON.stringify(request).length;
|
|
483
|
+
}
|
|
484
|
+
calculateResponseSize(response) {
|
|
485
|
+
return JSON.stringify(response).length;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
//# sourceMappingURL=performance-monitor.js.map
|