@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,769 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource Monitor
|
|
3
|
+
* Real-time resource tracking with cost calculation
|
|
4
|
+
* Sealed namespace for immutability
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Env, Logger, NodeSingletons } from '@zintrust/core';
|
|
8
|
+
|
|
9
|
+
const getOsModule = (): typeof NodeSingletons.os => NodeSingletons?.os ?? null;
|
|
10
|
+
|
|
11
|
+
const safeTotalMemory = (): number => {
|
|
12
|
+
const os = getOsModule();
|
|
13
|
+
if (!os?.totalmem) return 0;
|
|
14
|
+
try {
|
|
15
|
+
return os.totalmem();
|
|
16
|
+
} catch {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const safeFreeMemory = (): number => {
|
|
22
|
+
const os = getOsModule();
|
|
23
|
+
if (!os?.freemem) return 0;
|
|
24
|
+
try {
|
|
25
|
+
return os.freemem();
|
|
26
|
+
} catch {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const safeLoadAverage = (): number[] => {
|
|
32
|
+
const os = getOsModule();
|
|
33
|
+
if (!os?.loadavg) return [0, 0, 0];
|
|
34
|
+
try {
|
|
35
|
+
return os.loadavg();
|
|
36
|
+
} catch {
|
|
37
|
+
return [0, 0, 0];
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const safeCpuCount = (): number => {
|
|
42
|
+
const os = getOsModule();
|
|
43
|
+
if (!os?.cpus) return 1;
|
|
44
|
+
try {
|
|
45
|
+
return Math.max(1, os.cpus().length);
|
|
46
|
+
} catch {
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const safePlatform = (): string => {
|
|
52
|
+
const os = getOsModule();
|
|
53
|
+
if (!os?.platform) return 'unknown';
|
|
54
|
+
try {
|
|
55
|
+
return os.platform();
|
|
56
|
+
} catch {
|
|
57
|
+
return 'unknown';
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const safeArch = (): string => {
|
|
62
|
+
const os = getOsModule();
|
|
63
|
+
if (!os?.arch) return 'unknown';
|
|
64
|
+
try {
|
|
65
|
+
return os.arch();
|
|
66
|
+
} catch {
|
|
67
|
+
return 'unknown';
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const safeHostname = (): string => {
|
|
72
|
+
const os = getOsModule();
|
|
73
|
+
if (!os?.hostname) return 'unknown';
|
|
74
|
+
try {
|
|
75
|
+
return os.hostname();
|
|
76
|
+
} catch {
|
|
77
|
+
return 'unknown';
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const safeUptime = (): number => {
|
|
82
|
+
const os = getOsModule();
|
|
83
|
+
if (!os?.uptime) return 0;
|
|
84
|
+
try {
|
|
85
|
+
return os.uptime();
|
|
86
|
+
} catch {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type ResourceSnapshot = {
|
|
92
|
+
timestamp: Date;
|
|
93
|
+
cpu: {
|
|
94
|
+
usage: number; // Percentage 0-100
|
|
95
|
+
loadAverage: number[];
|
|
96
|
+
cores: number;
|
|
97
|
+
};
|
|
98
|
+
memory: {
|
|
99
|
+
total: number; // Bytes
|
|
100
|
+
used: number; // Bytes
|
|
101
|
+
free: number; // Bytes
|
|
102
|
+
usage: number; // Percentage 0-100
|
|
103
|
+
};
|
|
104
|
+
disk: {
|
|
105
|
+
read: number; // Bytes/sec
|
|
106
|
+
write: number; // Bytes/sec
|
|
107
|
+
};
|
|
108
|
+
network: {
|
|
109
|
+
received: number; // Bytes/sec
|
|
110
|
+
transmitted: number; // Bytes/sec
|
|
111
|
+
};
|
|
112
|
+
process: {
|
|
113
|
+
pid: number;
|
|
114
|
+
uptime: number; // Seconds
|
|
115
|
+
memoryUsage: NodeJS.MemoryUsage;
|
|
116
|
+
cpuUsage: NodeJS.CpuUsage;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export type WorkerResourceUsage = {
|
|
121
|
+
cpu: number;
|
|
122
|
+
memory: { number: number; percent: number; used: number; free: number };
|
|
123
|
+
cost: { hourly: number; daily: number };
|
|
124
|
+
workerName: string;
|
|
125
|
+
resourceSnapshot: ResourceSnapshot;
|
|
126
|
+
estimatedCost: {
|
|
127
|
+
perHour: number;
|
|
128
|
+
perDay: number;
|
|
129
|
+
perMonth: number;
|
|
130
|
+
};
|
|
131
|
+
efficiency: {
|
|
132
|
+
cpuEfficiency: number; // 0-100, higher is better
|
|
133
|
+
memoryEfficiency: number; // 0-100, higher is better
|
|
134
|
+
overallScore: number; // 0-100
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export type CostCalculationConfig = {
|
|
139
|
+
computeCostPerCoreHour: number; // USD per core per hour
|
|
140
|
+
memoryCostPerGBHour: number; // USD per GB per hour
|
|
141
|
+
networkCostPerGB: number; // USD per GB transferred
|
|
142
|
+
diskCostPerGB: number; // USD per GB storage
|
|
143
|
+
spotInstanceDiscount: number; // 0-100 percentage discount
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export type ResourceAlert = {
|
|
147
|
+
timestamp: Date;
|
|
148
|
+
workerName: string;
|
|
149
|
+
alertType: 'cpu-high' | 'memory-high' | 'disk-high' | 'cost-high';
|
|
150
|
+
severity: 'warning' | 'critical';
|
|
151
|
+
message: string;
|
|
152
|
+
currentValue: number;
|
|
153
|
+
threshold: number;
|
|
154
|
+
recommendation?: string;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export type ResourceTrend = {
|
|
158
|
+
workerName: string;
|
|
159
|
+
metric: 'cpu' | 'memory' | 'disk' | 'network' | 'cost';
|
|
160
|
+
period: 'hour' | 'day' | 'week';
|
|
161
|
+
trend: 'increasing' | 'decreasing' | 'stable';
|
|
162
|
+
changePercentage: number;
|
|
163
|
+
predictions: {
|
|
164
|
+
nextHour: number;
|
|
165
|
+
nextDay: number;
|
|
166
|
+
nextWeek: number;
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Default cost configuration (AWS-like pricing)
|
|
171
|
+
const DEFAULT_COST_CONFIG: CostCalculationConfig = {
|
|
172
|
+
computeCostPerCoreHour: 0.0416, // ~$0.0416 per vCPU hour (t3.medium equivalent)
|
|
173
|
+
memoryCostPerGBHour: 0.0052, // ~$0.0052 per GB hour
|
|
174
|
+
networkCostPerGB: 0.09, // $0.09 per GB transferred
|
|
175
|
+
diskCostPerGB: 0.1, // $0.10 per GB/month
|
|
176
|
+
spotInstanceDiscount: 70, // 70% discount for spot instances
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// Internal state
|
|
180
|
+
let costConfig: CostCalculationConfig = { ...DEFAULT_COST_CONFIG };
|
|
181
|
+
let monitoringInterval: NodeJS.Timeout | null = null;
|
|
182
|
+
const resourceHistory = new Map<string, ResourceSnapshot[]>();
|
|
183
|
+
const alertHistory = new Map<string, ResourceAlert[]>();
|
|
184
|
+
|
|
185
|
+
// Resource thresholds
|
|
186
|
+
const THRESHOLDS = {
|
|
187
|
+
cpu: { warning: 70, critical: 90 },
|
|
188
|
+
memory: { warning: 75, critical: 85 },
|
|
189
|
+
disk: { warning: 80, critical: 90 },
|
|
190
|
+
costPerHour: { warning: 10, critical: 50 },
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// Previous CPU usage for delta calculation
|
|
194
|
+
let previousCpuUsage: NodeJS.CpuUsage | null = null;
|
|
195
|
+
let previousCpuTimestamp: number | null = null;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Helper: Calculate CPU usage percentage
|
|
199
|
+
*/
|
|
200
|
+
const calculateCpuUsage = (): number => {
|
|
201
|
+
const currentCpuUsage = process.cpuUsage();
|
|
202
|
+
const currentTimestamp = Date.now();
|
|
203
|
+
|
|
204
|
+
if (previousCpuUsage === null || previousCpuTimestamp === null) {
|
|
205
|
+
previousCpuUsage = currentCpuUsage;
|
|
206
|
+
previousCpuTimestamp = currentTimestamp;
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const userDelta = currentCpuUsage.user - previousCpuUsage.user;
|
|
211
|
+
const systemDelta = currentCpuUsage.system - previousCpuUsage.system;
|
|
212
|
+
const timeDelta = (currentTimestamp - previousCpuTimestamp) * 1000; // Convert ms to microseconds
|
|
213
|
+
|
|
214
|
+
previousCpuUsage = currentCpuUsage;
|
|
215
|
+
previousCpuTimestamp = currentTimestamp;
|
|
216
|
+
|
|
217
|
+
if (timeDelta === 0) return 0;
|
|
218
|
+
|
|
219
|
+
const totalCpuDelta = userDelta + systemDelta;
|
|
220
|
+
const cpuPercentage = (totalCpuDelta / timeDelta) * 100;
|
|
221
|
+
|
|
222
|
+
return Math.min(100, Math.max(0, cpuPercentage));
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Helper: Get memory usage
|
|
227
|
+
*/
|
|
228
|
+
const getMemoryUsage = (): ResourceSnapshot['memory'] => {
|
|
229
|
+
const totalMemory = safeTotalMemory();
|
|
230
|
+
const freeMemory = safeFreeMemory();
|
|
231
|
+
const usedMemory = totalMemory - freeMemory;
|
|
232
|
+
const usage = totalMemory > 0 ? (usedMemory / totalMemory) * 100 : 0;
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
total: totalMemory,
|
|
236
|
+
used: usedMemory,
|
|
237
|
+
free: freeMemory,
|
|
238
|
+
usage,
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Helper: Capture resource snapshot
|
|
244
|
+
*/
|
|
245
|
+
const captureSnapshot = (): ResourceSnapshot => {
|
|
246
|
+
const cpuUsage = calculateCpuUsage();
|
|
247
|
+
const memoryUsage = getMemoryUsage();
|
|
248
|
+
const loadAverage = safeLoadAverage();
|
|
249
|
+
const cpuCores = safeCpuCount();
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
timestamp: new Date(),
|
|
253
|
+
cpu: {
|
|
254
|
+
usage: cpuUsage,
|
|
255
|
+
loadAverage,
|
|
256
|
+
cores: cpuCores,
|
|
257
|
+
},
|
|
258
|
+
memory: memoryUsage,
|
|
259
|
+
disk: {
|
|
260
|
+
read: 0, // Would need platform-specific implementation
|
|
261
|
+
write: 0,
|
|
262
|
+
},
|
|
263
|
+
network: {
|
|
264
|
+
received: 0, // Would need platform-specific implementation
|
|
265
|
+
transmitted: 0,
|
|
266
|
+
},
|
|
267
|
+
process: {
|
|
268
|
+
pid: process.pid,
|
|
269
|
+
uptime: process.uptime(),
|
|
270
|
+
memoryUsage: process.memoryUsage(),
|
|
271
|
+
cpuUsage: process.cpuUsage(),
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Helper: Calculate cost based on resource usage
|
|
278
|
+
*/
|
|
279
|
+
const calculateCost = (
|
|
280
|
+
snapshot: ResourceSnapshot,
|
|
281
|
+
useSpotDiscount = false
|
|
282
|
+
): WorkerResourceUsage['estimatedCost'] => {
|
|
283
|
+
const { cpu, memory } = snapshot;
|
|
284
|
+
|
|
285
|
+
// CPU cost (based on cores and usage)
|
|
286
|
+
const cpuCostPerHour = cpu.cores * (cpu.usage / 100) * costConfig.computeCostPerCoreHour;
|
|
287
|
+
|
|
288
|
+
// Memory cost (based on GB used)
|
|
289
|
+
const memoryGB = memory.used / (1024 * 1024 * 1024);
|
|
290
|
+
const memoryCostPerHour = memoryGB * costConfig.memoryCostPerGBHour;
|
|
291
|
+
|
|
292
|
+
// Total compute cost
|
|
293
|
+
let totalCostPerHour = cpuCostPerHour + memoryCostPerHour;
|
|
294
|
+
|
|
295
|
+
// Apply spot instance discount if applicable
|
|
296
|
+
if (useSpotDiscount) {
|
|
297
|
+
totalCostPerHour *= 1 - costConfig.spotInstanceDiscount / 100;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
perHour: totalCostPerHour,
|
|
302
|
+
perDay: totalCostPerHour * 24,
|
|
303
|
+
perMonth: totalCostPerHour * 24 * 30,
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Helper: Calculate efficiency score
|
|
309
|
+
*/
|
|
310
|
+
const calculateEfficiency = (snapshot: ResourceSnapshot): WorkerResourceUsage['efficiency'] => {
|
|
311
|
+
const { cpu, memory } = snapshot;
|
|
312
|
+
|
|
313
|
+
// CPU efficiency: 50-80% usage is ideal
|
|
314
|
+
let cpuEfficiency: number;
|
|
315
|
+
if (cpu.usage < 30) {
|
|
316
|
+
cpuEfficiency = cpu.usage * 2; // Under-utilized
|
|
317
|
+
} else if (cpu.usage > 80) {
|
|
318
|
+
cpuEfficiency = 100 - (cpu.usage - 80) * 2; // Over-utilized
|
|
319
|
+
} else {
|
|
320
|
+
cpuEfficiency = 100; // Ideal range
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Memory efficiency: 50-75% usage is ideal
|
|
324
|
+
let memoryEfficiency: number;
|
|
325
|
+
if (memory.usage < 40) {
|
|
326
|
+
memoryEfficiency = memory.usage * 1.5; // Under-utilized
|
|
327
|
+
} else if (memory.usage > 75) {
|
|
328
|
+
memoryEfficiency = 100 - (memory.usage - 75) * 2; // Over-utilized
|
|
329
|
+
} else {
|
|
330
|
+
memoryEfficiency = 100; // Ideal range
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const overallScore = cpuEfficiency * 0.6 + memoryEfficiency * 0.4;
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
cpuEfficiency: Math.round(cpuEfficiency),
|
|
337
|
+
memoryEfficiency: Math.round(memoryEfficiency),
|
|
338
|
+
overallScore: Math.round(overallScore),
|
|
339
|
+
};
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Helper: Check thresholds and create alerts
|
|
344
|
+
*/
|
|
345
|
+
const checkThresholds = (
|
|
346
|
+
workerName: string,
|
|
347
|
+
snapshot: ResourceSnapshot,
|
|
348
|
+
cost: WorkerResourceUsage['estimatedCost']
|
|
349
|
+
): ResourceAlert[] => {
|
|
350
|
+
const alerts: ResourceAlert[] = [];
|
|
351
|
+
|
|
352
|
+
// CPU alerts
|
|
353
|
+
if (snapshot.cpu.usage >= THRESHOLDS.cpu.critical) {
|
|
354
|
+
alerts.push({
|
|
355
|
+
timestamp: new Date(),
|
|
356
|
+
workerName,
|
|
357
|
+
alertType: 'cpu-high',
|
|
358
|
+
severity: 'critical',
|
|
359
|
+
message: `Critical CPU usage: ${snapshot.cpu.usage.toFixed(1)}%`,
|
|
360
|
+
currentValue: snapshot.cpu.usage,
|
|
361
|
+
threshold: THRESHOLDS.cpu.critical,
|
|
362
|
+
recommendation: 'Consider scaling up or optimizing worker code',
|
|
363
|
+
});
|
|
364
|
+
} else if (snapshot.cpu.usage >= THRESHOLDS.cpu.warning) {
|
|
365
|
+
alerts.push({
|
|
366
|
+
timestamp: new Date(),
|
|
367
|
+
workerName,
|
|
368
|
+
alertType: 'cpu-high',
|
|
369
|
+
severity: 'warning',
|
|
370
|
+
message: `High CPU usage: ${snapshot.cpu.usage.toFixed(1)}%`,
|
|
371
|
+
currentValue: snapshot.cpu.usage,
|
|
372
|
+
threshold: THRESHOLDS.cpu.warning,
|
|
373
|
+
recommendation: 'Monitor closely and consider scaling',
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Memory alerts
|
|
378
|
+
if (snapshot.memory.usage >= THRESHOLDS.memory.critical) {
|
|
379
|
+
alerts.push({
|
|
380
|
+
timestamp: new Date(),
|
|
381
|
+
workerName,
|
|
382
|
+
alertType: 'memory-high',
|
|
383
|
+
severity: 'critical',
|
|
384
|
+
message: `Critical memory usage: ${snapshot.memory.usage.toFixed(1)}%`,
|
|
385
|
+
currentValue: snapshot.memory.usage,
|
|
386
|
+
threshold: THRESHOLDS.memory.critical,
|
|
387
|
+
recommendation: 'Increase memory allocation or optimize memory usage',
|
|
388
|
+
});
|
|
389
|
+
} else if (snapshot.memory.usage >= THRESHOLDS.memory.warning) {
|
|
390
|
+
alerts.push({
|
|
391
|
+
timestamp: new Date(),
|
|
392
|
+
workerName,
|
|
393
|
+
alertType: 'memory-high',
|
|
394
|
+
severity: 'warning',
|
|
395
|
+
message: `High memory usage: ${snapshot.memory.usage.toFixed(1)}%`,
|
|
396
|
+
currentValue: snapshot.memory.usage,
|
|
397
|
+
threshold: THRESHOLDS.memory.warning,
|
|
398
|
+
recommendation: 'Monitor memory consumption',
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Cost alerts
|
|
403
|
+
if (cost.perHour >= THRESHOLDS.costPerHour.critical) {
|
|
404
|
+
alerts.push({
|
|
405
|
+
timestamp: new Date(),
|
|
406
|
+
workerName,
|
|
407
|
+
alertType: 'cost-high',
|
|
408
|
+
severity: 'critical',
|
|
409
|
+
message: `Critical hourly cost: $${cost.perHour.toFixed(2)}/hr`,
|
|
410
|
+
currentValue: cost.perHour,
|
|
411
|
+
threshold: THRESHOLDS.costPerHour.critical,
|
|
412
|
+
recommendation: 'Review resource allocation and consider cost optimization',
|
|
413
|
+
});
|
|
414
|
+
} else if (cost.perHour >= THRESHOLDS.costPerHour.warning) {
|
|
415
|
+
alerts.push({
|
|
416
|
+
timestamp: new Date(),
|
|
417
|
+
workerName,
|
|
418
|
+
alertType: 'cost-high',
|
|
419
|
+
severity: 'warning',
|
|
420
|
+
message: `High hourly cost: $${cost.perHour.toFixed(2)}/hr`,
|
|
421
|
+
currentValue: cost.perHour,
|
|
422
|
+
threshold: THRESHOLDS.costPerHour.warning,
|
|
423
|
+
recommendation: 'Consider using spot instances or reducing concurrency',
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return alerts;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Helper: Store alert
|
|
432
|
+
*/
|
|
433
|
+
const storeAlert = (alert: ResourceAlert): void => {
|
|
434
|
+
let history = alertHistory.get(alert.workerName);
|
|
435
|
+
if (!history) {
|
|
436
|
+
history = [];
|
|
437
|
+
alertHistory.set(alert.workerName, history);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
history.push(alert);
|
|
441
|
+
|
|
442
|
+
// Keep only last 1000 alerts
|
|
443
|
+
if (history.length > 1000) {
|
|
444
|
+
history.shift();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
Logger.warn(`Resource alert: ${alert.workerName}`, {
|
|
448
|
+
type: alert.alertType,
|
|
449
|
+
severity: alert.severity,
|
|
450
|
+
message: alert.message,
|
|
451
|
+
recommendation: alert.recommendation,
|
|
452
|
+
});
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Helper: Calculate trend
|
|
457
|
+
*/
|
|
458
|
+
const calculateTrend = (
|
|
459
|
+
workerName: string,
|
|
460
|
+
metric: ResourceTrend['metric'],
|
|
461
|
+
period: ResourceTrend['period']
|
|
462
|
+
): ResourceTrend | null => {
|
|
463
|
+
const history = resourceHistory.get(workerName);
|
|
464
|
+
if (!history || history.length < 2) return null;
|
|
465
|
+
|
|
466
|
+
const now = Date.now();
|
|
467
|
+
let periodMs: number;
|
|
468
|
+
|
|
469
|
+
switch (period) {
|
|
470
|
+
case 'hour':
|
|
471
|
+
periodMs = 60 * 60 * 1000;
|
|
472
|
+
break;
|
|
473
|
+
case 'day':
|
|
474
|
+
periodMs = 24 * 60 * 60 * 1000;
|
|
475
|
+
break;
|
|
476
|
+
case 'week':
|
|
477
|
+
periodMs = 7 * 24 * 60 * 60 * 1000;
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Filter snapshots within period
|
|
482
|
+
const periodSnapshots = history.filter((s) => now - s.timestamp.getTime() <= periodMs);
|
|
483
|
+
if (periodSnapshots.length < 2) return null;
|
|
484
|
+
|
|
485
|
+
// Get metric values
|
|
486
|
+
const values = periodSnapshots.map((s) => {
|
|
487
|
+
switch (metric) {
|
|
488
|
+
case 'cpu':
|
|
489
|
+
return s.cpu.usage;
|
|
490
|
+
case 'memory':
|
|
491
|
+
return s.memory.usage;
|
|
492
|
+
case 'disk':
|
|
493
|
+
return s.disk.read + s.disk.write;
|
|
494
|
+
case 'network':
|
|
495
|
+
return s.network.received + s.network.transmitted;
|
|
496
|
+
case 'cost':
|
|
497
|
+
return calculateCost(s).perHour;
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Simple linear regression for trend
|
|
502
|
+
const firstValue = values[0];
|
|
503
|
+
const lastValue = values.at(-1) ?? values[0];
|
|
504
|
+
const changePercentage = ((lastValue - firstValue) / firstValue) * 100;
|
|
505
|
+
|
|
506
|
+
let trend: ResourceTrend['trend'];
|
|
507
|
+
if (Math.abs(changePercentage) < 5) {
|
|
508
|
+
trend = 'stable';
|
|
509
|
+
} else if (changePercentage > 0) {
|
|
510
|
+
trend = 'increasing';
|
|
511
|
+
} else {
|
|
512
|
+
trend = 'decreasing';
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Simple predictions (linear extrapolation)
|
|
516
|
+
const avgChange = (lastValue - firstValue) / periodSnapshots.length;
|
|
517
|
+
const predictions = {
|
|
518
|
+
nextHour: lastValue + avgChange * 12, // Assuming 5-min intervals
|
|
519
|
+
nextDay: lastValue + avgChange * 288,
|
|
520
|
+
nextWeek: lastValue + avgChange * 2016,
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
return {
|
|
524
|
+
workerName,
|
|
525
|
+
metric,
|
|
526
|
+
period,
|
|
527
|
+
trend,
|
|
528
|
+
changePercentage,
|
|
529
|
+
predictions,
|
|
530
|
+
};
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Resource Monitor - Sealed namespace
|
|
535
|
+
*/
|
|
536
|
+
export const ResourceMonitor = Object.freeze({
|
|
537
|
+
/**
|
|
538
|
+
* Initialize resource monitor
|
|
539
|
+
*/
|
|
540
|
+
initialize(config?: Partial<CostCalculationConfig>): void {
|
|
541
|
+
if (config) {
|
|
542
|
+
costConfig = { ...DEFAULT_COST_CONFIG, ...config };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
Logger.info('ResourceMonitor initialized', { costConfig });
|
|
546
|
+
},
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Check whether monitoring is running
|
|
550
|
+
*/
|
|
551
|
+
isRunning(): boolean {
|
|
552
|
+
return monitoringInterval !== null;
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Start monitoring
|
|
557
|
+
*/
|
|
558
|
+
start(intervalSeconds = 30): void {
|
|
559
|
+
const globalResourceMonitoring = Env.getBool('WORKER_RESOURCE_MONITORING', false);
|
|
560
|
+
if (!globalResourceMonitoring) {
|
|
561
|
+
Logger.warn('ResourceMonitor disabled (WORKER_RESOURCE_MONITORING=false)');
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
if (monitoringInterval) {
|
|
565
|
+
Logger.warn('ResourceMonitor already running');
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Initial snapshot
|
|
570
|
+
previousCpuUsage = process.cpuUsage();
|
|
571
|
+
previousCpuTimestamp = Date.now();
|
|
572
|
+
|
|
573
|
+
monitoringInterval = setInterval(() => {
|
|
574
|
+
const snapshot = captureSnapshot();
|
|
575
|
+
// Store snapshot for later analysis
|
|
576
|
+
// This would typically be saved to a time-series database
|
|
577
|
+
Logger.debug('Resource snapshot captured', {
|
|
578
|
+
cpu: snapshot.cpu.usage.toFixed(1) + '%',
|
|
579
|
+
memory: snapshot.memory.usage.toFixed(1) + '%',
|
|
580
|
+
});
|
|
581
|
+
}, intervalSeconds * 1000);
|
|
582
|
+
|
|
583
|
+
Logger.info('ResourceMonitor started', { intervalSeconds });
|
|
584
|
+
},
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Stop monitoring
|
|
588
|
+
*/
|
|
589
|
+
stop(): void {
|
|
590
|
+
if (monitoringInterval) {
|
|
591
|
+
clearInterval(monitoringInterval);
|
|
592
|
+
monitoringInterval = null;
|
|
593
|
+
Logger.info('ResourceMonitor stopped');
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Get current resource usage
|
|
599
|
+
*/
|
|
600
|
+
getCurrentUsage(workerName: string, useSpotDiscount = false): WorkerResourceUsage {
|
|
601
|
+
const snapshot = captureSnapshot();
|
|
602
|
+
const cost = calculateCost(snapshot, useSpotDiscount);
|
|
603
|
+
const efficiency = calculateEfficiency(snapshot);
|
|
604
|
+
|
|
605
|
+
// Store in history
|
|
606
|
+
let history = resourceHistory.get(workerName);
|
|
607
|
+
if (!history) {
|
|
608
|
+
history = [];
|
|
609
|
+
resourceHistory.set(workerName, history);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
history.push(snapshot);
|
|
613
|
+
|
|
614
|
+
// Keep only last 1000 snapshots
|
|
615
|
+
if (history.length > 1000) {
|
|
616
|
+
history.shift();
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Check thresholds
|
|
620
|
+
const alerts = checkThresholds(workerName, snapshot, cost);
|
|
621
|
+
alerts.forEach(storeAlert);
|
|
622
|
+
|
|
623
|
+
return {
|
|
624
|
+
workerName,
|
|
625
|
+
cpu: snapshot.cpu.usage,
|
|
626
|
+
memory: {
|
|
627
|
+
number: snapshot.memory.total,
|
|
628
|
+
percent: snapshot.memory.usage,
|
|
629
|
+
used: snapshot.memory.used,
|
|
630
|
+
free: snapshot.memory.free,
|
|
631
|
+
},
|
|
632
|
+
cost: {
|
|
633
|
+
hourly: cost.perHour,
|
|
634
|
+
daily: cost.perDay,
|
|
635
|
+
},
|
|
636
|
+
resourceSnapshot: snapshot,
|
|
637
|
+
estimatedCost: cost,
|
|
638
|
+
efficiency,
|
|
639
|
+
};
|
|
640
|
+
},
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Get resource history
|
|
644
|
+
*/
|
|
645
|
+
getHistory(workerName: string, limit = 100): ReadonlyArray<ResourceSnapshot> {
|
|
646
|
+
const history = resourceHistory.get(workerName) ?? [];
|
|
647
|
+
return history.slice(-limit).map((s) => ({ ...s }));
|
|
648
|
+
},
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Get alerts
|
|
652
|
+
*/
|
|
653
|
+
getAlerts(workerName: string, limit = 100): ReadonlyArray<ResourceAlert> {
|
|
654
|
+
const history = alertHistory.get(workerName) ?? [];
|
|
655
|
+
return history.slice(-limit).map((a) => ({ ...a }));
|
|
656
|
+
},
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Get trend analysis
|
|
660
|
+
*/
|
|
661
|
+
getTrend(
|
|
662
|
+
workerName: string,
|
|
663
|
+
metric: ResourceTrend['metric'],
|
|
664
|
+
period: ResourceTrend['period']
|
|
665
|
+
): ResourceTrend | null {
|
|
666
|
+
return calculateTrend(workerName, metric, period);
|
|
667
|
+
},
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Get all trends
|
|
671
|
+
*/
|
|
672
|
+
getAllTrends(
|
|
673
|
+
workerName: string,
|
|
674
|
+
period: ResourceTrend['period']
|
|
675
|
+
): Record<ResourceTrend['metric'], ResourceTrend | null> {
|
|
676
|
+
return {
|
|
677
|
+
cpu: calculateTrend(workerName, 'cpu', period),
|
|
678
|
+
memory: calculateTrend(workerName, 'memory', period),
|
|
679
|
+
disk: calculateTrend(workerName, 'disk', period),
|
|
680
|
+
network: calculateTrend(workerName, 'network', period),
|
|
681
|
+
cost: calculateTrend(workerName, 'cost', period),
|
|
682
|
+
};
|
|
683
|
+
},
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Update cost configuration
|
|
687
|
+
*/
|
|
688
|
+
updateCostConfig(config: Partial<CostCalculationConfig>): void {
|
|
689
|
+
costConfig = { ...costConfig, ...config };
|
|
690
|
+
Logger.info('Resource monitor cost config updated', { costConfig });
|
|
691
|
+
},
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Get cost configuration
|
|
695
|
+
*/
|
|
696
|
+
getCostConfig(): CostCalculationConfig {
|
|
697
|
+
return { ...costConfig };
|
|
698
|
+
},
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Calculate projected cost
|
|
702
|
+
*/
|
|
703
|
+
calculateProjectedCost(
|
|
704
|
+
cpuUsagePercent: number,
|
|
705
|
+
memoryGB: number,
|
|
706
|
+
hoursPerDay: number,
|
|
707
|
+
useSpotDiscount = false
|
|
708
|
+
): { daily: number; monthly: number; yearly: number } {
|
|
709
|
+
const cpuCores = safeCpuCount();
|
|
710
|
+
const cpuCostPerHour = cpuCores * (cpuUsagePercent / 100) * costConfig.computeCostPerCoreHour;
|
|
711
|
+
const memoryCostPerHour = memoryGB * costConfig.memoryCostPerGBHour;
|
|
712
|
+
|
|
713
|
+
let totalCostPerHour = cpuCostPerHour + memoryCostPerHour;
|
|
714
|
+
|
|
715
|
+
if (useSpotDiscount) {
|
|
716
|
+
totalCostPerHour *= 1 - costConfig.spotInstanceDiscount / 100;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return {
|
|
720
|
+
daily: totalCostPerHour * hoursPerDay,
|
|
721
|
+
monthly: totalCostPerHour * hoursPerDay * 30,
|
|
722
|
+
yearly: totalCostPerHour * hoursPerDay * 365,
|
|
723
|
+
};
|
|
724
|
+
},
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Get system information
|
|
728
|
+
*/
|
|
729
|
+
getSystemInfo(): {
|
|
730
|
+
platform: string;
|
|
731
|
+
arch: string;
|
|
732
|
+
hostname: string;
|
|
733
|
+
cpus: number;
|
|
734
|
+
totalMemory: number;
|
|
735
|
+
freeMemory: number;
|
|
736
|
+
uptime: number;
|
|
737
|
+
} {
|
|
738
|
+
return {
|
|
739
|
+
platform: safePlatform(),
|
|
740
|
+
arch: safeArch(),
|
|
741
|
+
hostname: safeHostname(),
|
|
742
|
+
cpus: safeCpuCount(),
|
|
743
|
+
totalMemory: safeTotalMemory(),
|
|
744
|
+
freeMemory: safeFreeMemory(),
|
|
745
|
+
uptime: safeUptime(),
|
|
746
|
+
};
|
|
747
|
+
},
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Clear history for a worker
|
|
751
|
+
*/
|
|
752
|
+
clearHistory(workerName: string): void {
|
|
753
|
+
resourceHistory.delete(workerName);
|
|
754
|
+
alertHistory.delete(workerName);
|
|
755
|
+
Logger.info(`Cleared resource history for ${workerName}`);
|
|
756
|
+
},
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Shutdown
|
|
760
|
+
*/
|
|
761
|
+
shutdown(): void {
|
|
762
|
+
ResourceMonitor.stop();
|
|
763
|
+
resourceHistory.clear();
|
|
764
|
+
alertHistory.clear();
|
|
765
|
+
Logger.info('ResourceMonitor shutdown complete');
|
|
766
|
+
},
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
// Graceful shutdown handled by WorkerShutdown
|