@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,425 @@
1
+ /**
2
+ * Worker Auto-Scaler
3
+ * Automatic worker scaling based on queue depth, resource usage, and cost optimization
4
+ * Sealed namespace for immutability
5
+ */
6
+ import { ErrorFactory, Logger, workersConfig } from '@zintrust/core';
7
+ // Internal state
8
+ let config = null;
9
+ let scalingInterval = null;
10
+ const lastScalingDecisions = new Map();
11
+ const scalingHistory = new Map();
12
+ // Cost tracking
13
+ let currentHourlyCost = 0;
14
+ let dailyCost = 0;
15
+ let weeklyCost = 0;
16
+ let monthlyCost = 0;
17
+ const lastCostReset = {
18
+ daily: new Date(),
19
+ weekly: new Date(),
20
+ monthly: new Date(),
21
+ };
22
+ /**
23
+ * Helper: Reset cost counters if period has passed
24
+ */
25
+ const resetCostCountersIfNeeded = () => {
26
+ const now = new Date();
27
+ // Daily reset (midnight UTC)
28
+ const lastDailyReset = new Date(lastCostReset.daily);
29
+ lastDailyReset.setUTCHours(0, 0, 0, 0);
30
+ const todayMidnight = new Date(now);
31
+ todayMidnight.setUTCHours(0, 0, 0, 0);
32
+ if (todayMidnight > lastDailyReset) {
33
+ dailyCost = 0;
34
+ lastCostReset.daily = now;
35
+ Logger.info('Daily cost counter reset');
36
+ }
37
+ // Weekly reset (Sunday midnight UTC)
38
+ const dayOfWeek = now.getUTCDay();
39
+ const lastWeeklyReset = new Date(lastCostReset.weekly);
40
+ const daysSinceLastReset = Math.floor((now.getTime() - lastWeeklyReset.getTime()) / (24 * 60 * 60 * 1000));
41
+ if (daysSinceLastReset >= 7 || (dayOfWeek === 0 && now.getUTCHours() === 0)) {
42
+ weeklyCost = 0;
43
+ lastCostReset.weekly = now;
44
+ Logger.info('Weekly cost counter reset');
45
+ }
46
+ // Monthly reset (1st of month midnight UTC)
47
+ if (now.getUTCDate() === 1 && now.getUTCDate() !== lastCostReset.monthly.getUTCDate()) {
48
+ monthlyCost = 0;
49
+ lastCostReset.monthly = now;
50
+ Logger.info('Monthly cost counter reset');
51
+ }
52
+ };
53
+ /**
54
+ * Helper: Check if in off-peak period
55
+ */
56
+ const isOffPeakPeriod = (schedule) => {
57
+ if (!schedule)
58
+ return false;
59
+ const now = new Date();
60
+ const timeStr = now.toLocaleTimeString('en-US', {
61
+ timeZone: schedule.timezone,
62
+ hour12: false,
63
+ hour: '2-digit',
64
+ minute: '2-digit',
65
+ });
66
+ const [currentHour, currentMinute] = timeStr.split(':').map(Number);
67
+ const currentMinutes = currentHour * 60 + currentMinute;
68
+ const [startHour, startMinute] = schedule.start.split(':').map(Number);
69
+ const startMinutes = startHour * 60 + startMinute;
70
+ const [endHour, endMinute] = schedule.end.split(':').map(Number);
71
+ const endMinutes = endHour * 60 + endMinute;
72
+ // Handle cases where period crosses midnight
73
+ if (startMinutes > endMinutes) {
74
+ return currentMinutes >= startMinutes || currentMinutes < endMinutes;
75
+ }
76
+ return currentMinutes >= startMinutes && currentMinutes < endMinutes;
77
+ };
78
+ /**
79
+ * Helper: Calculate scaling step based on aggressiveness
80
+ */
81
+ const calculateScalingStep = (currentConcurrency, aggressiveness) => {
82
+ const baseStep = Math.max(1, Math.ceil(currentConcurrency * 0.1)); // 10% of current
83
+ switch (aggressiveness) {
84
+ case 'conservative':
85
+ return Math.max(1, Math.ceil(baseStep * 0.5)); // 5% increase
86
+ case 'moderate':
87
+ return baseStep; // 10% increase
88
+ case 'aggressive':
89
+ return Math.ceil(baseStep * 2); // 20% increase
90
+ }
91
+ };
92
+ /**
93
+ * Helper: Check if cooldown period has passed
94
+ */
95
+ const canScale = (workerName, cooldownPeriod) => {
96
+ const lastDecision = lastScalingDecisions.get(workerName);
97
+ if (!lastDecision || lastDecision.action === 'no-change') {
98
+ return true;
99
+ }
100
+ const elapsedSeconds = (Date.now() - lastDecision.timestamp.getTime()) / 1000;
101
+ return elapsedSeconds >= cooldownPeriod;
102
+ };
103
+ /**
104
+ * Helper: Check budget constraints
105
+ */
106
+ const checkBudgetConstraints = (additionalCost) => {
107
+ if (config?.costOptimization?.enabled === undefined) {
108
+ return { allowed: true };
109
+ }
110
+ resetCostCountersIfNeeded();
111
+ const { budgetAlerts } = config.costOptimization;
112
+ // Check daily limit
113
+ if (dailyCost + additionalCost > budgetAlerts.dailyLimit) {
114
+ return {
115
+ allowed: false,
116
+ reason: `Would exceed daily budget: $${(dailyCost + additionalCost).toFixed(2)} > $${budgetAlerts.dailyLimit}`,
117
+ };
118
+ }
119
+ // Check weekly limit
120
+ if (weeklyCost + additionalCost > budgetAlerts.weeklyLimit) {
121
+ return {
122
+ allowed: false,
123
+ reason: `Would exceed weekly budget: $${(weeklyCost + additionalCost).toFixed(2)} > $${budgetAlerts.weeklyLimit}`,
124
+ };
125
+ }
126
+ // Check monthly limit
127
+ if (monthlyCost + additionalCost > budgetAlerts.monthlyLimit) {
128
+ return {
129
+ allowed: false,
130
+ reason: `Would exceed monthly budget: $${(monthlyCost + additionalCost).toFixed(2)} > $${budgetAlerts.monthlyLimit}`,
131
+ };
132
+ }
133
+ return { allowed: true };
134
+ };
135
+ /**
136
+ * Helper: Make scaling decision for a worker
137
+ */
138
+ const buildDecision = (workerName, action, currentConcurrency, targetConcurrency, reason, metrics) => ({
139
+ workerName,
140
+ action,
141
+ currentConcurrency,
142
+ targetConcurrency,
143
+ reason,
144
+ metrics,
145
+ timestamp: new Date(),
146
+ });
147
+ const getOffPeakDecision = (workerName, policy, currentConcurrency, metrics) => {
148
+ if (config?.costOptimization.enabled === undefined)
149
+ return null;
150
+ const schedule = config.costOptimization.offPeakSchedule;
151
+ if (!schedule || !isOffPeakPeriod(schedule))
152
+ return null;
153
+ const reductionPercentage = schedule.reductionPercentage;
154
+ const targetConcurrency = Math.max(policy.minConcurrency, Math.ceil(currentConcurrency * (1 - reductionPercentage / 100)));
155
+ if (targetConcurrency >= currentConcurrency)
156
+ return null;
157
+ return buildDecision(workerName, 'scale-down', currentConcurrency, targetConcurrency, `Off-peak reduction: ${reductionPercentage}%`, metrics);
158
+ };
159
+ const getScaleUpDecision = (workerName, policy, currentConcurrency, metrics) => {
160
+ const shouldScaleUp = metrics.queueDepth > policy.scaleUpThreshold.queueDepth ||
161
+ metrics.cpuUsage > policy.scaleUpThreshold.cpuUsage ||
162
+ metrics.memoryUsage > policy.scaleUpThreshold.memoryUsage;
163
+ if (!shouldScaleUp || currentConcurrency >= policy.maxConcurrency)
164
+ return null;
165
+ const step = calculateScalingStep(currentConcurrency, policy.aggressiveness);
166
+ const targetConcurrency = Math.min(policy.maxConcurrency, currentConcurrency + step);
167
+ const additionalCost = metrics.costPerHour * (targetConcurrency - currentConcurrency);
168
+ const budgetCheck = checkBudgetConstraints(additionalCost);
169
+ if (!budgetCheck.allowed) {
170
+ return buildDecision(workerName, 'no-change', currentConcurrency, currentConcurrency, budgetCheck.reason ?? 'Budget constraints prevent scale-up', metrics);
171
+ }
172
+ const reasons = [];
173
+ if (metrics.queueDepth > policy.scaleUpThreshold.queueDepth) {
174
+ reasons.push(`Queue depth: ${metrics.queueDepth} > ${policy.scaleUpThreshold.queueDepth}`);
175
+ }
176
+ if (metrics.cpuUsage > policy.scaleUpThreshold.cpuUsage) {
177
+ reasons.push(`CPU usage: ${metrics.cpuUsage}% > ${policy.scaleUpThreshold.cpuUsage}%`);
178
+ }
179
+ if (metrics.memoryUsage > policy.scaleUpThreshold.memoryUsage) {
180
+ reasons.push(`Memory usage: ${metrics.memoryUsage}% > ${policy.scaleUpThreshold.memoryUsage}%`);
181
+ }
182
+ return buildDecision(workerName, 'scale-up', currentConcurrency, targetConcurrency, reasons.join('; '), metrics);
183
+ };
184
+ const getScaleDownDecision = (workerName, policy, currentConcurrency, metrics) => {
185
+ const shouldScaleDown = metrics.queueDepth < policy.scaleDownThreshold.queueDepth &&
186
+ metrics.cpuUsage < policy.scaleDownThreshold.cpuUsage &&
187
+ metrics.memoryUsage < policy.scaleDownThreshold.memoryUsage;
188
+ if (!shouldScaleDown || currentConcurrency <= policy.minConcurrency)
189
+ return null;
190
+ const step = calculateScalingStep(currentConcurrency, policy.aggressiveness);
191
+ const targetConcurrency = Math.max(policy.minConcurrency, currentConcurrency - step);
192
+ return buildDecision(workerName, 'scale-down', currentConcurrency, targetConcurrency, `Low utilization: Queue=${metrics.queueDepth}, CPU=${metrics.cpuUsage}%, Mem=${metrics.memoryUsage}%`, metrics);
193
+ };
194
+ const makeScalingDecision = (workerName, workerConfig, metrics) => {
195
+ if (!config) {
196
+ throw ErrorFactory.createGeneralError('AutoScaler not configured');
197
+ }
198
+ const policy = config.scalingPolicies.get(workerName) ?? getDefaultScalingPolicy(workerConfig);
199
+ const currentConcurrency = workerConfig.concurrency ?? 1;
200
+ if (!canScale(workerName, policy.cooldownPeriod)) {
201
+ return buildDecision(workerName, 'no-change', currentConcurrency, currentConcurrency, 'Cooldown period not elapsed', metrics);
202
+ }
203
+ const offPeakDecision = getOffPeakDecision(workerName, policy, currentConcurrency, metrics);
204
+ if (offPeakDecision)
205
+ return offPeakDecision;
206
+ const scaleUpDecision = getScaleUpDecision(workerName, policy, currentConcurrency, metrics);
207
+ if (scaleUpDecision)
208
+ return scaleUpDecision;
209
+ const scaleDownDecision = getScaleDownDecision(workerName, policy, currentConcurrency, metrics);
210
+ if (scaleDownDecision)
211
+ return scaleDownDecision;
212
+ return buildDecision(workerName, 'no-change', currentConcurrency, currentConcurrency, 'Metrics within acceptable range', metrics);
213
+ };
214
+ /**
215
+ * Helper: Get default scaling policy from worker config
216
+ */
217
+ const getDefaultScalingPolicy = (workerConfig) => {
218
+ const autoScaling = workerConfig.autoScaling;
219
+ return {
220
+ minConcurrency: autoScaling?.minConcurrency ?? 1,
221
+ maxConcurrency: autoScaling?.maxConcurrency ?? 10,
222
+ scaleUpThreshold: {
223
+ queueDepth: autoScaling?.scaleUpThreshold ?? 100,
224
+ cpuUsage: 70,
225
+ memoryUsage: 80,
226
+ },
227
+ scaleDownThreshold: {
228
+ queueDepth: autoScaling?.scaleDownThreshold ?? 10,
229
+ cpuUsage: 30,
230
+ memoryUsage: 40,
231
+ },
232
+ cooldownPeriod: autoScaling?.cooldownPeriod ?? 300, // 5 minutes
233
+ aggressiveness: 'moderate',
234
+ };
235
+ };
236
+ /**
237
+ * Helper: Record scaling decision
238
+ */
239
+ const recordScalingDecision = (decision) => {
240
+ lastScalingDecisions.set(decision.workerName, decision);
241
+ // Add to history
242
+ let history = scalingHistory.get(decision.workerName);
243
+ if (!history) {
244
+ history = [];
245
+ scalingHistory.set(decision.workerName, history);
246
+ }
247
+ history.push(decision);
248
+ // Keep only last 1000 decisions
249
+ if (history.length > 1000) {
250
+ history.shift();
251
+ }
252
+ // Update cost tracking
253
+ if (decision.action === 'scale-up') {
254
+ const additionalCost = decision.metrics.costPerHour * (decision.targetConcurrency - decision.currentConcurrency);
255
+ currentHourlyCost += additionalCost;
256
+ dailyCost += additionalCost;
257
+ weeklyCost += additionalCost;
258
+ monthlyCost += additionalCost;
259
+ }
260
+ };
261
+ /**
262
+ * Worker Auto-Scaler - Sealed namespace
263
+ */
264
+ export const AutoScaler = Object.freeze({
265
+ /**
266
+ * Initialize auto-scaler with configuration
267
+ */
268
+ initialize(autoScalerConfig) {
269
+ if (config) {
270
+ Logger.warn('AutoScaler already initialized');
271
+ return;
272
+ }
273
+ config = autoScalerConfig;
274
+ if (config.enabled) {
275
+ AutoScaler.start();
276
+ }
277
+ Logger.info('AutoScaler initialized', { enabled: config.enabled });
278
+ },
279
+ /**
280
+ * Start auto-scaling checks
281
+ */
282
+ start() {
283
+ if (!config) {
284
+ throw ErrorFactory.createConfigError('AutoScaler not initialized');
285
+ }
286
+ if (scalingInterval) {
287
+ Logger.warn('AutoScaler already running');
288
+ return;
289
+ }
290
+ if (!config.enabled) {
291
+ Logger.warn('AutoScaler is disabled in config');
292
+ return;
293
+ }
294
+ scalingInterval = setInterval(() => {
295
+ // Scaling checks will be triggered externally via evaluate()
296
+ // This interval is just a keepalive
297
+ }, config.checkInterval * 1000);
298
+ Logger.info('AutoScaler started', { checkInterval: config.checkInterval });
299
+ },
300
+ /**
301
+ * Stop auto-scaling checks
302
+ */
303
+ stop() {
304
+ if (scalingInterval) {
305
+ clearInterval(scalingInterval);
306
+ scalingInterval = null;
307
+ Logger.info('AutoScaler stopped');
308
+ }
309
+ },
310
+ /**
311
+ * Evaluate scaling decision for a worker
312
+ */
313
+ evaluate(workerName, metrics) {
314
+ if (!config) {
315
+ throw ErrorFactory.createConfigError('AutoScaler not initialized');
316
+ }
317
+ const workerConfig = workersConfig.defaultWorker;
318
+ const decision = makeScalingDecision(workerName, workerConfig, metrics);
319
+ recordScalingDecision(decision);
320
+ if (decision.action !== 'no-change') {
321
+ Logger.info(`Scaling decision for ${workerName}`, {
322
+ action: decision.action,
323
+ from: decision.currentConcurrency,
324
+ to: decision.targetConcurrency,
325
+ reason: decision.reason,
326
+ });
327
+ }
328
+ return decision;
329
+ },
330
+ /**
331
+ * Get last scaling decision
332
+ */
333
+ getLastDecision(workerName) {
334
+ return lastScalingDecisions.get(workerName) ?? null;
335
+ },
336
+ /**
337
+ * Get scaling history
338
+ */
339
+ getHistory(workerName, limit = 100) {
340
+ const history = scalingHistory.get(workerName) ?? [];
341
+ return history.slice(-limit);
342
+ },
343
+ /**
344
+ * Clear scaling history for a worker
345
+ */
346
+ clearHistory(workerName) {
347
+ lastScalingDecisions.delete(workerName);
348
+ scalingHistory.delete(workerName);
349
+ Logger.info(`Cleared auto-scaling history for ${workerName}`);
350
+ },
351
+ /**
352
+ * Get cost summary
353
+ */
354
+ getCostSummary() {
355
+ if (config?.costOptimization.enabled === undefined) {
356
+ return {
357
+ currentHourlyCost: 0,
358
+ dailyCost: 0,
359
+ weeklyCost: 0,
360
+ monthlyCost: 0,
361
+ budgetLimits: { dailyLimit: 0, weeklyLimit: 0, monthlyLimit: 0 },
362
+ utilizationPercentage: { daily: 0, weekly: 0, monthly: 0 },
363
+ };
364
+ }
365
+ resetCostCountersIfNeeded();
366
+ const { budgetAlerts } = config.costOptimization;
367
+ return {
368
+ currentHourlyCost,
369
+ dailyCost,
370
+ weeklyCost,
371
+ monthlyCost,
372
+ budgetLimits: budgetAlerts,
373
+ utilizationPercentage: {
374
+ daily: (dailyCost / budgetAlerts.dailyLimit) * 100,
375
+ weekly: (weeklyCost / budgetAlerts.weeklyLimit) * 100,
376
+ monthly: (monthlyCost / budgetAlerts.monthlyLimit) * 100,
377
+ },
378
+ };
379
+ },
380
+ /**
381
+ * Set scaling policy for a worker
382
+ */
383
+ setScalingPolicy(workerName, policy) {
384
+ if (!config) {
385
+ throw ErrorFactory.createWorkerError('AutoScaler not initialized');
386
+ }
387
+ config.scalingPolicies.set(workerName, policy);
388
+ Logger.info(`Updated scaling policy for ${workerName}`);
389
+ },
390
+ /**
391
+ * Get scaling policy for a worker
392
+ */
393
+ getScalingPolicy(workerName) {
394
+ if (!config) {
395
+ return null;
396
+ }
397
+ return config.scalingPolicies.get(workerName) ?? null;
398
+ },
399
+ /**
400
+ * Check if currently in off-peak period
401
+ */
402
+ isOffPeak() {
403
+ if (config?.costOptimization.enabled === undefined) {
404
+ return false;
405
+ }
406
+ return isOffPeakPeriod(config.costOptimization.offPeakSchedule);
407
+ },
408
+ /**
409
+ * Get configuration
410
+ */
411
+ getConfig() {
412
+ return config ? { ...config } : null;
413
+ },
414
+ /**
415
+ * Shutdown
416
+ */
417
+ shutdown() {
418
+ AutoScaler.stop();
419
+ config = null;
420
+ lastScalingDecisions.clear();
421
+ scalingHistory.clear();
422
+ Logger.info('AutoScaler shutdown complete');
423
+ },
424
+ });
425
+ // Graceful shutdown handled by WorkerShutdown
@@ -0,0 +1,21 @@
1
+ /**
2
+ * BroadcastWorker - Processes queued broadcasts
3
+ *
4
+ * This worker dequeues broadcast messages and sends them using the Broadcast service.
5
+ * Use with Queue.dequeue() in a background process or cron job.
6
+ */
7
+ export declare const BroadcastWorker: Readonly<{
8
+ processOne: (queueName?: string, driverName?: string) => Promise<boolean>;
9
+ processAll: (queueName?: string, driverName?: string) => Promise<number>;
10
+ runOnce: (opts?: {
11
+ queueName?: string;
12
+ driverName?: string;
13
+ maxItems?: number;
14
+ }) => Promise<number>;
15
+ startWorker: (opts?: {
16
+ queueName?: string;
17
+ driverName?: string;
18
+ signal?: AbortSignal;
19
+ }) => Promise<number>;
20
+ }>;
21
+ export default BroadcastWorker;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * BroadcastWorker - Processes queued broadcasts
3
+ *
4
+ * This worker dequeues broadcast messages and sends them using the Broadcast service.
5
+ * Use with Queue.dequeue() in a background process or cron job.
6
+ */
7
+ import { Broadcast } from '@zintrust/core';
8
+ import { createQueueWorker } from './createQueueWorker';
9
+ export const BroadcastWorker = Object.freeze({
10
+ ...createQueueWorker({
11
+ kindLabel: 'broadcast',
12
+ defaultQueueName: 'broadcasts',
13
+ maxAttempts: 3,
14
+ getLogFields: (payload) => ({
15
+ channel: payload.channel,
16
+ event: payload.event,
17
+ queuedAt: payload.timestamp,
18
+ }),
19
+ handle: async (payload) => {
20
+ await Broadcast.send(payload.channel, payload.event, payload.data);
21
+ },
22
+ }),
23
+ });
24
+ export default BroadcastWorker;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Canary Deployment Controller
3
+ * Gradual rollout with traffic percentage control and automatic rollback
4
+ * Sealed namespace for immutability
5
+ */
6
+ export type CanaryStage = 'initial' | 'ramping' | 'monitoring' | 'completed' | 'rolling-back' | 'failed';
7
+ export type CanaryDeploymentConfig = {
8
+ workerName: string;
9
+ currentVersion: string;
10
+ canaryVersion: string;
11
+ initialTrafficPercent: number;
12
+ targetTrafficPercent: number;
13
+ incrementPercent: number;
14
+ incrementInterval: number;
15
+ monitoringDuration: number;
16
+ errorThreshold: number;
17
+ latencyThreshold: number;
18
+ minSuccessRate: number;
19
+ autoRollback: boolean;
20
+ };
21
+ export type CanaryDeployment = {
22
+ config: CanaryDeploymentConfig;
23
+ currentTrafficPercent: number;
24
+ stage: CanaryStage;
25
+ startedAt: Date;
26
+ completedAt?: Date;
27
+ metrics: {
28
+ currentVersion: {
29
+ processed: number;
30
+ errors: number;
31
+ avgLatency: number;
32
+ };
33
+ canaryVersion: {
34
+ processed: number;
35
+ errors: number;
36
+ avgLatency: number;
37
+ };
38
+ };
39
+ history: Array<{
40
+ timestamp: Date;
41
+ trafficPercent: number;
42
+ stage: CanaryStage;
43
+ metrics: CanaryDeployment['metrics'];
44
+ decision: string;
45
+ }>;
46
+ };
47
+ /**
48
+ * Canary Deployment Controller - Sealed namespace
49
+ */
50
+ export declare const CanaryController: Readonly<{
51
+ /**
52
+ * Start canary deployment
53
+ */
54
+ start(config: CanaryDeploymentConfig): void;
55
+ /**
56
+ * Pause canary deployment
57
+ */
58
+ pause(workerName: string): void;
59
+ /**
60
+ * Resume canary deployment
61
+ */
62
+ resume(workerName: string): void;
63
+ /**
64
+ * Complete canary deployment
65
+ */
66
+ complete(workerName: string): void;
67
+ /**
68
+ * Rollback canary deployment
69
+ */
70
+ rollback(workerName: string, reason: string): void;
71
+ /**
72
+ * Get canary deployment status
73
+ */
74
+ getStatus(workerName: string): CanaryDeployment | null;
75
+ /**
76
+ * Update metrics for canary deployment
77
+ */
78
+ updateMetrics(workerName: string, version: string, processed: number, errors: number, avgLatency: number): void;
79
+ /**
80
+ * Route job to version based on traffic percentage
81
+ */
82
+ routeJob(workerName: string): string | null;
83
+ /**
84
+ * List all canary deployments
85
+ */
86
+ listDeployments(): string[];
87
+ /**
88
+ * Get deployment history
89
+ */
90
+ getHistory(workerName: string): CanaryDeployment["history"] | null;
91
+ /**
92
+ * Remove completed/failed deployment
93
+ */
94
+ remove(workerName: string): void;
95
+ /**
96
+ * Purge deployment data (force cleanup)
97
+ */
98
+ purge(workerName: string): void;
99
+ /**
100
+ * Shutdown all canary deployments
101
+ */
102
+ shutdown(): void;
103
+ }>;