@morojs/moro 1.6.6 → 1.6.8
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 +20 -4
- package/dist/core/auth/morojs-adapter.js +17 -14
- package/dist/core/auth/morojs-adapter.js.map +1 -1
- package/dist/core/config/config-sources.js +44 -0
- package/dist/core/config/config-sources.js.map +1 -1
- package/dist/core/database/adapters/drizzle.js +5 -5
- package/dist/core/database/adapters/drizzle.js.map +1 -1
- package/dist/core/database/adapters/mongodb.js +5 -1
- package/dist/core/database/adapters/mongodb.js.map +1 -1
- package/dist/core/database/adapters/mysql.js +5 -1
- package/dist/core/database/adapters/mysql.js.map +1 -1
- package/dist/core/database/adapters/postgresql.js +1 -1
- package/dist/core/database/adapters/postgresql.js.map +1 -1
- package/dist/core/database/adapters/redis.js +2 -2
- package/dist/core/database/adapters/redis.js.map +1 -1
- package/dist/core/database/adapters/sqlite.js +5 -1
- package/dist/core/database/adapters/sqlite.js.map +1 -1
- package/dist/core/docs/index.js.map +1 -1
- package/dist/core/docs/simple-docs.js +2 -1
- package/dist/core/docs/simple-docs.js.map +1 -1
- package/dist/core/docs/swagger-ui.js +1 -0
- package/dist/core/docs/swagger-ui.js.map +1 -1
- package/dist/core/docs/zod-to-openapi.js +4 -0
- package/dist/core/docs/zod-to-openapi.js.map +1 -1
- package/dist/core/events/event-bus.d.ts +1 -1
- package/dist/core/events/event-bus.js +1 -0
- package/dist/core/events/event-bus.js.map +1 -1
- package/dist/core/framework.d.ts +1 -1
- package/dist/core/framework.js +3 -1
- package/dist/core/framework.js.map +1 -1
- package/dist/core/graphql/adapter.d.ts +73 -0
- package/dist/core/graphql/adapter.js +2 -0
- package/dist/core/graphql/adapter.js.map +1 -0
- package/dist/core/graphql/adapters/graphql-js-adapter.d.ts +26 -0
- package/dist/core/graphql/adapters/graphql-js-adapter.js +229 -0
- package/dist/core/graphql/adapters/graphql-js-adapter.js.map +1 -0
- package/dist/core/graphql/core.d.ts +60 -0
- package/dist/core/graphql/core.js +165 -0
- package/dist/core/graphql/core.js.map +1 -0
- package/dist/core/graphql/index.d.ts +4 -0
- package/dist/core/graphql/index.js +4 -0
- package/dist/core/graphql/index.js.map +1 -0
- package/dist/core/graphql/loader.d.ts +9 -0
- package/dist/core/graphql/loader.js +32 -0
- package/dist/core/graphql/loader.js.map +1 -0
- package/dist/core/graphql/types.d.ts +211 -0
- package/dist/core/graphql/types.js +2 -0
- package/dist/core/graphql/types.js.map +1 -0
- package/dist/core/http/http-server.js +31 -9
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/utils/uws-worker-clustering.d.ts +28 -0
- package/dist/core/http/utils/uws-worker-clustering.js +313 -0
- package/dist/core/http/utils/uws-worker-clustering.js.map +1 -0
- package/dist/core/http/uws-http-server.d.ts +3 -1
- package/dist/core/http/uws-http-server.js +37 -8
- package/dist/core/http/uws-http-server.js.map +1 -1
- package/dist/core/jobs/cron-parser.d.ts +62 -0
- package/dist/core/jobs/cron-parser.js +239 -0
- package/dist/core/jobs/cron-parser.js.map +1 -0
- package/dist/core/jobs/index.d.ts +12 -0
- package/dist/core/jobs/index.js +9 -0
- package/dist/core/jobs/index.js.map +1 -0
- package/dist/core/jobs/job-executor.d.ts +134 -0
- package/dist/core/jobs/job-executor.js +413 -0
- package/dist/core/jobs/job-executor.js.map +1 -0
- package/dist/core/jobs/job-scheduler.d.ts +214 -0
- package/dist/core/jobs/job-scheduler.js +551 -0
- package/dist/core/jobs/job-scheduler.js.map +1 -0
- package/dist/core/jobs/job-state-manager.d.ts +158 -0
- package/dist/core/jobs/job-state-manager.js +444 -0
- package/dist/core/jobs/job-state-manager.js.map +1 -0
- package/dist/core/jobs/leader-election.d.ts +124 -0
- package/dist/core/jobs/leader-election.js +481 -0
- package/dist/core/jobs/leader-election.js.map +1 -0
- package/dist/core/jobs/types.d.ts +151 -0
- package/dist/core/jobs/types.js +4 -0
- package/dist/core/jobs/types.js.map +1 -0
- package/dist/core/jobs/utils.d.ts +95 -0
- package/dist/core/jobs/utils.js +258 -0
- package/dist/core/jobs/utils.js.map +1 -0
- package/dist/core/logger/filters.js +2 -0
- package/dist/core/logger/filters.js.map +1 -1
- package/dist/core/logger/logger.js +7 -3
- package/dist/core/logger/logger.js.map +1 -1
- package/dist/core/logger/outputs.js +2 -0
- package/dist/core/logger/outputs.js.map +1 -1
- package/dist/core/middleware/built-in/auth/helpers.js +1 -1
- package/dist/core/middleware/built-in/auth/helpers.js.map +1 -1
- package/dist/core/middleware/built-in/auth/jwt-helpers.js +1 -1
- package/dist/core/middleware/built-in/auth/jwt-helpers.js.map +1 -1
- package/dist/core/middleware/built-in/auth/providers.js +1 -1
- package/dist/core/middleware/built-in/auth/providers.js.map +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/file.js +3 -3
- package/dist/core/middleware/built-in/cache/adapters/cache/file.js.map +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/memory.js +1 -0
- package/dist/core/middleware/built-in/cache/adapters/cache/memory.js.map +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/redis.js +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/redis.js.map +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.d.ts +8 -0
- package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js +100 -7
- package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js.map +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.d.ts +6 -0
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js +97 -13
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js.map +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js.map +1 -1
- package/dist/core/middleware/built-in/cookie/hook.d.ts +1 -1
- package/dist/core/middleware/built-in/cookie/hook.js +2 -2
- package/dist/core/middleware/built-in/cookie/hook.js.map +1 -1
- package/dist/core/middleware/built-in/csrf/core.js +1 -0
- package/dist/core/middleware/built-in/csrf/core.js.map +1 -1
- package/dist/core/middleware/built-in/graphql/core.d.ts +11 -0
- package/dist/core/middleware/built-in/graphql/core.js +24 -0
- package/dist/core/middleware/built-in/graphql/core.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/helpers.d.ts +69 -0
- package/dist/core/middleware/built-in/graphql/helpers.js +187 -0
- package/dist/core/middleware/built-in/graphql/helpers.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/hook.d.ts +7 -0
- package/dist/core/middleware/built-in/graphql/hook.js +78 -0
- package/dist/core/middleware/built-in/graphql/hook.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/index.d.ts +5 -0
- package/dist/core/middleware/built-in/graphql/index.js +5 -0
- package/dist/core/middleware/built-in/graphql/index.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/middleware.d.ts +7 -0
- package/dist/core/middleware/built-in/graphql/middleware.js +54 -0
- package/dist/core/middleware/built-in/graphql/middleware.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/subscriptions.d.ts +20 -0
- package/dist/core/middleware/built-in/graphql/subscriptions.js +37 -0
- package/dist/core/middleware/built-in/graphql/subscriptions.js.map +1 -0
- package/dist/core/middleware/built-in/index.d.ts +2 -1
- package/dist/core/middleware/built-in/index.js +3 -0
- package/dist/core/middleware/built-in/index.js.map +1 -1
- package/dist/core/middleware/built-in/validation/core.js +4 -2
- package/dist/core/middleware/built-in/validation/core.js.map +1 -1
- package/dist/core/middleware/index.js +1 -0
- package/dist/core/middleware/index.js.map +1 -1
- package/dist/core/modules/auto-discovery.js +5 -4
- package/dist/core/modules/auto-discovery.js.map +1 -1
- package/dist/core/modules/modules.js.map +1 -1
- package/dist/core/networking/adapters/socketio-adapter.js +1 -1
- package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
- package/dist/core/networking/adapters/uws-adapter.js +7 -2
- package/dist/core/networking/adapters/uws-adapter.js.map +1 -1
- package/dist/core/networking/adapters/ws-adapter.js +5 -2
- package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
- package/dist/core/networking/websocket-manager.js +2 -0
- package/dist/core/networking/websocket-manager.js.map +1 -1
- package/dist/core/pooling/object-pool-manager.js +3 -0
- package/dist/core/pooling/object-pool-manager.js.map +1 -1
- package/dist/core/routing/app-integration.d.ts +3 -3
- package/dist/core/routing/app-integration.js +1 -1
- package/dist/core/routing/app-integration.js.map +1 -1
- package/dist/core/routing/index.d.ts +1 -1
- package/dist/core/routing/index.js +1 -1
- package/dist/core/routing/index.js.map +1 -1
- package/dist/core/runtime/base-adapter.js +3 -3
- package/dist/core/runtime/base-adapter.js.map +1 -1
- package/dist/core/runtime/cloudflare-workers-adapter.js +1 -1
- package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -1
- package/dist/core/runtime/node-adapter.d.ts +1 -1
- package/dist/core/runtime/node-adapter.js +7 -4
- package/dist/core/runtime/node-adapter.js.map +1 -1
- package/dist/core/runtime/vercel-edge-adapter.js +1 -0
- package/dist/core/runtime/vercel-edge-adapter.js.map +1 -1
- package/dist/core/utilities/circuit-breaker.d.ts +9 -2
- package/dist/core/utilities/circuit-breaker.js +32 -3
- package/dist/core/utilities/circuit-breaker.js.map +1 -1
- package/dist/core/utilities/container.js +6 -0
- package/dist/core/utilities/container.js.map +1 -1
- package/dist/core/utilities/hooks.js +4 -0
- package/dist/core/utilities/hooks.js.map +1 -1
- package/dist/core/validation/index.js +6 -1
- package/dist/core/validation/index.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/moro.d.ts +154 -1
- package/dist/moro.js +588 -11
- package/dist/moro.js.map +1 -1
- package/dist/types/config.d.ts +28 -0
- package/dist/types/core.d.ts +1 -0
- package/dist/types/events.d.ts +1 -1
- package/dist/types/events.js +1 -0
- package/dist/types/events.js.map +1 -1
- package/dist/types/module.d.ts +2 -2
- package/package.json +21 -1
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
// Production-grade Job Executor
|
|
2
|
+
// Handles job execution with retry logic, timeout, circuit breaker, and memory monitoring
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import { CircuitBreaker } from '../utilities/circuit-breaker.js';
|
|
5
|
+
// Reference to global AbortController
|
|
6
|
+
const AbortControllerClass = globalThis.AbortController;
|
|
7
|
+
/**
|
|
8
|
+
* JobExecutor - Executes jobs with production-grade resilience
|
|
9
|
+
* Features:
|
|
10
|
+
* - Configurable retry with exponential backoff + jitter
|
|
11
|
+
* - Timeout enforcement
|
|
12
|
+
* - Circuit breaker integration
|
|
13
|
+
* - Memory leak detection
|
|
14
|
+
* - Graceful cancellation
|
|
15
|
+
*/
|
|
16
|
+
export class JobExecutor extends EventEmitter {
|
|
17
|
+
options;
|
|
18
|
+
logger;
|
|
19
|
+
circuitBreakers = new Map();
|
|
20
|
+
activeExecutions = new Map();
|
|
21
|
+
isShuttingDown = false;
|
|
22
|
+
constructor(logger, options = {}) {
|
|
23
|
+
super();
|
|
24
|
+
this.logger = logger;
|
|
25
|
+
// Set defaults
|
|
26
|
+
this.options = {
|
|
27
|
+
maxRetries: options.maxRetries ?? 3,
|
|
28
|
+
retryDelay: options.retryDelay ?? 1000,
|
|
29
|
+
retryBackoff: options.retryBackoff ?? 'exponential',
|
|
30
|
+
retryBackoffMultiplier: options.retryBackoffMultiplier ?? 2,
|
|
31
|
+
maxRetryDelay: options.maxRetryDelay ?? 60000,
|
|
32
|
+
timeout: options.timeout ?? 300000, // 5 minutes default
|
|
33
|
+
enableCircuitBreaker: options.enableCircuitBreaker ?? true,
|
|
34
|
+
circuitBreakerThreshold: options.circuitBreakerThreshold ?? 5,
|
|
35
|
+
circuitBreakerResetTimeout: options.circuitBreakerResetTimeout ?? 60000,
|
|
36
|
+
enableMemoryMonitoring: options.enableMemoryMonitoring ?? true,
|
|
37
|
+
memoryThreshold: options.memoryThreshold ?? 512, // 512MB default
|
|
38
|
+
};
|
|
39
|
+
this.logger.debug('JobExecutor initialized', 'JobExecutor', { options: this.options });
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Execute a job with full resilience features
|
|
43
|
+
*/
|
|
44
|
+
async execute(jobId, executionId, jobFn, context) {
|
|
45
|
+
if (this.isShuttingDown) {
|
|
46
|
+
throw new Error('JobExecutor is shutting down');
|
|
47
|
+
}
|
|
48
|
+
const ctx = context || {
|
|
49
|
+
jobId,
|
|
50
|
+
executionId,
|
|
51
|
+
attempt: 1,
|
|
52
|
+
startTime: new Date(),
|
|
53
|
+
};
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
const abortController = new AbortControllerClass();
|
|
56
|
+
this.activeExecutions.set(executionId, abortController);
|
|
57
|
+
let lastError;
|
|
58
|
+
let attempts = 0;
|
|
59
|
+
let circuitBreakerTripped = false;
|
|
60
|
+
let timedOut = false;
|
|
61
|
+
try {
|
|
62
|
+
// Check memory before execution
|
|
63
|
+
if (this.options.enableMemoryMonitoring) {
|
|
64
|
+
await this.checkMemoryUsage(jobId);
|
|
65
|
+
}
|
|
66
|
+
// Execute with retries
|
|
67
|
+
while (attempts <= this.options.maxRetries) {
|
|
68
|
+
attempts++;
|
|
69
|
+
ctx.attempt = attempts;
|
|
70
|
+
try {
|
|
71
|
+
// Check circuit breaker
|
|
72
|
+
if (this.options.enableCircuitBreaker) {
|
|
73
|
+
const breaker = this.getCircuitBreaker(jobId);
|
|
74
|
+
if (breaker.isOpen()) {
|
|
75
|
+
circuitBreakerTripped = true;
|
|
76
|
+
throw new Error(`Circuit breaker open for job ${jobId}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Execute with timeout
|
|
80
|
+
const result = await this.executeWithTimeout(jobFn, ctx, abortController.signal);
|
|
81
|
+
// Success - record in circuit breaker
|
|
82
|
+
if (this.options.enableCircuitBreaker) {
|
|
83
|
+
const breaker = this.getCircuitBreaker(jobId);
|
|
84
|
+
breaker.recordSuccess();
|
|
85
|
+
}
|
|
86
|
+
const duration = Date.now() - startTime;
|
|
87
|
+
const memoryUsed = this.getMemoryUsage();
|
|
88
|
+
this.logger.debug(`Job executed successfully: ${jobId}`, 'JobExecutor', {
|
|
89
|
+
executionId,
|
|
90
|
+
attempts,
|
|
91
|
+
duration,
|
|
92
|
+
memoryUsed,
|
|
93
|
+
});
|
|
94
|
+
this.emit('execution:success', {
|
|
95
|
+
jobId,
|
|
96
|
+
executionId,
|
|
97
|
+
attempts,
|
|
98
|
+
duration,
|
|
99
|
+
memoryUsed,
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
value: result,
|
|
104
|
+
attempts,
|
|
105
|
+
duration,
|
|
106
|
+
memoryUsed,
|
|
107
|
+
circuitBreakerTripped: false,
|
|
108
|
+
timedOut: false,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
113
|
+
// Check if it's a timeout
|
|
114
|
+
if (lastError.name === 'TimeoutError' || lastError.message.includes('timeout')) {
|
|
115
|
+
timedOut = true;
|
|
116
|
+
}
|
|
117
|
+
// Record failure in circuit breaker
|
|
118
|
+
if (this.options.enableCircuitBreaker && !circuitBreakerTripped) {
|
|
119
|
+
const breaker = this.getCircuitBreaker(jobId);
|
|
120
|
+
breaker.recordFailure();
|
|
121
|
+
}
|
|
122
|
+
this.logger.warn(`Job execution failed: ${jobId} (attempt ${attempts}/${this.options.maxRetries + 1})`, 'JobExecutor', {
|
|
123
|
+
executionId,
|
|
124
|
+
error: lastError.message,
|
|
125
|
+
timedOut,
|
|
126
|
+
circuitBreakerTripped,
|
|
127
|
+
});
|
|
128
|
+
this.emit('execution:retry', {
|
|
129
|
+
jobId,
|
|
130
|
+
executionId,
|
|
131
|
+
attempt: attempts,
|
|
132
|
+
maxAttempts: this.options.maxRetries + 1,
|
|
133
|
+
error: lastError,
|
|
134
|
+
timedOut,
|
|
135
|
+
});
|
|
136
|
+
// Don't retry if circuit breaker tripped or shutting down
|
|
137
|
+
if (circuitBreakerTripped || this.isShuttingDown) {
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
// Calculate retry delay with backoff and jitter
|
|
141
|
+
if (attempts <= this.options.maxRetries) {
|
|
142
|
+
const delay = this.calculateRetryDelay(attempts);
|
|
143
|
+
await this.sleep(delay);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// All retries exhausted
|
|
148
|
+
const duration = Date.now() - startTime;
|
|
149
|
+
this.logger.error(`Job execution failed after ${attempts} attempts: ${jobId}`, 'JobExecutor', {
|
|
150
|
+
executionId,
|
|
151
|
+
error: lastError?.message,
|
|
152
|
+
timedOut,
|
|
153
|
+
circuitBreakerTripped,
|
|
154
|
+
});
|
|
155
|
+
this.emit('execution:failed', {
|
|
156
|
+
jobId,
|
|
157
|
+
executionId,
|
|
158
|
+
attempts,
|
|
159
|
+
duration,
|
|
160
|
+
error: lastError,
|
|
161
|
+
timedOut,
|
|
162
|
+
circuitBreakerTripped,
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
success: false,
|
|
166
|
+
error: lastError,
|
|
167
|
+
attempts,
|
|
168
|
+
duration,
|
|
169
|
+
circuitBreakerTripped,
|
|
170
|
+
timedOut,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
this.activeExecutions.delete(executionId);
|
|
175
|
+
// Force GC if memory threshold exceeded
|
|
176
|
+
if (this.options.enableMemoryMonitoring) {
|
|
177
|
+
const memUsage = process.memoryUsage();
|
|
178
|
+
const heapUsedMB = memUsage.heapUsed / 1024 / 1024;
|
|
179
|
+
if (heapUsedMB > this.options.memoryThreshold * 0.9 && globalThis.gc) {
|
|
180
|
+
this.logger.warn('High memory usage detected, triggering GC', 'JobExecutor', {
|
|
181
|
+
heapUsedMB: Math.round(heapUsedMB),
|
|
182
|
+
threshold: this.options.memoryThreshold,
|
|
183
|
+
});
|
|
184
|
+
globalThis.gc();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Execute job function with timeout
|
|
191
|
+
*/
|
|
192
|
+
async executeWithTimeout(jobFn, context, signal) {
|
|
193
|
+
return new Promise((resolve, reject) => {
|
|
194
|
+
// Setup timeout
|
|
195
|
+
const timeoutId = setTimeout(() => {
|
|
196
|
+
const error = new Error(`Job execution timeout after ${this.options.timeout}ms`);
|
|
197
|
+
error.name = 'TimeoutError';
|
|
198
|
+
reject(error);
|
|
199
|
+
}, this.options.timeout);
|
|
200
|
+
// Setup abort handler
|
|
201
|
+
const abortHandler = () => {
|
|
202
|
+
clearTimeout(timeoutId);
|
|
203
|
+
reject(new Error('Job execution cancelled'));
|
|
204
|
+
};
|
|
205
|
+
if (signal.aborted) {
|
|
206
|
+
clearTimeout(timeoutId);
|
|
207
|
+
reject(new Error('Job execution cancelled'));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
signal.addEventListener('abort', abortHandler, { once: true });
|
|
211
|
+
// Execute job
|
|
212
|
+
Promise.resolve()
|
|
213
|
+
.then(() => jobFn(context))
|
|
214
|
+
.then(result => {
|
|
215
|
+
clearTimeout(timeoutId);
|
|
216
|
+
signal.removeEventListener('abort', abortHandler);
|
|
217
|
+
resolve(result);
|
|
218
|
+
})
|
|
219
|
+
.catch(error => {
|
|
220
|
+
clearTimeout(timeoutId);
|
|
221
|
+
signal.removeEventListener('abort', abortHandler);
|
|
222
|
+
reject(error);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Calculate retry delay with backoff and jitter
|
|
228
|
+
*/
|
|
229
|
+
calculateRetryDelay(attempt) {
|
|
230
|
+
let delay;
|
|
231
|
+
if (this.options.retryBackoff === 'exponential') {
|
|
232
|
+
delay = this.options.retryDelay * Math.pow(this.options.retryBackoffMultiplier, attempt - 1);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
delay = this.options.retryDelay * attempt;
|
|
236
|
+
}
|
|
237
|
+
// Cap at max delay
|
|
238
|
+
delay = Math.min(delay, this.options.maxRetryDelay);
|
|
239
|
+
// Add jitter (±20%)
|
|
240
|
+
const jitter = delay * 0.2 * (Math.random() * 2 - 1);
|
|
241
|
+
delay = Math.max(0, delay + jitter);
|
|
242
|
+
return Math.floor(delay);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Sleep helper
|
|
246
|
+
*/
|
|
247
|
+
sleep(ms) {
|
|
248
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get or create circuit breaker for job
|
|
252
|
+
*/
|
|
253
|
+
getCircuitBreaker(jobId) {
|
|
254
|
+
let breaker = this.circuitBreakers.get(jobId);
|
|
255
|
+
if (!breaker) {
|
|
256
|
+
breaker = new CircuitBreaker({
|
|
257
|
+
failureThreshold: this.options.circuitBreakerThreshold,
|
|
258
|
+
resetTimeout: this.options.circuitBreakerResetTimeout,
|
|
259
|
+
});
|
|
260
|
+
// Log circuit breaker state changes
|
|
261
|
+
breaker.on('open', () => {
|
|
262
|
+
this.logger.error(`Circuit breaker opened for job: ${jobId}`);
|
|
263
|
+
this.emit('circuit-breaker:open', { jobId });
|
|
264
|
+
});
|
|
265
|
+
breaker.on('halfOpen', () => {
|
|
266
|
+
this.logger.info(`Circuit breaker half-open for job: ${jobId}`);
|
|
267
|
+
this.emit('circuit-breaker:half-open', { jobId });
|
|
268
|
+
});
|
|
269
|
+
breaker.on('closed', () => {
|
|
270
|
+
this.logger.info(`Circuit breaker closed for job: ${jobId}`);
|
|
271
|
+
this.emit('circuit-breaker:closed', { jobId });
|
|
272
|
+
});
|
|
273
|
+
this.circuitBreakers.set(jobId, breaker);
|
|
274
|
+
}
|
|
275
|
+
return breaker;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Check memory usage before execution
|
|
279
|
+
*/
|
|
280
|
+
async checkMemoryUsage(jobId) {
|
|
281
|
+
const memUsage = process.memoryUsage();
|
|
282
|
+
const heapUsedMB = memUsage.heapUsed / 1024 / 1024;
|
|
283
|
+
if (heapUsedMB > this.options.memoryThreshold) {
|
|
284
|
+
const error = `Memory threshold exceeded: ${Math.round(heapUsedMB)}MB / ${this.options.memoryThreshold}MB`;
|
|
285
|
+
this.logger.error(error, 'JobExecutor', { jobId });
|
|
286
|
+
this.emit('memory:threshold-exceeded', {
|
|
287
|
+
jobId,
|
|
288
|
+
heapUsedMB,
|
|
289
|
+
threshold: this.options.memoryThreshold,
|
|
290
|
+
});
|
|
291
|
+
// Try to force GC if available
|
|
292
|
+
if (globalThis.gc) {
|
|
293
|
+
this.logger.warn('Forcing garbage collection', 'JobExecutor', { jobId });
|
|
294
|
+
globalThis.gc();
|
|
295
|
+
// Check again after GC
|
|
296
|
+
const newMemUsage = process.memoryUsage();
|
|
297
|
+
const newHeapUsedMB = newMemUsage.heapUsed / 1024 / 1024;
|
|
298
|
+
if (newHeapUsedMB > this.options.memoryThreshold) {
|
|
299
|
+
throw new Error(error);
|
|
300
|
+
}
|
|
301
|
+
this.logger.info('Memory recovered after GC', 'JobExecutor', {
|
|
302
|
+
before: Math.round(heapUsedMB),
|
|
303
|
+
after: Math.round(newHeapUsedMB),
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
throw new Error(error);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Get current memory usage in MB
|
|
313
|
+
*/
|
|
314
|
+
getMemoryUsage() {
|
|
315
|
+
const memUsage = process.memoryUsage();
|
|
316
|
+
return Math.round(memUsage.heapUsed / 1024 / 1024);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Cancel a running execution
|
|
320
|
+
*/
|
|
321
|
+
cancelExecution(executionId) {
|
|
322
|
+
const controller = this.activeExecutions.get(executionId);
|
|
323
|
+
if (!controller) {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
controller.abort();
|
|
327
|
+
this.logger.info(`Execution cancelled: ${executionId}`);
|
|
328
|
+
this.emit('execution:cancelled', { executionId });
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Cancel all running executions
|
|
333
|
+
*/
|
|
334
|
+
cancelAllExecutions() {
|
|
335
|
+
const executionIds = Array.from(this.activeExecutions.keys());
|
|
336
|
+
this.logger.info(`Cancelling ${executionIds.length} running executions`);
|
|
337
|
+
for (const executionId of executionIds) {
|
|
338
|
+
this.cancelExecution(executionId);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get active execution count
|
|
343
|
+
*/
|
|
344
|
+
getActiveExecutionCount() {
|
|
345
|
+
return this.activeExecutions.size;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Get circuit breaker status for job
|
|
349
|
+
*/
|
|
350
|
+
getCircuitBreakerStatus(jobId) {
|
|
351
|
+
const breaker = this.circuitBreakers.get(jobId);
|
|
352
|
+
if (!breaker) {
|
|
353
|
+
return { exists: false };
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
exists: true,
|
|
357
|
+
state: breaker.getState().toLowerCase(),
|
|
358
|
+
failures: breaker.getFailures(),
|
|
359
|
+
threshold: this.options.circuitBreakerThreshold,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Reset circuit breaker for job
|
|
364
|
+
*/
|
|
365
|
+
resetCircuitBreaker(jobId) {
|
|
366
|
+
const breaker = this.circuitBreakers.get(jobId);
|
|
367
|
+
if (!breaker) {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
breaker.reset();
|
|
371
|
+
this.logger.info(`Circuit breaker reset for job: ${jobId}`);
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Update executor options at runtime
|
|
376
|
+
*/
|
|
377
|
+
updateOptions(options) {
|
|
378
|
+
Object.assign(this.options, options);
|
|
379
|
+
this.logger.info('JobExecutor options updated', 'JobExecutor', { options });
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Get current options
|
|
383
|
+
*/
|
|
384
|
+
getOptions() {
|
|
385
|
+
return { ...this.options };
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Shutdown and cleanup
|
|
389
|
+
*/
|
|
390
|
+
async shutdown(gracePeriod = 30000) {
|
|
391
|
+
this.logger.info('JobExecutor shutting down...', 'JobExecutor', {
|
|
392
|
+
activeExecutions: this.activeExecutions.size,
|
|
393
|
+
gracePeriod,
|
|
394
|
+
});
|
|
395
|
+
this.isShuttingDown = true;
|
|
396
|
+
// Wait for active executions to complete or grace period to expire
|
|
397
|
+
const startTime = Date.now();
|
|
398
|
+
while (this.activeExecutions.size > 0 && Date.now() - startTime < gracePeriod) {
|
|
399
|
+
this.logger.debug(`Waiting for ${this.activeExecutions.size} active executions to complete`);
|
|
400
|
+
await this.sleep(1000);
|
|
401
|
+
}
|
|
402
|
+
// Force cancel remaining executions
|
|
403
|
+
if (this.activeExecutions.size > 0) {
|
|
404
|
+
this.logger.warn(`Force cancelling ${this.activeExecutions.size} remaining executions`);
|
|
405
|
+
this.cancelAllExecutions();
|
|
406
|
+
}
|
|
407
|
+
// Cleanup circuit breakers
|
|
408
|
+
this.circuitBreakers.clear();
|
|
409
|
+
this.removeAllListeners();
|
|
410
|
+
this.logger.info('JobExecutor shutdown complete');
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
//# sourceMappingURL=job-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-executor.js","sourceRoot":"","sources":["../../../src/core/jobs/job-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { Logger } from '../../types/logger.js';
|
|
3
|
+
import { JobExecution } from './job-state-manager.js';
|
|
4
|
+
import { LeaderElectionOptions } from './leader-election.js';
|
|
5
|
+
import { JobExecutorOptions, JobFunction } from './job-executor.js';
|
|
6
|
+
export type JobScheduleType = 'cron' | 'interval' | 'oneTime';
|
|
7
|
+
export interface JobSchedule {
|
|
8
|
+
type: JobScheduleType;
|
|
9
|
+
cron?: string;
|
|
10
|
+
interval?: number;
|
|
11
|
+
at?: Date;
|
|
12
|
+
}
|
|
13
|
+
export interface JobOptions {
|
|
14
|
+
name?: string;
|
|
15
|
+
schedule: JobSchedule;
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
priority?: number;
|
|
18
|
+
timezone?: string;
|
|
19
|
+
maxConcurrent?: number;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
maxRetries?: number;
|
|
22
|
+
retryDelay?: number;
|
|
23
|
+
retryBackoff?: 'linear' | 'exponential';
|
|
24
|
+
enableCircuitBreaker?: boolean;
|
|
25
|
+
metadata?: Record<string, any>;
|
|
26
|
+
onStart?: (context: ExecutionContext) => void | Promise<void>;
|
|
27
|
+
onComplete?: (context: ExecutionContext, result: any) => void | Promise<void>;
|
|
28
|
+
onError?: (context: ExecutionContext, error: Error) => void | Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export interface ExecutionContext {
|
|
31
|
+
jobId: string;
|
|
32
|
+
executionId: string;
|
|
33
|
+
attempt: number;
|
|
34
|
+
startTime: Date;
|
|
35
|
+
metadata?: Record<string, any>;
|
|
36
|
+
}
|
|
37
|
+
export interface Job {
|
|
38
|
+
id: string;
|
|
39
|
+
name: string;
|
|
40
|
+
schedule: JobSchedule;
|
|
41
|
+
fn: JobFunction;
|
|
42
|
+
options: JobOptions;
|
|
43
|
+
nextRun?: Date;
|
|
44
|
+
enabled: boolean;
|
|
45
|
+
priority: number;
|
|
46
|
+
timer?: NodeJS.Timeout;
|
|
47
|
+
concurrentExecutions: number;
|
|
48
|
+
createdAt: Date;
|
|
49
|
+
}
|
|
50
|
+
export interface JobSchedulerOptions {
|
|
51
|
+
maxConcurrentJobs?: number;
|
|
52
|
+
enableLeaderElection?: boolean;
|
|
53
|
+
leaderElection?: LeaderElectionOptions;
|
|
54
|
+
executor?: JobExecutorOptions;
|
|
55
|
+
stateManager?: {
|
|
56
|
+
persistPath?: string;
|
|
57
|
+
historySize?: number;
|
|
58
|
+
persistInterval?: number;
|
|
59
|
+
enableAutoPersist?: boolean;
|
|
60
|
+
enableRecovery?: boolean;
|
|
61
|
+
};
|
|
62
|
+
gracefulShutdownTimeout?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* JobScheduler - Production-grade job scheduling system
|
|
66
|
+
* Features:
|
|
67
|
+
* - Cron, interval, and one-time scheduling
|
|
68
|
+
* - Leader election for distributed systems
|
|
69
|
+
* - Graceful shutdown with running job completion
|
|
70
|
+
* - Global and per-job concurrency control
|
|
71
|
+
* - Priority queue for job execution
|
|
72
|
+
* - Automatic crash recovery
|
|
73
|
+
* - Full observability with events and metrics
|
|
74
|
+
*/
|
|
75
|
+
export declare class JobScheduler extends EventEmitter {
|
|
76
|
+
private jobs;
|
|
77
|
+
private stateManager;
|
|
78
|
+
private leaderElection?;
|
|
79
|
+
private executor;
|
|
80
|
+
private logger;
|
|
81
|
+
private loggerContext;
|
|
82
|
+
private started;
|
|
83
|
+
private isShuttingDown;
|
|
84
|
+
private maxConcurrentJobs;
|
|
85
|
+
private currentConcurrentJobs;
|
|
86
|
+
private pendingQueue;
|
|
87
|
+
private gracefulShutdownTimeout;
|
|
88
|
+
private enableLeaderElection;
|
|
89
|
+
constructor(logger: Logger, options?: JobSchedulerOptions);
|
|
90
|
+
/**
|
|
91
|
+
* Start the job scheduler
|
|
92
|
+
*/
|
|
93
|
+
start(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Register a new job
|
|
96
|
+
*/
|
|
97
|
+
registerJob(name: string, schedule: JobSchedule, fn: JobFunction, options?: Partial<JobOptions>): string;
|
|
98
|
+
/**
|
|
99
|
+
* Unregister a job
|
|
100
|
+
*/
|
|
101
|
+
unregisterJob(jobId: string): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Get job by ID
|
|
104
|
+
*/
|
|
105
|
+
getJob(jobId: string): Job | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* Get all jobs
|
|
108
|
+
*/
|
|
109
|
+
getAllJobs(): Job[];
|
|
110
|
+
/**
|
|
111
|
+
* Enable/disable job
|
|
112
|
+
*/
|
|
113
|
+
setJobEnabled(jobId: string, enabled: boolean): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Manually trigger job execution
|
|
116
|
+
*/
|
|
117
|
+
triggerJob(jobId: string, metadata?: Record<string, any>): Promise<ExecutionResult>;
|
|
118
|
+
/**
|
|
119
|
+
* Validate schedule configuration
|
|
120
|
+
*/
|
|
121
|
+
private validateSchedule;
|
|
122
|
+
/**
|
|
123
|
+
* Calculate next run time for job
|
|
124
|
+
*/
|
|
125
|
+
private calculateNextRun;
|
|
126
|
+
/**
|
|
127
|
+
* Schedule all jobs
|
|
128
|
+
*/
|
|
129
|
+
private scheduleAllJobs;
|
|
130
|
+
/**
|
|
131
|
+
* Schedule a single job
|
|
132
|
+
*/
|
|
133
|
+
private scheduleJob;
|
|
134
|
+
/**
|
|
135
|
+
* Unschedule a job
|
|
136
|
+
*/
|
|
137
|
+
private unscheduleJob;
|
|
138
|
+
/**
|
|
139
|
+
* Unschedule all jobs
|
|
140
|
+
*/
|
|
141
|
+
private unscheduleAllJobs;
|
|
142
|
+
/**
|
|
143
|
+
* Queue job for execution (with concurrency control)
|
|
144
|
+
*/
|
|
145
|
+
private queueJobExecution;
|
|
146
|
+
/**
|
|
147
|
+
* Execute job asynchronously
|
|
148
|
+
*/
|
|
149
|
+
private executeJobAsync;
|
|
150
|
+
/**
|
|
151
|
+
* Execute job with full lifecycle management
|
|
152
|
+
*/
|
|
153
|
+
private executeJob;
|
|
154
|
+
/**
|
|
155
|
+
* Process pending queue
|
|
156
|
+
*/
|
|
157
|
+
private processPendingQueue;
|
|
158
|
+
/**
|
|
159
|
+
* Check if this instance is the leader
|
|
160
|
+
*/
|
|
161
|
+
private isLeader;
|
|
162
|
+
/**
|
|
163
|
+
* Generate unique job ID
|
|
164
|
+
*/
|
|
165
|
+
private generateJobId;
|
|
166
|
+
/**
|
|
167
|
+
* Generate unique execution ID
|
|
168
|
+
*/
|
|
169
|
+
private generateExecutionId;
|
|
170
|
+
/**
|
|
171
|
+
* Get scheduler stats
|
|
172
|
+
*/
|
|
173
|
+
getStats(): {
|
|
174
|
+
totalJobs: number;
|
|
175
|
+
enabledJobs: number;
|
|
176
|
+
runningJobs: number;
|
|
177
|
+
queuedJobs: number;
|
|
178
|
+
isLeader: boolean;
|
|
179
|
+
isStarted: boolean;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Get job state
|
|
183
|
+
*/
|
|
184
|
+
getJobState(jobId: string): import("./job-state-manager.js").JobState | undefined;
|
|
185
|
+
/**
|
|
186
|
+
* Get job history
|
|
187
|
+
*/
|
|
188
|
+
getJobHistory(jobId: string, limit?: number): JobExecution[];
|
|
189
|
+
/**
|
|
190
|
+
* Get job metrics
|
|
191
|
+
*/
|
|
192
|
+
getJobMetrics(jobId: string): {
|
|
193
|
+
successRate: number;
|
|
194
|
+
failureRate: number;
|
|
195
|
+
averageDuration: number;
|
|
196
|
+
totalExecutions: number;
|
|
197
|
+
recentFailures: number;
|
|
198
|
+
} | null;
|
|
199
|
+
/**
|
|
200
|
+
* Graceful shutdown
|
|
201
|
+
*/
|
|
202
|
+
shutdown(): Promise<void>;
|
|
203
|
+
private sleep;
|
|
204
|
+
}
|
|
205
|
+
interface ExecutionResult {
|
|
206
|
+
success: boolean;
|
|
207
|
+
value?: any;
|
|
208
|
+
error?: Error;
|
|
209
|
+
attempts: number;
|
|
210
|
+
duration: number;
|
|
211
|
+
circuitBreakerTripped?: boolean;
|
|
212
|
+
timedOut?: boolean;
|
|
213
|
+
}
|
|
214
|
+
export {};
|