@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.
Files changed (178) hide show
  1. package/README.md +861 -0
  2. package/dist/AnomalyDetection.d.ts +102 -0
  3. package/dist/AnomalyDetection.js +321 -0
  4. package/dist/AutoScaler.d.ts +127 -0
  5. package/dist/AutoScaler.js +425 -0
  6. package/dist/BroadcastWorker.d.ts +21 -0
  7. package/dist/BroadcastWorker.js +24 -0
  8. package/dist/CanaryController.d.ts +103 -0
  9. package/dist/CanaryController.js +380 -0
  10. package/dist/ChaosEngineering.d.ts +79 -0
  11. package/dist/ChaosEngineering.js +216 -0
  12. package/dist/CircuitBreaker.d.ts +106 -0
  13. package/dist/CircuitBreaker.js +374 -0
  14. package/dist/ClusterLock.d.ts +90 -0
  15. package/dist/ClusterLock.js +385 -0
  16. package/dist/ComplianceManager.d.ts +177 -0
  17. package/dist/ComplianceManager.js +556 -0
  18. package/dist/DatacenterOrchestrator.d.ts +133 -0
  19. package/dist/DatacenterOrchestrator.js +404 -0
  20. package/dist/DeadLetterQueue.d.ts +122 -0
  21. package/dist/DeadLetterQueue.js +539 -0
  22. package/dist/HealthMonitor.d.ts +42 -0
  23. package/dist/HealthMonitor.js +301 -0
  24. package/dist/MultiQueueWorker.d.ts +89 -0
  25. package/dist/MultiQueueWorker.js +277 -0
  26. package/dist/NotificationWorker.d.ts +21 -0
  27. package/dist/NotificationWorker.js +23 -0
  28. package/dist/Observability.d.ts +153 -0
  29. package/dist/Observability.js +530 -0
  30. package/dist/PluginManager.d.ts +123 -0
  31. package/dist/PluginManager.js +392 -0
  32. package/dist/PriorityQueue.d.ts +117 -0
  33. package/dist/PriorityQueue.js +244 -0
  34. package/dist/ResourceMonitor.d.ts +164 -0
  35. package/dist/ResourceMonitor.js +605 -0
  36. package/dist/SLAMonitor.d.ts +110 -0
  37. package/dist/SLAMonitor.js +274 -0
  38. package/dist/WorkerFactory.d.ts +193 -0
  39. package/dist/WorkerFactory.js +1507 -0
  40. package/dist/WorkerInit.d.ts +85 -0
  41. package/dist/WorkerInit.js +223 -0
  42. package/dist/WorkerMetrics.d.ts +114 -0
  43. package/dist/WorkerMetrics.js +509 -0
  44. package/dist/WorkerRegistry.d.ts +145 -0
  45. package/dist/WorkerRegistry.js +319 -0
  46. package/dist/WorkerShutdown.d.ts +61 -0
  47. package/dist/WorkerShutdown.js +159 -0
  48. package/dist/WorkerVersioning.d.ts +107 -0
  49. package/dist/WorkerVersioning.js +300 -0
  50. package/dist/build-manifest.json +462 -0
  51. package/dist/config/workerConfig.d.ts +3 -0
  52. package/dist/config/workerConfig.js +19 -0
  53. package/dist/createQueueWorker.d.ts +23 -0
  54. package/dist/createQueueWorker.js +113 -0
  55. package/dist/dashboard/index.d.ts +1 -0
  56. package/dist/dashboard/index.js +1 -0
  57. package/dist/dashboard/types.d.ts +117 -0
  58. package/dist/dashboard/types.js +1 -0
  59. package/dist/dashboard/workers-api.d.ts +4 -0
  60. package/dist/dashboard/workers-api.js +638 -0
  61. package/dist/dashboard/workers-dashboard-ui.d.ts +3 -0
  62. package/dist/dashboard/workers-dashboard-ui.js +1026 -0
  63. package/dist/dashboard/workers-dashboard.d.ts +4 -0
  64. package/dist/dashboard/workers-dashboard.js +904 -0
  65. package/dist/helper/index.d.ts +5 -0
  66. package/dist/helper/index.js +10 -0
  67. package/dist/http/WorkerApiController.d.ts +38 -0
  68. package/dist/http/WorkerApiController.js +312 -0
  69. package/dist/http/WorkerController.d.ts +374 -0
  70. package/dist/http/WorkerController.js +1351 -0
  71. package/dist/http/middleware/CustomValidation.d.ts +92 -0
  72. package/dist/http/middleware/CustomValidation.js +270 -0
  73. package/dist/http/middleware/DatacenterValidator.d.ts +3 -0
  74. package/dist/http/middleware/DatacenterValidator.js +94 -0
  75. package/dist/http/middleware/EditWorkerValidation.d.ts +7 -0
  76. package/dist/http/middleware/EditWorkerValidation.js +55 -0
  77. package/dist/http/middleware/FeaturesValidator.d.ts +3 -0
  78. package/dist/http/middleware/FeaturesValidator.js +60 -0
  79. package/dist/http/middleware/InfrastructureValidator.d.ts +31 -0
  80. package/dist/http/middleware/InfrastructureValidator.js +226 -0
  81. package/dist/http/middleware/OptionsValidator.d.ts +3 -0
  82. package/dist/http/middleware/OptionsValidator.js +112 -0
  83. package/dist/http/middleware/PayloadSanitizer.d.ts +7 -0
  84. package/dist/http/middleware/PayloadSanitizer.js +42 -0
  85. package/dist/http/middleware/ProcessorPathSanitizer.d.ts +3 -0
  86. package/dist/http/middleware/ProcessorPathSanitizer.js +74 -0
  87. package/dist/http/middleware/QueueNameSanitizer.d.ts +3 -0
  88. package/dist/http/middleware/QueueNameSanitizer.js +45 -0
  89. package/dist/http/middleware/ValidateDriver.d.ts +7 -0
  90. package/dist/http/middleware/ValidateDriver.js +20 -0
  91. package/dist/http/middleware/VersionSanitizer.d.ts +3 -0
  92. package/dist/http/middleware/VersionSanitizer.js +25 -0
  93. package/dist/http/middleware/WorkerNameSanitizer.d.ts +3 -0
  94. package/dist/http/middleware/WorkerNameSanitizer.js +46 -0
  95. package/dist/http/middleware/WorkerValidationChain.d.ts +27 -0
  96. package/dist/http/middleware/WorkerValidationChain.js +185 -0
  97. package/dist/index.d.ts +46 -0
  98. package/dist/index.js +48 -0
  99. package/dist/routes/workers.d.ts +12 -0
  100. package/dist/routes/workers.js +81 -0
  101. package/dist/storage/WorkerStore.d.ts +45 -0
  102. package/dist/storage/WorkerStore.js +195 -0
  103. package/dist/type.d.ts +76 -0
  104. package/dist/type.js +1 -0
  105. package/dist/ui/router/ui.d.ts +3 -0
  106. package/dist/ui/router/ui.js +83 -0
  107. package/dist/ui/types/worker-ui.d.ts +229 -0
  108. package/dist/ui/types/worker-ui.js +5 -0
  109. package/package.json +53 -0
  110. package/src/AnomalyDetection.ts +434 -0
  111. package/src/AutoScaler.ts +654 -0
  112. package/src/BroadcastWorker.ts +34 -0
  113. package/src/CanaryController.ts +531 -0
  114. package/src/ChaosEngineering.ts +301 -0
  115. package/src/CircuitBreaker.ts +495 -0
  116. package/src/ClusterLock.ts +499 -0
  117. package/src/ComplianceManager.ts +815 -0
  118. package/src/DatacenterOrchestrator.ts +561 -0
  119. package/src/DeadLetterQueue.ts +733 -0
  120. package/src/HealthMonitor.ts +390 -0
  121. package/src/MultiQueueWorker.ts +431 -0
  122. package/src/NotificationWorker.ts +33 -0
  123. package/src/Observability.ts +696 -0
  124. package/src/PluginManager.ts +551 -0
  125. package/src/PriorityQueue.ts +351 -0
  126. package/src/ResourceMonitor.ts +769 -0
  127. package/src/SLAMonitor.ts +408 -0
  128. package/src/WorkerFactory.ts +2108 -0
  129. package/src/WorkerInit.ts +313 -0
  130. package/src/WorkerMetrics.ts +709 -0
  131. package/src/WorkerRegistry.ts +443 -0
  132. package/src/WorkerShutdown.ts +210 -0
  133. package/src/WorkerVersioning.ts +422 -0
  134. package/src/config/workerConfig.ts +25 -0
  135. package/src/createQueueWorker.ts +174 -0
  136. package/src/dashboard/index.ts +6 -0
  137. package/src/dashboard/types.ts +141 -0
  138. package/src/dashboard/workers-api.ts +785 -0
  139. package/src/dashboard/zintrust.svg +30 -0
  140. package/src/helper/index.ts +11 -0
  141. package/src/http/WorkerApiController.ts +369 -0
  142. package/src/http/WorkerController.ts +1512 -0
  143. package/src/http/middleware/CustomValidation.ts +360 -0
  144. package/src/http/middleware/DatacenterValidator.ts +124 -0
  145. package/src/http/middleware/EditWorkerValidation.ts +74 -0
  146. package/src/http/middleware/FeaturesValidator.ts +82 -0
  147. package/src/http/middleware/InfrastructureValidator.ts +295 -0
  148. package/src/http/middleware/OptionsValidator.ts +144 -0
  149. package/src/http/middleware/PayloadSanitizer.ts +52 -0
  150. package/src/http/middleware/ProcessorPathSanitizer.ts +86 -0
  151. package/src/http/middleware/QueueNameSanitizer.ts +55 -0
  152. package/src/http/middleware/ValidateDriver.ts +29 -0
  153. package/src/http/middleware/VersionSanitizer.ts +30 -0
  154. package/src/http/middleware/WorkerNameSanitizer.ts +56 -0
  155. package/src/http/middleware/WorkerValidationChain.ts +230 -0
  156. package/src/index.ts +98 -0
  157. package/src/routes/workers.ts +154 -0
  158. package/src/storage/WorkerStore.ts +240 -0
  159. package/src/type.ts +89 -0
  160. package/src/types/queue-monitor.d.ts +38 -0
  161. package/src/types/queue-redis.d.ts +38 -0
  162. package/src/ui/README.md +13 -0
  163. package/src/ui/components/JsonEditor.js +670 -0
  164. package/src/ui/components/JsonViewer.js +387 -0
  165. package/src/ui/components/WorkerCard.js +178 -0
  166. package/src/ui/components/WorkerExpandPanel.js +257 -0
  167. package/src/ui/components/fetcher.js +42 -0
  168. package/src/ui/components/sla-scorecard.js +32 -0
  169. package/src/ui/components/styles.css +30 -0
  170. package/src/ui/components/table-expander.js +34 -0
  171. package/src/ui/integration/worker-ui-integration.js +565 -0
  172. package/src/ui/router/ui.ts +99 -0
  173. package/src/ui/services/workerApi.js +240 -0
  174. package/src/ui/types/worker-ui.ts +283 -0
  175. package/src/ui/utils/jsonValidator.js +444 -0
  176. package/src/ui/workers/index.html +202 -0
  177. package/src/ui/workers/main.js +1781 -0
  178. package/src/ui/workers/styles.css +1350 -0
@@ -0,0 +1,561 @@
1
+ /**
2
+ * Datacenter Orchestration
3
+ * Multi-datacenter worker coordination with region affinity and failover
4
+ * Sealed namespace for immutability
5
+ */
6
+
7
+ import { ErrorFactory, Logger } from '@zintrust/core';
8
+ import { ClusterLock } from './ClusterLock';
9
+
10
+ export type DatacenterRegion = {
11
+ id: string;
12
+ name: string;
13
+ location: {
14
+ continent: string;
15
+ country: string;
16
+ city: string;
17
+ coordinates?: { lat: number; lng: number };
18
+ };
19
+ priority: number; // Higher = preferred
20
+ capacity: number; // Max concurrent workers
21
+ currentLoad: number; // Current active workers
22
+ healthStatus: 'healthy' | 'degraded' | 'offline';
23
+ latency: number; // Average latency in ms
24
+ costMultiplier: number; // Relative cost (1.0 = baseline)
25
+ };
26
+
27
+ export type ReplicationStrategy = 'none' | 'active-passive' | 'active-active' | 'multi-master';
28
+
29
+ export type FailoverPolicy = {
30
+ enabled: boolean;
31
+ autoFailover: boolean;
32
+ failoverThreshold: number; // Error rate threshold (0-1)
33
+ healthCheckInterval: number; // Seconds between health checks
34
+ minHealthyRegions: number; // Minimum regions that must be healthy
35
+ preferredRegions: string[]; // Prefer these regions for failover
36
+ };
37
+
38
+ export type WorkerPlacement = {
39
+ workerName: string;
40
+ primaryRegion: string;
41
+ secondaryRegions: string[];
42
+ replicationStrategy: ReplicationStrategy;
43
+ affinityRules: {
44
+ preferLocal: boolean; // Prefer local region for jobs
45
+ maxLatency?: number; // Max acceptable latency for cross-region
46
+ avoidRegions?: string[]; // Never place in these regions
47
+ };
48
+ };
49
+
50
+ export type DatacenterTopology = {
51
+ regions: DatacenterRegion[];
52
+ connections: Array<{
53
+ from: string;
54
+ to: string;
55
+ latency: number; // ms
56
+ bandwidth: number; // Mbps
57
+ }>;
58
+ };
59
+
60
+ // Internal state
61
+ const regions = new Map<string, DatacenterRegion>();
62
+ const workerPlacements = new Map<string, WorkerPlacement>();
63
+ const failoverPolicies = new Map<string, FailoverPolicy>();
64
+ const healthCheckIntervals = new Map<string, NodeJS.Timeout>();
65
+
66
+ /**
67
+ * Helper: Calculate distance between two coordinates (Haversine formula)
68
+ */
69
+ const calculateDistance = (lat1: number, lng1: number, lat2: number, lng2: number): number => {
70
+ const R = 6371; // Earth's radius in km
71
+ const dLat = ((lat2 - lat1) * Math.PI) / 180;
72
+ const dLng = ((lng2 - lng1) * Math.PI) / 180;
73
+
74
+ const a =
75
+ Math.sin(dLat / 2) * Math.sin(dLat / 2) +
76
+ Math.cos((lat1 * Math.PI) / 180) *
77
+ Math.cos((lat2 * Math.PI) / 180) *
78
+ Math.sin(dLng / 2) *
79
+ Math.sin(dLng / 2);
80
+
81
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
82
+ return R * c;
83
+ };
84
+
85
+ /**
86
+ * Helper: Find optimal region for placement
87
+ */
88
+ const findOptimalRegion = (placement: WorkerPlacement, clientRegion?: string): string | null => {
89
+ const candidateRegions = [placement.primaryRegion, ...placement.secondaryRegions];
90
+ const healthyRegions = candidateRegions.filter((regionId) => {
91
+ const region = regions.get(regionId);
92
+ return region?.healthStatus === 'healthy' && region.currentLoad < region.capacity;
93
+ });
94
+
95
+ if (healthyRegions.length === 0) {
96
+ return null;
97
+ }
98
+
99
+ // If client region specified and local preference enabled
100
+ if (
101
+ placement.affinityRules.preferLocal &&
102
+ typeof clientRegion === 'string' &&
103
+ clientRegion.length > 0
104
+ ) {
105
+ if (healthyRegions.includes(clientRegion)) {
106
+ return clientRegion;
107
+ }
108
+ }
109
+
110
+ // Sort by priority, then by current load (lower is better)
111
+ healthyRegions.sort((a, b) => {
112
+ const regionA = regions.get(a);
113
+ const regionB = regions.get(b);
114
+
115
+ if (!regionA || !regionB) {
116
+ return 0;
117
+ }
118
+
119
+ if (regionA.priority !== regionB.priority) {
120
+ return regionB.priority - regionA.priority;
121
+ }
122
+
123
+ const loadA = regionA.currentLoad / regionA.capacity;
124
+ const loadB = regionB.currentLoad / regionB.capacity;
125
+
126
+ return loadA - loadB;
127
+ });
128
+
129
+ return healthyRegions[0];
130
+ };
131
+
132
+ /**
133
+ * Helper: Perform health check for region
134
+ */
135
+ const performHealthCheck = async (regionId: string): Promise<void> => {
136
+ const region = regions.get(regionId);
137
+
138
+ if (!region) {
139
+ return;
140
+ }
141
+
142
+ try {
143
+ // Check if region can acquire lock (indicates healthy Redis connection)
144
+ const lockKey = `health:${regionId}`;
145
+ const acquired = await ClusterLock.acquire({
146
+ lockKey,
147
+ ttl: 5,
148
+ region: regionId,
149
+ userId: regionId,
150
+ });
151
+
152
+ if (acquired) {
153
+ await ClusterLock.release(lockKey, regionId);
154
+
155
+ // Update health status
156
+ if (region.healthStatus === 'offline') {
157
+ region.healthStatus = 'healthy';
158
+ Logger.info(`Region recovered: ${regionId}`);
159
+ }
160
+ }
161
+ } catch (error) {
162
+ Logger.error(`Health check failed for region: ${regionId}`, error as Error);
163
+
164
+ region.healthStatus = 'offline';
165
+
166
+ // Trigger failover if enabled
167
+ const policy = failoverPolicies.get(regionId);
168
+ if (policy?.enabled === true && policy.autoFailover) {
169
+ triggerFailover(regionId);
170
+ }
171
+ }
172
+ };
173
+
174
+ /**
175
+ * Helper: Trigger failover from unhealthy region
176
+ */
177
+ const triggerFailover = (failedRegionId: string): void => {
178
+ Logger.warn(`Triggering failover from region: ${failedRegionId}`);
179
+
180
+ // Find all workers placed in failed region
181
+ const affectedWorkers: string[] = [];
182
+
183
+ for (const [workerName, placement] of workerPlacements.entries()) {
184
+ if (placement.primaryRegion === failedRegionId) {
185
+ affectedWorkers.push(workerName);
186
+ }
187
+ }
188
+
189
+ // Reassign workers to healthy regions
190
+ for (const workerName of affectedWorkers) {
191
+ const placement = workerPlacements.get(workerName);
192
+ if (!placement) {
193
+ continue;
194
+ }
195
+ const newRegion = findOptimalRegion(placement);
196
+
197
+ if (newRegion === null) {
198
+ Logger.error(`Failover failed: No healthy region available for worker`, {
199
+ workerName,
200
+ failedRegion: failedRegionId,
201
+ });
202
+ continue;
203
+ }
204
+
205
+ Logger.info(`Failover: Moving worker from ${failedRegionId} to ${newRegion}`, {
206
+ workerName,
207
+ });
208
+
209
+ // Update placement (would trigger actual worker migration in real implementation)
210
+ placement.primaryRegion = newRegion;
211
+ }
212
+ };
213
+
214
+ /**
215
+ * Datacenter Orchestrator - Sealed namespace
216
+ */
217
+ export const DatacenterOrchestrator = Object.freeze({
218
+ /**
219
+ * Register datacenter region
220
+ */
221
+ registerRegion(region: DatacenterRegion): void {
222
+ if (regions.has(region.id)) {
223
+ throw ErrorFactory.createConfigError(`Region "${region.id}" already registered`);
224
+ }
225
+
226
+ regions.set(region.id, { ...region });
227
+
228
+ Logger.info(`Datacenter region registered: ${region.id}`, {
229
+ location: `${region.location.city}, ${region.location.country}`,
230
+ capacity: region.capacity,
231
+ });
232
+ },
233
+
234
+ /**
235
+ * Unregister datacenter region
236
+ */
237
+ unregisterRegion(regionId: string): void {
238
+ const region = regions.get(regionId);
239
+
240
+ if (!region) {
241
+ throw ErrorFactory.createNotFoundError(`Region "${regionId}" not found`);
242
+ }
243
+
244
+ // Check if any workers are still placed in this region
245
+ const hasWorkers = Array.from(workerPlacements.values()).some(
246
+ (p) => p.primaryRegion === regionId || p.secondaryRegions.includes(regionId)
247
+ );
248
+
249
+ if (hasWorkers) {
250
+ throw ErrorFactory.createValidationError(
251
+ `Cannot unregister region with active workers: ${regionId}`
252
+ );
253
+ }
254
+
255
+ regions.delete(regionId);
256
+
257
+ // Stop health checks
258
+ const interval = healthCheckIntervals.get(regionId);
259
+ if (interval) {
260
+ clearInterval(interval);
261
+ healthCheckIntervals.delete(regionId);
262
+ }
263
+
264
+ Logger.info(`Datacenter region unregistered: ${regionId}`);
265
+ },
266
+
267
+ /**
268
+ * Get region information
269
+ */
270
+ getRegion(regionId: string): DatacenterRegion | null {
271
+ const region = regions.get(regionId);
272
+ return region ? { ...region } : null;
273
+ },
274
+
275
+ /**
276
+ * List all regions
277
+ */
278
+ listRegions(healthStatus?: DatacenterRegion['healthStatus']): ReadonlyArray<DatacenterRegion> {
279
+ const allRegions = Array.from(regions.values());
280
+
281
+ if (healthStatus) {
282
+ return allRegions.filter((r) => r.healthStatus === healthStatus);
283
+ }
284
+
285
+ return allRegions;
286
+ },
287
+
288
+ /**
289
+ * Update region health status
290
+ */
291
+ updateRegionHealth(regionId: string, healthStatus: DatacenterRegion['healthStatus']): void {
292
+ const region = regions.get(regionId);
293
+
294
+ if (!region) {
295
+ throw ErrorFactory.createNotFoundError(`Region "${regionId}" not found`);
296
+ }
297
+
298
+ const oldStatus = region.healthStatus;
299
+ region.healthStatus = healthStatus;
300
+
301
+ Logger.info(`Region health updated: ${regionId}`, {
302
+ oldStatus,
303
+ newStatus: healthStatus,
304
+ });
305
+
306
+ // Trigger failover if region went offline
307
+ if (healthStatus === 'offline' && oldStatus !== 'offline') {
308
+ const policy = failoverPolicies.get(regionId);
309
+ if (policy?.enabled === true && policy.autoFailover) {
310
+ triggerFailover(regionId);
311
+ }
312
+ }
313
+ },
314
+
315
+ /**
316
+ * Update region load
317
+ */
318
+ updateRegionLoad(regionId: string, currentLoad: number): void {
319
+ const region = regions.get(regionId);
320
+
321
+ if (!region) {
322
+ throw ErrorFactory.createNotFoundError(`Region "${regionId}" not found`);
323
+ }
324
+
325
+ region.currentLoad = currentLoad;
326
+
327
+ // Check if region is overloaded
328
+ if (currentLoad > region.capacity * 0.9) {
329
+ Logger.warn(`Region approaching capacity: ${regionId}`, {
330
+ currentLoad,
331
+ capacity: region.capacity,
332
+ });
333
+
334
+ region.healthStatus = 'degraded';
335
+ } else if (region.healthStatus === 'degraded' && currentLoad < region.capacity * 0.7) {
336
+ region.healthStatus = 'healthy';
337
+ }
338
+ },
339
+
340
+ /**
341
+ * Place worker in datacenter
342
+ */
343
+ placeWorker(placement: WorkerPlacement): void {
344
+ if (workerPlacements.has(placement.workerName)) {
345
+ throw ErrorFactory.createConfigError(
346
+ `Worker "${placement.workerName}" already has a placement`
347
+ );
348
+ }
349
+
350
+ // Validate regions exist
351
+ const allRegions = [placement.primaryRegion, ...placement.secondaryRegions];
352
+ for (const regionId of allRegions) {
353
+ if (!regions.has(regionId)) {
354
+ throw ErrorFactory.createNotFoundError(`Region "${regionId}" not found`);
355
+ }
356
+ }
357
+
358
+ workerPlacements.set(placement.workerName, { ...placement });
359
+
360
+ Logger.info(`Worker placed in datacenter: ${placement.workerName}`, {
361
+ primaryRegion: placement.primaryRegion,
362
+ secondaryRegions: placement.secondaryRegions,
363
+ });
364
+ },
365
+
366
+ /**
367
+ * Get worker placement
368
+ */
369
+ getPlacement(workerName: string): WorkerPlacement | null {
370
+ const placement = workerPlacements.get(workerName);
371
+ return placement ? { ...placement } : null;
372
+ },
373
+
374
+ /**
375
+ * Update worker placement
376
+ */
377
+ updatePlacement(workerName: string, updates: Partial<WorkerPlacement>): void {
378
+ const placement = workerPlacements.get(workerName);
379
+
380
+ if (!placement) {
381
+ throw ErrorFactory.createNotFoundError(`Placement not found for worker "${workerName}"`);
382
+ }
383
+
384
+ Object.assign(placement, updates);
385
+
386
+ Logger.info(`Worker placement updated: ${workerName}`);
387
+ },
388
+
389
+ /**
390
+ * Remove worker placement
391
+ */
392
+ removeWorker(workerName: string): void {
393
+ if (!workerPlacements.has(workerName)) {
394
+ return;
395
+ }
396
+
397
+ workerPlacements.delete(workerName);
398
+
399
+ Logger.info(`Worker placement removed: ${workerName}`);
400
+ },
401
+
402
+ /**
403
+ * Find optimal region for job execution
404
+ */
405
+ findOptimalRegion(workerName: string, clientRegion?: string): string | null {
406
+ const placement = workerPlacements.get(workerName);
407
+
408
+ if (!placement) {
409
+ throw ErrorFactory.createNotFoundError(`Placement not found for worker "${workerName}"`);
410
+ }
411
+
412
+ return findOptimalRegion(placement, clientRegion);
413
+ },
414
+
415
+ /**
416
+ * Set failover policy for region
417
+ */
418
+ setFailoverPolicy(regionId: string, policy: FailoverPolicy): void {
419
+ const region = regions.get(regionId);
420
+
421
+ if (!region) {
422
+ throw ErrorFactory.createNotFoundError(`Region "${regionId}" not found`);
423
+ }
424
+
425
+ failoverPolicies.set(regionId, { ...policy });
426
+
427
+ // Start health checks if enabled
428
+ if (policy.enabled) {
429
+ DatacenterOrchestrator.startHealthChecks(regionId, policy.healthCheckInterval);
430
+ }
431
+
432
+ Logger.info(`Failover policy set for region: ${regionId}`, {
433
+ autoFailover: policy.autoFailover,
434
+ });
435
+ },
436
+
437
+ /**
438
+ * Get failover policy
439
+ */
440
+ getFailoverPolicy(regionId: string): FailoverPolicy | null {
441
+ const policy = failoverPolicies.get(regionId);
442
+ return policy ? { ...policy } : null;
443
+ },
444
+
445
+ /**
446
+ * Start health checks for region
447
+ */
448
+ startHealthChecks(regionId: string, intervalSeconds: number): void {
449
+ // Clear existing interval
450
+ const existing = healthCheckIntervals.get(regionId);
451
+ if (existing) {
452
+ clearInterval(existing);
453
+ }
454
+
455
+ // Start new interval
456
+ const interval = setInterval(() => {
457
+ performHealthCheck(regionId);
458
+ }, intervalSeconds * 1000);
459
+
460
+ healthCheckIntervals.set(regionId, interval);
461
+
462
+ Logger.info(`Health checks started for region: ${regionId}`, {
463
+ interval: intervalSeconds,
464
+ });
465
+ },
466
+
467
+ /**
468
+ * Stop health checks for region
469
+ */
470
+ stopHealthChecks(regionId: string): void {
471
+ const interval = healthCheckIntervals.get(regionId);
472
+
473
+ if (interval) {
474
+ clearInterval(interval);
475
+ healthCheckIntervals.delete(regionId);
476
+
477
+ Logger.info(`Health checks stopped for region: ${regionId}`);
478
+ }
479
+ },
480
+
481
+ /**
482
+ * Get datacenter topology
483
+ */
484
+ getTopology(): DatacenterTopology {
485
+ const regionList = Array.from(regions.values());
486
+ const connections: DatacenterTopology['connections'] = [];
487
+
488
+ // Calculate latencies between regions based on distance
489
+ for (let i = 0; i < regionList.length; i++) {
490
+ for (let j = i + 1; j < regionList.length; j++) {
491
+ const from = regionList[i];
492
+ const to = regionList[j];
493
+
494
+ if (from.location.coordinates && to.location.coordinates) {
495
+ const distance = calculateDistance(
496
+ from.location.coordinates.lat,
497
+ from.location.coordinates.lng,
498
+ to.location.coordinates.lat,
499
+ to.location.coordinates.lng
500
+ );
501
+
502
+ // Rough estimate: 1ms per 100km + base latency
503
+ const latency = Math.round(distance / 100) + 10;
504
+
505
+ connections.push({
506
+ from: from.id,
507
+ to: to.id,
508
+ latency,
509
+ bandwidth: 10000, // 10 Gbps default
510
+ });
511
+ }
512
+ }
513
+ }
514
+
515
+ return {
516
+ regions: regionList,
517
+ connections,
518
+ };
519
+ },
520
+
521
+ /**
522
+ * Get load balancing recommendation
523
+ */
524
+ getLoadBalancingRecommendation(): Array<{ regionId: string; recommendedLoad: number }> {
525
+ const regionList = Array.from(regions.values()).filter((r) => r.healthStatus === 'healthy');
526
+
527
+ const totalCapacity = regionList.reduce((sum, r) => sum + r.capacity, 0);
528
+
529
+ return regionList.map((region) => {
530
+ const idealLoad = (region.capacity / totalCapacity) * 100;
531
+ const currentLoadPercent = (region.currentLoad / region.capacity) * 100;
532
+ const recommendedAdjustment = idealLoad - currentLoadPercent;
533
+
534
+ return {
535
+ regionId: region.id,
536
+ recommendedLoad: Math.max(0, region.currentLoad + recommendedAdjustment),
537
+ };
538
+ });
539
+ },
540
+
541
+ /**
542
+ * Shutdown datacenter orchestrator
543
+ */
544
+ shutdown(): void {
545
+ Logger.info('DatacenterOrchestrator shutting down...');
546
+
547
+ // Stop all health checks
548
+ for (const interval of healthCheckIntervals.values()) {
549
+ clearInterval(interval);
550
+ }
551
+ healthCheckIntervals.clear();
552
+
553
+ regions.clear();
554
+ workerPlacements.clear();
555
+ failoverPolicies.clear();
556
+
557
+ Logger.info('DatacenterOrchestrator shutdown complete');
558
+ },
559
+ });
560
+
561
+ // Graceful shutdown handled by WorkerShutdown