@zintrust/workers 0.1.27
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/README.md +861 -0
- package/dist/AnomalyDetection.d.ts +102 -0
- package/dist/AnomalyDetection.js +321 -0
- package/dist/AutoScaler.d.ts +127 -0
- package/dist/AutoScaler.js +425 -0
- package/dist/BroadcastWorker.d.ts +21 -0
- package/dist/BroadcastWorker.js +24 -0
- package/dist/CanaryController.d.ts +103 -0
- package/dist/CanaryController.js +380 -0
- package/dist/ChaosEngineering.d.ts +79 -0
- package/dist/ChaosEngineering.js +216 -0
- package/dist/CircuitBreaker.d.ts +106 -0
- package/dist/CircuitBreaker.js +374 -0
- package/dist/ClusterLock.d.ts +90 -0
- package/dist/ClusterLock.js +385 -0
- package/dist/ComplianceManager.d.ts +177 -0
- package/dist/ComplianceManager.js +556 -0
- package/dist/DatacenterOrchestrator.d.ts +133 -0
- package/dist/DatacenterOrchestrator.js +404 -0
- package/dist/DeadLetterQueue.d.ts +122 -0
- package/dist/DeadLetterQueue.js +539 -0
- package/dist/HealthMonitor.d.ts +42 -0
- package/dist/HealthMonitor.js +301 -0
- package/dist/MultiQueueWorker.d.ts +89 -0
- package/dist/MultiQueueWorker.js +277 -0
- package/dist/NotificationWorker.d.ts +21 -0
- package/dist/NotificationWorker.js +23 -0
- package/dist/Observability.d.ts +153 -0
- package/dist/Observability.js +530 -0
- package/dist/PluginManager.d.ts +123 -0
- package/dist/PluginManager.js +392 -0
- package/dist/PriorityQueue.d.ts +117 -0
- package/dist/PriorityQueue.js +244 -0
- package/dist/ResourceMonitor.d.ts +164 -0
- package/dist/ResourceMonitor.js +605 -0
- package/dist/SLAMonitor.d.ts +110 -0
- package/dist/SLAMonitor.js +274 -0
- package/dist/WorkerFactory.d.ts +193 -0
- package/dist/WorkerFactory.js +1507 -0
- package/dist/WorkerInit.d.ts +85 -0
- package/dist/WorkerInit.js +223 -0
- package/dist/WorkerMetrics.d.ts +114 -0
- package/dist/WorkerMetrics.js +509 -0
- package/dist/WorkerRegistry.d.ts +145 -0
- package/dist/WorkerRegistry.js +319 -0
- package/dist/WorkerShutdown.d.ts +61 -0
- package/dist/WorkerShutdown.js +159 -0
- package/dist/WorkerVersioning.d.ts +107 -0
- package/dist/WorkerVersioning.js +300 -0
- package/dist/build-manifest.json +462 -0
- package/dist/config/workerConfig.d.ts +3 -0
- package/dist/config/workerConfig.js +19 -0
- package/dist/createQueueWorker.d.ts +23 -0
- package/dist/createQueueWorker.js +113 -0
- package/dist/dashboard/index.d.ts +1 -0
- package/dist/dashboard/index.js +1 -0
- package/dist/dashboard/types.d.ts +117 -0
- package/dist/dashboard/types.js +1 -0
- package/dist/dashboard/workers-api.d.ts +4 -0
- package/dist/dashboard/workers-api.js +638 -0
- package/dist/dashboard/workers-dashboard-ui.d.ts +3 -0
- package/dist/dashboard/workers-dashboard-ui.js +1026 -0
- package/dist/dashboard/workers-dashboard.d.ts +4 -0
- package/dist/dashboard/workers-dashboard.js +904 -0
- package/dist/helper/index.d.ts +5 -0
- package/dist/helper/index.js +10 -0
- package/dist/http/WorkerApiController.d.ts +38 -0
- package/dist/http/WorkerApiController.js +312 -0
- package/dist/http/WorkerController.d.ts +374 -0
- package/dist/http/WorkerController.js +1351 -0
- package/dist/http/middleware/CustomValidation.d.ts +92 -0
- package/dist/http/middleware/CustomValidation.js +270 -0
- package/dist/http/middleware/DatacenterValidator.d.ts +3 -0
- package/dist/http/middleware/DatacenterValidator.js +94 -0
- package/dist/http/middleware/EditWorkerValidation.d.ts +7 -0
- package/dist/http/middleware/EditWorkerValidation.js +55 -0
- package/dist/http/middleware/FeaturesValidator.d.ts +3 -0
- package/dist/http/middleware/FeaturesValidator.js +60 -0
- package/dist/http/middleware/InfrastructureValidator.d.ts +31 -0
- package/dist/http/middleware/InfrastructureValidator.js +226 -0
- package/dist/http/middleware/OptionsValidator.d.ts +3 -0
- package/dist/http/middleware/OptionsValidator.js +112 -0
- package/dist/http/middleware/PayloadSanitizer.d.ts +7 -0
- package/dist/http/middleware/PayloadSanitizer.js +42 -0
- package/dist/http/middleware/ProcessorPathSanitizer.d.ts +3 -0
- package/dist/http/middleware/ProcessorPathSanitizer.js +74 -0
- package/dist/http/middleware/QueueNameSanitizer.d.ts +3 -0
- package/dist/http/middleware/QueueNameSanitizer.js +45 -0
- package/dist/http/middleware/ValidateDriver.d.ts +7 -0
- package/dist/http/middleware/ValidateDriver.js +20 -0
- package/dist/http/middleware/VersionSanitizer.d.ts +3 -0
- package/dist/http/middleware/VersionSanitizer.js +25 -0
- package/dist/http/middleware/WorkerNameSanitizer.d.ts +3 -0
- package/dist/http/middleware/WorkerNameSanitizer.js +46 -0
- package/dist/http/middleware/WorkerValidationChain.d.ts +27 -0
- package/dist/http/middleware/WorkerValidationChain.js +185 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +48 -0
- package/dist/routes/workers.d.ts +12 -0
- package/dist/routes/workers.js +81 -0
- package/dist/storage/WorkerStore.d.ts +45 -0
- package/dist/storage/WorkerStore.js +195 -0
- package/dist/type.d.ts +76 -0
- package/dist/type.js +1 -0
- package/dist/ui/router/ui.d.ts +3 -0
- package/dist/ui/router/ui.js +83 -0
- package/dist/ui/types/worker-ui.d.ts +229 -0
- package/dist/ui/types/worker-ui.js +5 -0
- package/package.json +53 -0
- package/src/AnomalyDetection.ts +434 -0
- package/src/AutoScaler.ts +654 -0
- package/src/BroadcastWorker.ts +34 -0
- package/src/CanaryController.ts +531 -0
- package/src/ChaosEngineering.ts +301 -0
- package/src/CircuitBreaker.ts +495 -0
- package/src/ClusterLock.ts +499 -0
- package/src/ComplianceManager.ts +815 -0
- package/src/DatacenterOrchestrator.ts +561 -0
- package/src/DeadLetterQueue.ts +733 -0
- package/src/HealthMonitor.ts +390 -0
- package/src/MultiQueueWorker.ts +431 -0
- package/src/NotificationWorker.ts +33 -0
- package/src/Observability.ts +696 -0
- package/src/PluginManager.ts +551 -0
- package/src/PriorityQueue.ts +351 -0
- package/src/ResourceMonitor.ts +769 -0
- package/src/SLAMonitor.ts +408 -0
- package/src/WorkerFactory.ts +2108 -0
- package/src/WorkerInit.ts +313 -0
- package/src/WorkerMetrics.ts +709 -0
- package/src/WorkerRegistry.ts +443 -0
- package/src/WorkerShutdown.ts +210 -0
- package/src/WorkerVersioning.ts +422 -0
- package/src/config/workerConfig.ts +25 -0
- package/src/createQueueWorker.ts +174 -0
- package/src/dashboard/index.ts +6 -0
- package/src/dashboard/types.ts +141 -0
- package/src/dashboard/workers-api.ts +785 -0
- package/src/dashboard/zintrust.svg +30 -0
- package/src/helper/index.ts +11 -0
- package/src/http/WorkerApiController.ts +369 -0
- package/src/http/WorkerController.ts +1512 -0
- package/src/http/middleware/CustomValidation.ts +360 -0
- package/src/http/middleware/DatacenterValidator.ts +124 -0
- package/src/http/middleware/EditWorkerValidation.ts +74 -0
- package/src/http/middleware/FeaturesValidator.ts +82 -0
- package/src/http/middleware/InfrastructureValidator.ts +295 -0
- package/src/http/middleware/OptionsValidator.ts +144 -0
- package/src/http/middleware/PayloadSanitizer.ts +52 -0
- package/src/http/middleware/ProcessorPathSanitizer.ts +86 -0
- package/src/http/middleware/QueueNameSanitizer.ts +55 -0
- package/src/http/middleware/ValidateDriver.ts +29 -0
- package/src/http/middleware/VersionSanitizer.ts +30 -0
- package/src/http/middleware/WorkerNameSanitizer.ts +56 -0
- package/src/http/middleware/WorkerValidationChain.ts +230 -0
- package/src/index.ts +98 -0
- package/src/routes/workers.ts +154 -0
- package/src/storage/WorkerStore.ts +240 -0
- package/src/type.ts +89 -0
- package/src/types/queue-monitor.d.ts +38 -0
- package/src/types/queue-redis.d.ts +38 -0
- package/src/ui/README.md +13 -0
- package/src/ui/components/JsonEditor.js +670 -0
- package/src/ui/components/JsonViewer.js +387 -0
- package/src/ui/components/WorkerCard.js +178 -0
- package/src/ui/components/WorkerExpandPanel.js +257 -0
- package/src/ui/components/fetcher.js +42 -0
- package/src/ui/components/sla-scorecard.js +32 -0
- package/src/ui/components/styles.css +30 -0
- package/src/ui/components/table-expander.js +34 -0
- package/src/ui/integration/worker-ui-integration.js +565 -0
- package/src/ui/router/ui.ts +99 -0
- package/src/ui/services/workerApi.js +240 -0
- package/src/ui/types/worker-ui.ts +283 -0
- package/src/ui/utils/jsonValidator.js +444 -0
- package/src/ui/workers/index.html +202 -0
- package/src/ui/workers/main.js +1781 -0
- package/src/ui/workers/styles.css +1350 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anomaly Detection
|
|
3
|
+
* Statistical anomaly detection with lightweight baselines
|
|
4
|
+
* Sealed namespace for immutability
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ErrorFactory, Logger, generateUuid } from '@zintrust/core';
|
|
8
|
+
import { ResourceMonitor } from './ResourceMonitor';
|
|
9
|
+
import { WorkerMetrics, type MetricPoint, type MetricType } from './WorkerMetrics';
|
|
10
|
+
|
|
11
|
+
export interface IAnomalyConfig {
|
|
12
|
+
workerName: string;
|
|
13
|
+
metrics: MetricType[];
|
|
14
|
+
sensitivity: number; // 0-1 (higher = more sensitive)
|
|
15
|
+
learningPeriod: number; // Days to learn baseline
|
|
16
|
+
alertThreshold: number; // Confidence % to alert (0-1)
|
|
17
|
+
autoAdjust: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface IMetric {
|
|
21
|
+
metricType: MetricType;
|
|
22
|
+
value: number;
|
|
23
|
+
timestamp: Date;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IAnomaly {
|
|
27
|
+
id: string;
|
|
28
|
+
timestamp: Date;
|
|
29
|
+
workerName: string;
|
|
30
|
+
metric: MetricType;
|
|
31
|
+
actual: number;
|
|
32
|
+
expected: number;
|
|
33
|
+
deviation: number;
|
|
34
|
+
confidence: number;
|
|
35
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
36
|
+
possibleCauses: string[];
|
|
37
|
+
recommendations: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface IPrediction {
|
|
41
|
+
workerName: string;
|
|
42
|
+
horizonHours: number;
|
|
43
|
+
riskScore: number;
|
|
44
|
+
expectedErrorRate: number;
|
|
45
|
+
summary: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface IRootCauseAnalysis {
|
|
49
|
+
anomalyId: string;
|
|
50
|
+
suspectedCauses: string[];
|
|
51
|
+
supportingSignals: Record<string, number>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface IForecast {
|
|
55
|
+
workerName: string;
|
|
56
|
+
metric: MetricType;
|
|
57
|
+
horizonHours: number;
|
|
58
|
+
forecast: number;
|
|
59
|
+
confidence: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface IRecommendation {
|
|
63
|
+
action: string;
|
|
64
|
+
reason: string;
|
|
65
|
+
priority: 'low' | 'medium' | 'high';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
type MetricStats = {
|
|
69
|
+
mean: number;
|
|
70
|
+
variance: number;
|
|
71
|
+
count: number;
|
|
72
|
+
updatedAt: Date;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const configs = new Map<string, IAnomalyConfig>();
|
|
76
|
+
const models = new Map<string, Map<MetricType, MetricStats>>();
|
|
77
|
+
|
|
78
|
+
const updateStats = (stats: MetricStats, value: number): MetricStats => {
|
|
79
|
+
const count = stats.count + 1;
|
|
80
|
+
const delta = value - stats.mean;
|
|
81
|
+
const mean = stats.mean + delta / count;
|
|
82
|
+
const delta2 = value - mean;
|
|
83
|
+
const variance = stats.variance + delta * delta2;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
mean,
|
|
87
|
+
variance,
|
|
88
|
+
count,
|
|
89
|
+
updatedAt: new Date(),
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const buildStats = (values: number[]): MetricStats => {
|
|
94
|
+
if (values.length === 0) {
|
|
95
|
+
return { mean: 0, variance: 0, count: 0, updatedAt: new Date() };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let mean = 0;
|
|
99
|
+
let variance = 0;
|
|
100
|
+
let count = 0;
|
|
101
|
+
|
|
102
|
+
values.forEach((value) => {
|
|
103
|
+
const updated = updateStats({ mean, variance, count, updatedAt: new Date() }, value);
|
|
104
|
+
mean = updated.mean;
|
|
105
|
+
variance = updated.variance;
|
|
106
|
+
count = updated.count;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return { mean, variance, count, updatedAt: new Date() };
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const getStdDev = (stats: MetricStats): number => {
|
|
113
|
+
if (stats.count <= 1) return 0;
|
|
114
|
+
return Math.sqrt(stats.variance / (stats.count - 1));
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const getThreshold = (sensitivity: number): number => {
|
|
118
|
+
const clamped = Math.min(1, Math.max(0, sensitivity));
|
|
119
|
+
return Math.min(3, Math.max(1, 3 - clamped * 2));
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const buildPossibleCauses = (metric: MetricType): string[] => {
|
|
123
|
+
switch (metric) {
|
|
124
|
+
case 'duration':
|
|
125
|
+
return ['Increased processing time', 'Downstream dependency latency'];
|
|
126
|
+
case 'errors':
|
|
127
|
+
return ['Increased failure rate', 'New error conditions'];
|
|
128
|
+
case 'cpu':
|
|
129
|
+
return ['Resource saturation', 'Inefficient processing logic'];
|
|
130
|
+
case 'memory':
|
|
131
|
+
return ['Memory leak risk', 'Increased payload size'];
|
|
132
|
+
case 'queue-size':
|
|
133
|
+
return ['Traffic spike', 'Worker throttling'];
|
|
134
|
+
default:
|
|
135
|
+
return ['Unexpected workload change'];
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const buildRecommendations = (metric: MetricType): string[] => {
|
|
140
|
+
switch (metric) {
|
|
141
|
+
case 'duration':
|
|
142
|
+
return ['Review slow job traces', 'Scale worker concurrency temporarily'];
|
|
143
|
+
case 'errors':
|
|
144
|
+
return ['Inspect recent failures', 'Review circuit breaker events'];
|
|
145
|
+
case 'cpu':
|
|
146
|
+
return ['Add capacity or optimize processing', 'Monitor CPU hotspots'];
|
|
147
|
+
case 'memory':
|
|
148
|
+
return ['Inspect memory usage', 'Enable heap snapshots'];
|
|
149
|
+
default:
|
|
150
|
+
return ['Monitor trends and adjust thresholds'];
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const selectSeverity = (zScore: number): IAnomaly['severity'] => {
|
|
155
|
+
if (zScore >= 3.5) return 'critical';
|
|
156
|
+
if (zScore >= 2.5) return 'high';
|
|
157
|
+
if (zScore >= 1.8) return 'medium';
|
|
158
|
+
return 'low';
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const ensureConfig = (workerName: string): IAnomalyConfig => {
|
|
162
|
+
const config = configs.get(workerName);
|
|
163
|
+
if (!config) {
|
|
164
|
+
throw ErrorFactory.createNotFoundError(`Anomaly config not found for worker "${workerName}"`);
|
|
165
|
+
}
|
|
166
|
+
return config;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const ensureModelMap = (workerName: string): Map<MetricType, MetricStats> => {
|
|
170
|
+
let map = models.get(workerName);
|
|
171
|
+
if (!map) {
|
|
172
|
+
map = new Map();
|
|
173
|
+
models.set(workerName, map);
|
|
174
|
+
}
|
|
175
|
+
return map;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const mapPoints = (metric: MetricType, points: ReadonlyArray<MetricPoint>): IMetric[] =>
|
|
179
|
+
points.map((point) => ({
|
|
180
|
+
metricType: metric,
|
|
181
|
+
value: point.value,
|
|
182
|
+
timestamp: point.timestamp,
|
|
183
|
+
}));
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Anomaly Detection - Sealed namespace
|
|
187
|
+
*/
|
|
188
|
+
export const AnomalyDetection = Object.freeze({
|
|
189
|
+
/**
|
|
190
|
+
* Configure anomaly detection for a worker
|
|
191
|
+
*/
|
|
192
|
+
configure(config: IAnomalyConfig): void {
|
|
193
|
+
configs.set(config.workerName, { ...config });
|
|
194
|
+
Logger.info(`Anomaly detection configured for ${config.workerName}`);
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Train baseline model
|
|
199
|
+
*/
|
|
200
|
+
trainModel(workerName: string, historicalData: IMetric[]): void {
|
|
201
|
+
const config = ensureConfig(workerName);
|
|
202
|
+
const modelMap = ensureModelMap(workerName);
|
|
203
|
+
|
|
204
|
+
const metrics = config.metrics;
|
|
205
|
+
metrics.forEach((metric) => {
|
|
206
|
+
const values = historicalData
|
|
207
|
+
.filter((item) => item.metricType === metric)
|
|
208
|
+
.map((item) => item.value);
|
|
209
|
+
|
|
210
|
+
modelMap.set(metric, buildStats(values));
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
Logger.info(`Anomaly model trained for ${workerName}`);
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Update baseline model with recent data
|
|
218
|
+
*/
|
|
219
|
+
updateModel(workerName: string, recentData: IMetric[]): void {
|
|
220
|
+
ensureConfig(workerName);
|
|
221
|
+
const modelMap = ensureModelMap(workerName);
|
|
222
|
+
|
|
223
|
+
recentData.forEach((item) => {
|
|
224
|
+
const current = modelMap.get(item.metricType) ?? {
|
|
225
|
+
mean: 0,
|
|
226
|
+
variance: 0,
|
|
227
|
+
count: 0,
|
|
228
|
+
updatedAt: new Date(),
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
modelMap.set(item.metricType, updateStats(current, item.value));
|
|
232
|
+
});
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Detect anomalies for a worker
|
|
237
|
+
*/
|
|
238
|
+
async detectAnomalies(workerName: string): Promise<IAnomaly[]> {
|
|
239
|
+
const config = ensureConfig(workerName);
|
|
240
|
+
const modelMap = ensureModelMap(workerName);
|
|
241
|
+
|
|
242
|
+
const range = {
|
|
243
|
+
start: new Date(Date.now() - 60 * 60 * 1000),
|
|
244
|
+
end: new Date(),
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const results = await Promise.all(
|
|
248
|
+
config.metrics.map(async (metric) => {
|
|
249
|
+
const entry = await WorkerMetrics.query({
|
|
250
|
+
workerName,
|
|
251
|
+
metricType: metric,
|
|
252
|
+
granularity: 'hourly',
|
|
253
|
+
startDate: range.start,
|
|
254
|
+
endDate: range.end,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const points = entry.points;
|
|
258
|
+
if (points.length === 0) return null;
|
|
259
|
+
|
|
260
|
+
const stats = modelMap.get(metric) ?? buildStats(points.map((point) => point.value));
|
|
261
|
+
modelMap.set(metric, stats);
|
|
262
|
+
|
|
263
|
+
const latest = points.at(-1);
|
|
264
|
+
if (!latest) return null;
|
|
265
|
+
|
|
266
|
+
const stdDev = getStdDev(stats);
|
|
267
|
+
if (stdDev === 0) return null;
|
|
268
|
+
|
|
269
|
+
const zScore = Math.abs((latest.value - stats.mean) / stdDev);
|
|
270
|
+
const threshold = getThreshold(config.sensitivity);
|
|
271
|
+
const confidence = Math.min(1, zScore / threshold);
|
|
272
|
+
|
|
273
|
+
if (zScore < threshold || confidence < config.alertThreshold) return null;
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
id: generateUuid(),
|
|
277
|
+
timestamp: latest.timestamp,
|
|
278
|
+
workerName,
|
|
279
|
+
metric,
|
|
280
|
+
actual: latest.value,
|
|
281
|
+
expected: stats.mean,
|
|
282
|
+
deviation: zScore,
|
|
283
|
+
confidence,
|
|
284
|
+
severity: selectSeverity(zScore),
|
|
285
|
+
possibleCauses: buildPossibleCauses(metric),
|
|
286
|
+
recommendations: buildRecommendations(metric),
|
|
287
|
+
} as IAnomaly;
|
|
288
|
+
})
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
return results.filter((item): item is IAnomaly => item !== null);
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Predict failure risk for a worker
|
|
296
|
+
*/
|
|
297
|
+
async predictFailure(workerName: string, horizonHours: number): Promise<IPrediction> {
|
|
298
|
+
ensureConfig(workerName);
|
|
299
|
+
|
|
300
|
+
const range = {
|
|
301
|
+
start: new Date(Date.now() - 24 * 60 * 60 * 1000),
|
|
302
|
+
end: new Date(),
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const [errors, processed] = await Promise.all([
|
|
306
|
+
WorkerMetrics.aggregate({
|
|
307
|
+
workerName,
|
|
308
|
+
metricType: 'errors',
|
|
309
|
+
granularity: 'hourly',
|
|
310
|
+
startDate: range.start,
|
|
311
|
+
endDate: range.end,
|
|
312
|
+
}),
|
|
313
|
+
WorkerMetrics.aggregate({
|
|
314
|
+
workerName,
|
|
315
|
+
metricType: 'processed',
|
|
316
|
+
granularity: 'hourly',
|
|
317
|
+
startDate: range.start,
|
|
318
|
+
endDate: range.end,
|
|
319
|
+
}),
|
|
320
|
+
]);
|
|
321
|
+
|
|
322
|
+
const errorRate = processed.total > 0 ? errors.total / processed.total : 0;
|
|
323
|
+
const riskScore = Math.min(1, errorRate * 5);
|
|
324
|
+
|
|
325
|
+
let summary = 'Low failure risk detected';
|
|
326
|
+
if (riskScore >= 0.8) {
|
|
327
|
+
summary = 'High failure risk detected';
|
|
328
|
+
} else if (riskScore >= 0.4) {
|
|
329
|
+
summary = 'Moderate failure risk detected';
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
workerName,
|
|
334
|
+
horizonHours,
|
|
335
|
+
riskScore,
|
|
336
|
+
expectedErrorRate: errorRate,
|
|
337
|
+
summary,
|
|
338
|
+
};
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Analyze root cause for an anomaly
|
|
343
|
+
*/
|
|
344
|
+
analyzeRootCause(anomaly: IAnomaly): IRootCauseAnalysis {
|
|
345
|
+
const usage = ResourceMonitor.getCurrentUsage(anomaly.workerName);
|
|
346
|
+
const signals = {
|
|
347
|
+
cpu: usage.cpu,
|
|
348
|
+
memory: usage.memory.percent,
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const suspected = [...anomaly.possibleCauses];
|
|
352
|
+
if (usage.cpu > 80) suspected.push('CPU saturation');
|
|
353
|
+
if (usage.memory.percent > 80) suspected.push('Memory pressure');
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
anomalyId: anomaly.id,
|
|
357
|
+
suspectedCauses: suspected,
|
|
358
|
+
supportingSignals: signals,
|
|
359
|
+
};
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Forecast a metric
|
|
364
|
+
*/
|
|
365
|
+
async forecastMetric(workerName: string, metric: MetricType, hours: number): Promise<IForecast> {
|
|
366
|
+
ensureConfig(workerName);
|
|
367
|
+
|
|
368
|
+
const entry = await WorkerMetrics.query({
|
|
369
|
+
workerName,
|
|
370
|
+
metricType: metric,
|
|
371
|
+
granularity: 'hourly',
|
|
372
|
+
startDate: new Date(Date.now() - 24 * 60 * 60 * 1000),
|
|
373
|
+
endDate: new Date(),
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const values = entry.points.map((point) => point.value);
|
|
377
|
+
const average = values.length > 0 ? values.reduce((sum, v) => sum + v, 0) / values.length : 0;
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
workerName,
|
|
381
|
+
metric,
|
|
382
|
+
horizonHours: hours,
|
|
383
|
+
forecast: average,
|
|
384
|
+
confidence: values.length > 10 ? 0.7 : 0.4,
|
|
385
|
+
};
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Generate recommendations for an anomaly
|
|
390
|
+
*/
|
|
391
|
+
getRecommendations(anomaly: IAnomaly): IRecommendation[] {
|
|
392
|
+
return anomaly.recommendations.map((rec) => ({
|
|
393
|
+
action: rec,
|
|
394
|
+
reason: `Metric ${anomaly.metric} deviated from baseline`,
|
|
395
|
+
priority: anomaly.severity === 'critical' ? 'high' : 'medium',
|
|
396
|
+
}));
|
|
397
|
+
},
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Attempt auto-remediation
|
|
401
|
+
*/
|
|
402
|
+
autoRemediate(_anomaly: IAnomaly): boolean {
|
|
403
|
+
return false;
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Helper: build training data from metrics
|
|
408
|
+
*/
|
|
409
|
+
async buildTrainingData(workerName: string): Promise<IMetric[]> {
|
|
410
|
+
const config = ensureConfig(workerName);
|
|
411
|
+
const range = {
|
|
412
|
+
start: new Date(Date.now() - config.learningPeriod * 24 * 60 * 60 * 1000),
|
|
413
|
+
end: new Date(),
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const batches = await Promise.all(
|
|
417
|
+
config.metrics.map(async (metric) => {
|
|
418
|
+
const entry = await WorkerMetrics.query({
|
|
419
|
+
workerName,
|
|
420
|
+
metricType: metric,
|
|
421
|
+
granularity: 'daily',
|
|
422
|
+
startDate: range.start,
|
|
423
|
+
endDate: range.end,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
return mapPoints(metric, entry.points);
|
|
427
|
+
})
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
return batches.flat();
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
export default AnomalyDetection;
|