@neofinancial/chrono 0.4.1-next.0 → 0.5.0
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 +10 -0
- package/build/index.d.mts +217 -0
- package/build/index.d.ts +217 -4
- package/build/index.js +352 -11
- package/build/index.js.map +1 -1
- package/build/index.mjs +325 -0
- package/build/index.mjs.map +1 -0
- package/package.json +17 -4
- package/build/backoff-strategy.d.ts +0 -64
- package/build/backoff-strategy.js +0 -86
- package/build/backoff-strategy.js.map +0 -1
- package/build/chrono.d.ts +0 -37
- package/build/chrono.js +0 -71
- package/build/chrono.js.map +0 -1
- package/build/datastore.d.ts +0 -63
- package/build/datastore.js +0 -10
- package/build/datastore.js.map +0 -1
- package/build/events.d.ts +0 -16
- package/build/events.js +0 -10
- package/build/events.js.map +0 -1
- package/build/processors/create-processor.d.ts +0 -31
- package/build/processors/create-processor.js +0 -11
- package/build/processors/create-processor.js.map +0 -1
- package/build/processors/events.d.ts +0 -50
- package/build/processors/events.js +0 -19
- package/build/processors/events.js.map +0 -1
- package/build/processors/index.d.ts +0 -3
- package/build/processors/index.js +0 -8
- package/build/processors/index.js.map +0 -1
- package/build/processors/processor.d.ts +0 -7
- package/build/processors/processor.js +0 -3
- package/build/processors/processor.js.map +0 -1
- package/build/processors/simple-processor.d.ts +0 -63
- package/build/processors/simple-processor.js +0 -161
- package/build/processors/simple-processor.js.map +0 -1
- package/build/scheduler.d.ts +0 -9
- package/build/scheduler.js +0 -21
- package/build/scheduler.js.map +0 -1
- package/build/task.d.ts +0 -33
- package/build/task.js +0 -10
- package/build/task.js.map +0 -1
- package/build/utils/promise-utils.d.ts +0 -1
- package/build/utils/promise-utils.js +0 -20
- package/build/utils/promise-utils.js.map +0 -1
package/build/index.mjs
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { EventEmitter } from "node:stream";
|
|
2
|
+
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
3
|
+
|
|
4
|
+
//#region src/events.ts
|
|
5
|
+
const ChronoEvents = {
|
|
6
|
+
STARTED: "started",
|
|
7
|
+
STOP_ABORTED: "stopAborted"
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/backoff-strategy.ts
|
|
12
|
+
/**
|
|
13
|
+
* Creates a strategy that provides no delay (immediate retry).
|
|
14
|
+
*/
|
|
15
|
+
function createNoBackoffStrategy() {
|
|
16
|
+
return (_input) => 0;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates a strategy that waits a fixed amount of time.
|
|
20
|
+
*/
|
|
21
|
+
function createFixedBackoffStrategy(config) {
|
|
22
|
+
return (_input) => config.delayMs;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a strategy where the delay increases linearly.
|
|
26
|
+
* Delay = (incrementMs * retryAttempt)
|
|
27
|
+
*/
|
|
28
|
+
function createLinearBackoffStrategy(config) {
|
|
29
|
+
const { incrementMs, baseDelayMs = 0 } = config;
|
|
30
|
+
return (input) => {
|
|
31
|
+
return baseDelayMs + input.retryAttempt * incrementMs;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates a strategy where the delay increases exponentially, potentially with jitter.
|
|
36
|
+
* Base Delay Formula = baseDelayMs * (2 ** retryAttempt)
|
|
37
|
+
*/
|
|
38
|
+
function createExponentialBackoffStrategy(config) {
|
|
39
|
+
const { baseDelayMs, maxDelayMs = Number.POSITIVE_INFINITY, jitter = "none" } = config;
|
|
40
|
+
return (input) => {
|
|
41
|
+
const exponentialDelay = baseDelayMs * 2 ** input.retryAttempt;
|
|
42
|
+
const cappedDelay = Math.min(exponentialDelay, maxDelayMs);
|
|
43
|
+
switch (jitter) {
|
|
44
|
+
case "full": return Math.floor(Math.random() * cappedDelay);
|
|
45
|
+
case "equal": {
|
|
46
|
+
const halfDelay = cappedDelay / 2;
|
|
47
|
+
return halfDelay + Math.random() * halfDelay;
|
|
48
|
+
}
|
|
49
|
+
case "none": return cappedDelay;
|
|
50
|
+
default: throw new Error("Unknown jitter type for exponential backoff strategy");
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const DEFAULT_BACKOFF_STRATEGY = {
|
|
55
|
+
type: "linear",
|
|
56
|
+
incrementMs: 2e3
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Factory function to create a backoff strategy based on type and configuration.
|
|
60
|
+
* @param options - Configuration object including the strategy type.
|
|
61
|
+
* @returns A BackoffStrategy function.
|
|
62
|
+
*/
|
|
63
|
+
function backoffStrategyFactory(options = DEFAULT_BACKOFF_STRATEGY) {
|
|
64
|
+
switch (options.type) {
|
|
65
|
+
case "none": return createNoBackoffStrategy();
|
|
66
|
+
case "fixed": return createFixedBackoffStrategy(options);
|
|
67
|
+
case "linear": return createLinearBackoffStrategy(options);
|
|
68
|
+
case "exponential": return createExponentialBackoffStrategy(options);
|
|
69
|
+
default: throw new Error("Unknown backoff strategy type");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/utils/promise-utils.ts
|
|
75
|
+
function promiseWithTimeout(promise, timeout) {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
const timer = setTimeout(() => {
|
|
78
|
+
reject(/* @__PURE__ */ new Error("Promise timed out"));
|
|
79
|
+
}, timeout);
|
|
80
|
+
promise.then((result) => {
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
resolve(result);
|
|
83
|
+
}).catch((error) => {
|
|
84
|
+
clearTimeout(timer);
|
|
85
|
+
reject(error);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/processors/events.ts
|
|
92
|
+
const ProcessorEvents = {
|
|
93
|
+
TASK_CLAIMED: "taskClaimed",
|
|
94
|
+
TASK_COMPLETED: "taskCompleted",
|
|
95
|
+
TASK_RETRY_SCHEDULED: "taskRetryScheduled",
|
|
96
|
+
TASK_FAILED: "taskFailed",
|
|
97
|
+
TASK_COMPLETION_FAILURE: "taskCompletionFailure",
|
|
98
|
+
UNKNOWN_PROCESSING_ERROR: "unknownProcessingError"
|
|
99
|
+
};
|
|
100
|
+
ProcessorEvents.TASK_CLAIMED;
|
|
101
|
+
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/processors/simple-processor.ts
|
|
104
|
+
const DEFAULT_CONFIG = {
|
|
105
|
+
maxConcurrency: 1,
|
|
106
|
+
claimIntervalMs: 50,
|
|
107
|
+
idleIntervalMs: 5e3,
|
|
108
|
+
claimStaleTimeoutMs: 1e4,
|
|
109
|
+
taskHandlerTimeoutMs: 5e3,
|
|
110
|
+
taskHandlerMaxRetries: 5,
|
|
111
|
+
processLoopRetryIntervalMs: 2e4
|
|
112
|
+
};
|
|
113
|
+
const InternalProcessorEvents = { PROCESSOR_LOOP_EXIT: "processorLoopExit" };
|
|
114
|
+
var SimpleProcessor = class extends EventEmitter {
|
|
115
|
+
config;
|
|
116
|
+
exitChannels = [];
|
|
117
|
+
stopRequested = false;
|
|
118
|
+
constructor(datastore, taskKind, handler, backOffStrategy, config) {
|
|
119
|
+
super();
|
|
120
|
+
this.datastore = datastore;
|
|
121
|
+
this.taskKind = taskKind;
|
|
122
|
+
this.handler = handler;
|
|
123
|
+
this.backOffStrategy = backOffStrategy;
|
|
124
|
+
this.config = {
|
|
125
|
+
...DEFAULT_CONFIG,
|
|
126
|
+
...config
|
|
127
|
+
};
|
|
128
|
+
this.validatedHandlerTimeout();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Validates that the task handler timeout is less than the claim stale timeout.
|
|
132
|
+
* Throws an error if the validation fails.
|
|
133
|
+
* This ensures that the task handler has enough time to complete before the task is considered stale.
|
|
134
|
+
* This is important to prevent tasks from being claimed again while they are still being processed.
|
|
135
|
+
*
|
|
136
|
+
* @throws {Error} If the task handler timeout is greater than or equal to the claim stale timeout.
|
|
137
|
+
*/
|
|
138
|
+
validatedHandlerTimeout() {
|
|
139
|
+
if (this.config.taskHandlerTimeoutMs >= this.config.claimStaleTimeoutMs) throw new Error(`Task handler timeout (${this.config.taskHandlerTimeoutMs}ms) must be less than the claim stale timeout (${this.config.claimStaleTimeoutMs}ms)`);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Starts multiple concurrent process loops that claim and process tasks.
|
|
143
|
+
* Max concurrent processes is defined by the `maxConcurrency` property set in the constructor.
|
|
144
|
+
*/
|
|
145
|
+
async start() {
|
|
146
|
+
if (this.stopRequested || this.exitChannels.length > 0) return;
|
|
147
|
+
for (let i = 0; i < this.config.maxConcurrency; i++) {
|
|
148
|
+
const exitChannel = new EventEmitter();
|
|
149
|
+
this.exitChannels.push(exitChannel);
|
|
150
|
+
this.runProcessLoop(exitChannel);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Stops the processor by signaling all process loops to exit,
|
|
155
|
+
* then waits for all process loops to finish before resolving.
|
|
156
|
+
*/
|
|
157
|
+
async stop() {
|
|
158
|
+
const exitPromises = this.exitChannels.map((channel) => new Promise((resolve) => channel.once(InternalProcessorEvents.PROCESSOR_LOOP_EXIT, () => resolve(null))));
|
|
159
|
+
this.stopRequested = true;
|
|
160
|
+
await Promise.all(exitPromises);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The main loop that processes tasks.
|
|
164
|
+
*
|
|
165
|
+
* @param exitChannel The channel to signal when the loop exits.
|
|
166
|
+
*/
|
|
167
|
+
async runProcessLoop(exitChannel) {
|
|
168
|
+
while (!this.stopRequested) try {
|
|
169
|
+
const task = await this.datastore.claim({
|
|
170
|
+
kind: this.taskKind,
|
|
171
|
+
claimStaleTimeoutMs: this.config.claimStaleTimeoutMs
|
|
172
|
+
});
|
|
173
|
+
if (!task) {
|
|
174
|
+
await setTimeout$1(this.config.idleIntervalMs);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
this.emit(ProcessorEvents.TASK_CLAIMED, {
|
|
178
|
+
task,
|
|
179
|
+
claimedAt: task.claimedAt || /* @__PURE__ */ new Date()
|
|
180
|
+
});
|
|
181
|
+
await this.handleTask(task);
|
|
182
|
+
await setTimeout$1(this.config.claimIntervalMs);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
this.emit(ProcessorEvents.UNKNOWN_PROCESSING_ERROR, {
|
|
185
|
+
error,
|
|
186
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
187
|
+
});
|
|
188
|
+
await setTimeout$1(this.config.processLoopRetryIntervalMs);
|
|
189
|
+
}
|
|
190
|
+
exitChannel.emit(InternalProcessorEvents.PROCESSOR_LOOP_EXIT);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Handles a task by calling the handler and marking it as complete or failed.
|
|
194
|
+
*
|
|
195
|
+
* Emits:
|
|
196
|
+
* - `task.completed` when the task is successfully completed.
|
|
197
|
+
* - `task.failed` when the task fails.
|
|
198
|
+
* - `task.complete.failed` when the task fails to mark as completed.
|
|
199
|
+
*
|
|
200
|
+
* @param task The task to handle.
|
|
201
|
+
*/
|
|
202
|
+
async handleTask(task) {
|
|
203
|
+
try {
|
|
204
|
+
await promiseWithTimeout(this.handler(task), this.config.taskHandlerTimeoutMs);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
await this.handleTaskError(task, error);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
const completedTask = await this.datastore.complete(task.id);
|
|
211
|
+
this.emit(ProcessorEvents.TASK_COMPLETED, {
|
|
212
|
+
task: completedTask,
|
|
213
|
+
completedAt: completedTask.completedAt || /* @__PURE__ */ new Date()
|
|
214
|
+
});
|
|
215
|
+
} catch (error) {
|
|
216
|
+
this.emit(ProcessorEvents.TASK_COMPLETION_FAILURE, {
|
|
217
|
+
error,
|
|
218
|
+
failedAt: /* @__PURE__ */ new Date(),
|
|
219
|
+
task
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async handleTaskError(task, error) {
|
|
224
|
+
const failedAt = /* @__PURE__ */ new Date();
|
|
225
|
+
if (task.retryCount >= this.config.taskHandlerMaxRetries) {
|
|
226
|
+
await this.datastore.fail(task.id);
|
|
227
|
+
this.emit(ProcessorEvents.TASK_FAILED, {
|
|
228
|
+
task,
|
|
229
|
+
error,
|
|
230
|
+
failedAt
|
|
231
|
+
});
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const delay = this.backOffStrategy({ retryAttempt: task.retryCount });
|
|
235
|
+
const retryAt = new Date(Date.now() + delay);
|
|
236
|
+
await this.datastore.retry(task.id, retryAt);
|
|
237
|
+
this.emit(ProcessorEvents.TASK_RETRY_SCHEDULED, {
|
|
238
|
+
task,
|
|
239
|
+
error,
|
|
240
|
+
errorAt: failedAt,
|
|
241
|
+
retryScheduledAt: retryAt
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/processors/create-processor.ts
|
|
248
|
+
function createProcessor(input) {
|
|
249
|
+
const backoffStrategy = backoffStrategyFactory(input.backoffStrategyOptions);
|
|
250
|
+
return new SimpleProcessor(input.datastore, input.kind, input.handler, backoffStrategy, input.configuration);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/chrono.ts
|
|
255
|
+
/**
|
|
256
|
+
* This is a type that represents the mapping of task kinds to their respective data types.
|
|
257
|
+
*
|
|
258
|
+
* Eg. shape of the TaskMapping type:
|
|
259
|
+
*
|
|
260
|
+
* type TaskMapping = {
|
|
261
|
+
* "async-messaging": { someField: number };
|
|
262
|
+
* "send-email": { url: string };
|
|
263
|
+
* };
|
|
264
|
+
*
|
|
265
|
+
*/
|
|
266
|
+
var Chrono = class extends EventEmitter {
|
|
267
|
+
datastore;
|
|
268
|
+
processors = /* @__PURE__ */ new Map();
|
|
269
|
+
exitTimeoutMs = 6e4;
|
|
270
|
+
constructor(datastore) {
|
|
271
|
+
super();
|
|
272
|
+
this.datastore = datastore;
|
|
273
|
+
}
|
|
274
|
+
async start() {
|
|
275
|
+
for (const processor of this.processors.values()) await processor.start();
|
|
276
|
+
this.emit(ChronoEvents.STARTED, { startedAt: /* @__PURE__ */ new Date() });
|
|
277
|
+
}
|
|
278
|
+
async stop() {
|
|
279
|
+
const stopPromises = Array.from(this.processors.values()).map((processor) => processor.stop());
|
|
280
|
+
try {
|
|
281
|
+
await promiseWithTimeout(Promise.all(stopPromises), this.exitTimeoutMs);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
this.emit(ChronoEvents.STOP_ABORTED, {
|
|
284
|
+
error,
|
|
285
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async scheduleTask(input) {
|
|
290
|
+
return await this.datastore.schedule({
|
|
291
|
+
when: input.when,
|
|
292
|
+
kind: input.kind,
|
|
293
|
+
data: input.data,
|
|
294
|
+
datastoreOptions: input.datastoreOptions
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async deleteTask(taskId) {
|
|
298
|
+
return await this.datastore.delete(taskId);
|
|
299
|
+
}
|
|
300
|
+
registerTaskHandler(input) {
|
|
301
|
+
if (this.processors.has(input.kind)) throw new Error("Handler for task kind already exists");
|
|
302
|
+
const processor = createProcessor({
|
|
303
|
+
kind: input.kind,
|
|
304
|
+
datastore: this.datastore,
|
|
305
|
+
handler: input.handler,
|
|
306
|
+
backoffStrategyOptions: input.backoffStrategyOptions,
|
|
307
|
+
configuration: input.processorConfiguration
|
|
308
|
+
});
|
|
309
|
+
this.processors.set(input.kind, processor);
|
|
310
|
+
return processor;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region src/datastore.ts
|
|
316
|
+
const TaskStatus = {
|
|
317
|
+
PENDING: "PENDING",
|
|
318
|
+
CLAIMED: "CLAIMED",
|
|
319
|
+
COMPLETED: "COMPLETED",
|
|
320
|
+
FAILED: "FAILED"
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
//#endregion
|
|
324
|
+
export { Chrono, ChronoEvents, ProcessorEvents, TaskStatus };
|
|
325
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["DEFAULT_BACKOFF_STRATEGY: BackoffStrategyOptions","DEFAULT_CONFIG: SimpleProcessorConfig","datastore: Datastore<TaskMapping, DatastoreOptions>","taskKind: TaskKind","handler: (task: Task<TaskKind, TaskMapping[TaskKind]>) => Promise<void>","backOffStrategy: BackoffStrategy","setTimeout"],"sources":["../src/events.ts","../src/backoff-strategy.ts","../src/utils/promise-utils.ts","../src/processors/events.ts","../src/processors/simple-processor.ts","../src/processors/create-processor.ts","../src/chrono.ts","../src/datastore.ts"],"sourcesContent":["export const ChronoEvents = {\n /** Chrono instance has started processors and begun polling tasks */\n STARTED: 'started',\n /** Chrono instance has failed to gracefully stop so shutdown has been aborted */\n STOP_ABORTED: 'stopAborted',\n} as const;\n\nexport type ChronoEvents = (typeof ChronoEvents)[keyof typeof ChronoEvents];\n\nexport type ChronoEventsMap = {\n [ChronoEvents.STARTED]: [{ startedAt: Date }];\n [ChronoEvents.STOP_ABORTED]: [{ timestamp: Date; error: unknown }];\n};\n","/** Input for calculating the next backoff delay. */\nexport type BackoffStrategyInput = {\n /** The number of retries already attempted (0 for the first retry). */\n retryAttempt: number;\n};\n\nexport type DelayMs = number;\n\n/**\n * A function that calculates the backoff delay in milliseconds.\n * @param input - Contains information like the current retry number.\n * @returns The delay duration in milliseconds.\n */\nexport type BackoffStrategy = (input: BackoffStrategyInput) => DelayMs;\n\n/**\n * Creates a strategy that provides no delay (immediate retry).\n */\nexport function createNoBackoffStrategy(): BackoffStrategy {\n return (_input: BackoffStrategyInput) => 0;\n}\n\nexport interface FixedBackoffStrategyConfig {\n /** The constant delay in milliseconds. */\n readonly delayMs: number;\n}\n/**\n * Creates a strategy that waits a fixed amount of time.\n */\nexport function createFixedBackoffStrategy(config: FixedBackoffStrategyConfig): BackoffStrategy {\n return (_input: BackoffStrategyInput) => config.delayMs;\n}\n\nexport interface LinearBackoffStrategyConfig {\n /** The base delay for all retires that will be incremented off of */\n readonly baseDelayMs?: number;\n /** The amount to increase the delay by for each subsequent retry in milliseconds. */\n readonly incrementMs: number;\n}\n\n/**\n * Creates a strategy where the delay increases linearly.\n * Delay = (incrementMs * retryAttempt)\n */\nexport function createLinearBackoffStrategy(config: LinearBackoffStrategyConfig): BackoffStrategy {\n const { incrementMs, baseDelayMs = 0 } = config;\n return (input: BackoffStrategyInput) => {\n return baseDelayMs + input.retryAttempt * incrementMs;\n };\n}\n\nexport interface ExponentialBackoffStrategyConfig {\n /** The base delay for the first retry (retryAttempt = 0) in milliseconds. */\n readonly baseDelayMs: number;\n /** The maximum delay in milliseconds. Defaults to Infinity. */\n readonly maxDelayMs?: number;\n /** Type of jitter to apply. Defaults to 'none'. */\n readonly jitter?: 'none' | 'full' | 'equal';\n}\n/**\n * Creates a strategy where the delay increases exponentially, potentially with jitter.\n * Base Delay Formula = baseDelayMs * (2 ** retryAttempt)\n */\nexport function createExponentialBackoffStrategy(config: ExponentialBackoffStrategyConfig): BackoffStrategy {\n const { baseDelayMs, maxDelayMs = Number.POSITIVE_INFINITY, jitter = 'none' } = config;\n\n return (input: BackoffStrategyInput) => {\n const exponentialDelay = baseDelayMs * 2 ** input.retryAttempt;\n const cappedDelay = Math.min(exponentialDelay, maxDelayMs);\n\n switch (jitter) {\n case 'full':\n // Full Jitter: random_between(0, cappedDelay)\n return Math.floor(Math.random() * cappedDelay);\n case 'equal': {\n // Equal Jitter: (cappedDelay / 2) + random_between(0, cappedDelay / 2)\n const halfDelay = cappedDelay / 2;\n return halfDelay + Math.random() * halfDelay;\n }\n case 'none':\n // No Jitter\n return cappedDelay;\n default: {\n // This should be caught by TypeScript if options type is correct\n const _exhaustiveCheck: never = jitter;\n throw new Error('Unknown jitter type for exponential backoff strategy');\n }\n }\n };\n}\n\nexport type BackoffStrategyType = 'none' | 'fixed' | 'linear' | 'exponential';\n\nexport type BackoffStrategyOptions =\n | { type: 'none' }\n | ({ type: 'fixed' } & FixedBackoffStrategyConfig)\n | ({ type: 'linear' } & LinearBackoffStrategyConfig)\n | ({ type: 'exponential' } & ExponentialBackoffStrategyConfig);\n\nconst DEFAULT_BACKOFF_STRATEGY: BackoffStrategyOptions = {\n type: 'linear',\n incrementMs: 2000,\n} as const;\n\n/**\n * Factory function to create a backoff strategy based on type and configuration.\n * @param options - Configuration object including the strategy type.\n * @returns A BackoffStrategy function.\n */\nexport function backoffStrategyFactory(options: BackoffStrategyOptions = DEFAULT_BACKOFF_STRATEGY): BackoffStrategy {\n switch (options.type) {\n case 'none':\n return createNoBackoffStrategy();\n case 'fixed':\n // No need to destructure 'type', pass the rest\n return createFixedBackoffStrategy(options);\n case 'linear':\n return createLinearBackoffStrategy(options);\n case 'exponential':\n return createExponentialBackoffStrategy(options);\n default: {\n // This should be caught by TypeScript if options type is correct\n const _exhaustiveCheck: never = options;\n throw new Error('Unknown backoff strategy type');\n }\n }\n}\n","export function promiseWithTimeout<T>(promise: Promise<T>, timeout: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error('Promise timed out'));\n }, timeout);\n\n promise\n .then((result) => {\n clearTimeout(timer);\n resolve(result);\n })\n .catch((error) => {\n clearTimeout(timer);\n reject(error);\n });\n });\n}\n","import type { Task, TaskMappingBase } from '..';\n\nexport const ProcessorEvents = {\n /** A task has been claimed by the running processor for handling */\n TASK_CLAIMED: 'taskClaimed',\n /** A task has completed processing and successfully marked as completed */\n TASK_COMPLETED: 'taskCompleted',\n /** A task has failed during processing and being scheduled for retry */\n TASK_RETRY_SCHEDULED: 'taskRetryScheduled',\n /** A task has been marked as FAILED due to process failures exceeding max retries */\n TASK_FAILED: 'taskFailed',\n /** A task has been successfully processed but underlying data store failed to mark task as completed. Duplicate processing expected */\n TASK_COMPLETION_FAILURE: 'taskCompletionFailure',\n /** An unknown and uncaught exception occurred in processor. Processing paused for processLoopRetryIntervalMs before continuing */\n UNKNOWN_PROCESSING_ERROR: 'unknownProcessingError',\n} as const;\n\nexport type ProcessorEvents = (typeof ProcessorEvents)[keyof typeof ProcessorEvents];\n\nexport type ProcessorEventsMap<TaskKind extends keyof TaskMapping, TaskMapping extends TaskMappingBase> = {\n [ProcessorEvents.TASK_CLAIMED]: [{ task: Task<TaskKind, TaskMapping[TaskKind]>; claimedAt: Date }];\n [ProcessorEvents.TASK_COMPLETED]: [{ task: Task<TaskKind, TaskMapping[TaskKind]>; completedAt: Date }];\n [ProcessorEvents.TASK_RETRY_SCHEDULED]: [\n { task: Task<TaskKind, TaskMapping[TaskKind]>; error: unknown; retryScheduledAt: Date; errorAt: Date },\n ];\n [ProcessorEvents.TASK_FAILED]: [{ task: Task<TaskKind, TaskMapping[TaskKind]>; error: unknown; failedAt: Date }];\n [ProcessorEvents.TASK_COMPLETION_FAILURE]: [\n { task: Task<TaskKind, TaskMapping[TaskKind]>; error: unknown; failedAt: Date },\n ];\n [ProcessorEvents.UNKNOWN_PROCESSING_ERROR]: [{ error: unknown; timestamp: Date }];\n};\n\nProcessorEvents.TASK_CLAIMED;\n","import { EventEmitter } from 'node:stream';\nimport { setTimeout } from 'node:timers/promises';\n\nimport type { BackoffStrategy } from '../backoff-strategy';\nimport type { TaskMappingBase } from '../chrono';\nimport type { Datastore, Task } from '../datastore';\nimport { promiseWithTimeout } from '../utils/promise-utils';\nimport { ProcessorEvents, type ProcessorEventsMap } from './events';\nimport type { Processor } from './processor';\n\nconst DEFAULT_CONFIG: SimpleProcessorConfig = {\n maxConcurrency: 1,\n claimIntervalMs: 50,\n idleIntervalMs: 5_000,\n claimStaleTimeoutMs: 10_000,\n taskHandlerTimeoutMs: 5_000,\n taskHandlerMaxRetries: 5,\n processLoopRetryIntervalMs: 20_000,\n};\n\ntype SimpleProcessorConfig = {\n maxConcurrency: number;\n claimIntervalMs: number;\n claimStaleTimeoutMs: number;\n idleIntervalMs: number;\n taskHandlerTimeoutMs: number;\n taskHandlerMaxRetries: number;\n processLoopRetryIntervalMs: number;\n};\n\nconst InternalProcessorEvents = { PROCESSOR_LOOP_EXIT: 'processorLoopExit' } as const;\n\ntype InternalProcessorEventsMap = {\n [InternalProcessorEvents.PROCESSOR_LOOP_EXIT]: [];\n};\n\nexport class SimpleProcessor<\n TaskKind extends Extract<keyof TaskMapping, string>,\n TaskMapping extends TaskMappingBase,\n DatastoreOptions,\n >\n extends EventEmitter<ProcessorEventsMap<TaskKind, TaskMapping>>\n implements Processor<TaskKind, TaskMapping>\n{\n private config: SimpleProcessorConfig;\n\n private exitChannels: EventEmitter<InternalProcessorEventsMap>[] = [];\n private stopRequested = false;\n\n constructor(\n private datastore: Datastore<TaskMapping, DatastoreOptions>,\n private taskKind: TaskKind,\n private handler: (task: Task<TaskKind, TaskMapping[TaskKind]>) => Promise<void>,\n private backOffStrategy: BackoffStrategy,\n config?: Partial<SimpleProcessorConfig>,\n ) {\n super();\n\n this.config = {\n ...DEFAULT_CONFIG,\n ...config,\n };\n\n this.validatedHandlerTimeout();\n }\n\n /**\n * Validates that the task handler timeout is less than the claim stale timeout.\n * Throws an error if the validation fails.\n * This ensures that the task handler has enough time to complete before the task is considered stale.\n * This is important to prevent tasks from being claimed again while they are still being processed.\n *\n * @throws {Error} If the task handler timeout is greater than or equal to the claim stale timeout.\n */\n private validatedHandlerTimeout() {\n if (this.config.taskHandlerTimeoutMs >= this.config.claimStaleTimeoutMs) {\n throw new Error(\n `Task handler timeout (${this.config.taskHandlerTimeoutMs}ms) must be less than the claim stale timeout (${this.config.claimStaleTimeoutMs}ms)`,\n );\n }\n }\n\n /**\n * Starts multiple concurrent process loops that claim and process tasks.\n * Max concurrent processes is defined by the `maxConcurrency` property set in the constructor.\n */\n async start(): Promise<void> {\n if (this.stopRequested || this.exitChannels.length > 0) {\n return;\n }\n\n for (let i = 0; i < this.config.maxConcurrency; i++) {\n const exitChannel = new EventEmitter<InternalProcessorEventsMap>();\n\n this.exitChannels.push(exitChannel);\n this.runProcessLoop(exitChannel);\n }\n }\n\n /**\n * Stops the processor by signaling all process loops to exit,\n * then waits for all process loops to finish before resolving.\n */\n async stop(): Promise<void> {\n const exitPromises = this.exitChannels.map(\n (channel) =>\n new Promise((resolve) => channel.once(InternalProcessorEvents.PROCESSOR_LOOP_EXIT, () => resolve(null))),\n );\n\n this.stopRequested = true;\n\n await Promise.all(exitPromises);\n }\n\n /**\n * The main loop that processes tasks.\n *\n * @param exitChannel The channel to signal when the loop exits.\n */\n private async runProcessLoop(exitChannel: EventEmitter<InternalProcessorEventsMap>): Promise<void> {\n while (!this.stopRequested) {\n try {\n const task = await this.datastore.claim({\n kind: this.taskKind,\n claimStaleTimeoutMs: this.config.claimStaleTimeoutMs,\n });\n\n // If no tasks are available, wait before trying again\n if (!task) {\n await setTimeout(this.config.idleIntervalMs);\n\n continue;\n }\n\n this.emit(ProcessorEvents.TASK_CLAIMED, { task, claimedAt: task.claimedAt || new Date() });\n\n // Process the task using the handler\n await this.handleTask(task);\n\n // Wait a bit before claiming the next task\n await setTimeout(this.config.claimIntervalMs);\n } catch (error) {\n this.emit(ProcessorEvents.UNKNOWN_PROCESSING_ERROR, { error, timestamp: new Date() });\n\n await setTimeout(this.config.processLoopRetryIntervalMs);\n }\n }\n\n exitChannel.emit(InternalProcessorEvents.PROCESSOR_LOOP_EXIT);\n }\n\n /**\n * Handles a task by calling the handler and marking it as complete or failed.\n *\n * Emits:\n * - `task.completed` when the task is successfully completed.\n * - `task.failed` when the task fails.\n * - `task.complete.failed` when the task fails to mark as completed.\n *\n * @param task The task to handle.\n */\n private async handleTask(task: Task<TaskKind, TaskMapping[TaskKind]>) {\n try {\n await promiseWithTimeout(this.handler(task), this.config.taskHandlerTimeoutMs);\n } catch (error) {\n await this.handleTaskError(task, error as Error);\n\n return;\n }\n\n try {\n const completedTask = await this.datastore.complete<TaskKind>(task.id);\n\n this.emit(ProcessorEvents.TASK_COMPLETED, {\n task: completedTask,\n completedAt: completedTask.completedAt || new Date(),\n });\n } catch (error) {\n this.emit(ProcessorEvents.TASK_COMPLETION_FAILURE, {\n error: error,\n failedAt: new Date(),\n task,\n });\n }\n }\n\n private async handleTaskError(task: Task<TaskKind, TaskMapping[TaskKind]>, error: Error): Promise<void> {\n const failedAt = new Date();\n if (task.retryCount >= this.config.taskHandlerMaxRetries) {\n // Mark the task as failed\n await this.datastore.fail(task.id);\n this.emit(ProcessorEvents.TASK_FAILED, {\n task,\n error,\n failedAt,\n });\n\n return;\n }\n\n const delay = this.backOffStrategy({ retryAttempt: task.retryCount });\n const retryAt = new Date(Date.now() + delay);\n\n await this.datastore.retry(task.id, retryAt);\n this.emit(ProcessorEvents.TASK_RETRY_SCHEDULED, {\n task,\n error,\n errorAt: failedAt,\n retryScheduledAt: retryAt,\n });\n }\n}\n","import type { Datastore, Task } from 'datastore';\nimport type { TaskMappingBase } from '..';\nimport { type BackoffStrategyOptions, backoffStrategyFactory } from '../backoff-strategy';\nimport type { Processor } from './processor';\nimport { SimpleProcessor } from './simple-processor';\n\n/**\n * Configuration for the processor.\n */\nexport type ProcessorConfiguration = {\n /** The maximum number of concurrent tasks that the processor will use when processing. @default 1 */\n maxConcurrency?: number;\n /** The interval at which the processor will wait before next poll when the previous poll returned a task @default 50ms */\n claimIntervalMs?: number;\n /** The interval at which the processor will wait before next poll when no tasks are available for processing @default 5000ms */\n idleIntervalMs?: number;\n /** The maximum time a task can be claimed for processing before it will be considered stale and claimed again @default 10000ms */\n claimStaleTimeoutMs?: number;\n /** The maximum time a task handler can take to complete before it will be considered timed out @default 5000ms */\n taskHandlerTimeoutMs?: number;\n /** The maximum number of retries for a task handler, before task is marked as failed. @default 5 */\n taskHandlerMaxRetries?: number;\n /** The interval at which the processor will wait before next poll when an unexpected error occurs @default 20000ms */\n processLoopRetryIntervalMs?: number;\n};\n\nexport type CreateProcessorInput<\n TaskKind extends keyof TaskMapping,\n TaskMapping extends TaskMappingBase,\n DatastoreOptions,\n> = {\n kind: TaskKind;\n datastore: Datastore<TaskMapping, DatastoreOptions>;\n handler: (task: Task<TaskKind, TaskMapping[TaskKind]>) => Promise<void>;\n configuration?: ProcessorConfiguration;\n backoffStrategyOptions?: BackoffStrategyOptions;\n};\n\nexport function createProcessor<\n TaskKind extends Extract<keyof TaskMapping, string>,\n TaskMapping extends TaskMappingBase,\n DatastoreOptions,\n>(input: CreateProcessorInput<TaskKind, TaskMapping, DatastoreOptions>): Processor<TaskKind, TaskMapping> {\n const backoffStrategy = backoffStrategyFactory(input.backoffStrategyOptions);\n // add more processors here\n return new SimpleProcessor<TaskKind, TaskMapping, DatastoreOptions>(\n input.datastore,\n input.kind,\n input.handler,\n backoffStrategy,\n input.configuration,\n );\n}\n","import { EventEmitter } from 'node:stream';\n\nimport type { BackoffStrategyOptions } from './backoff-strategy';\nimport type { Datastore, ScheduleInput, Task } from './datastore';\nimport { ChronoEvents, type ChronoEventsMap } from './events';\nimport { createProcessor, type Processor } from './processors';\nimport type { ProcessorConfiguration } from './processors/create-processor';\nimport type { ProcessorEventsMap } from './processors/events';\nimport { promiseWithTimeout } from './utils/promise-utils';\n\nexport type TaskMappingBase = Record<string, unknown>;\n\nexport type ScheduleTaskInput<TaskKind, TaskData, DatastoreOptions> = ScheduleInput<\n TaskKind,\n TaskData,\n DatastoreOptions\n>;\n\nexport type RegisterTaskHandlerInput<TaskKind, TaskData> = {\n kind: TaskKind;\n handler: (task: Task<TaskKind, TaskData>) => Promise<void>;\n backoffStrategyOptions?: BackoffStrategyOptions;\n processorConfiguration?: ProcessorConfiguration;\n};\n\nexport type RegisterTaskHandlerResponse<\n TaskKind extends keyof TaskMapping,\n TaskMapping extends TaskMappingBase,\n> = EventEmitter<ProcessorEventsMap<TaskKind, TaskMapping>>;\n\n/**\n * This is a type that represents the mapping of task kinds to their respective data types.\n *\n * Eg. shape of the TaskMapping type:\n *\n * type TaskMapping = {\n * \"async-messaging\": { someField: number };\n * \"send-email\": { url: string };\n * };\n *\n */\n\nexport class Chrono<TaskMapping extends TaskMappingBase, DatastoreOptions> extends EventEmitter<ChronoEventsMap> {\n private readonly datastore: Datastore<TaskMapping, DatastoreOptions>;\n private readonly processors: Map<keyof TaskMapping, Processor<keyof TaskMapping, TaskMapping>> = new Map();\n\n readonly exitTimeoutMs = 60_000;\n\n constructor(datastore: Datastore<TaskMapping, DatastoreOptions>) {\n super();\n\n this.datastore = datastore;\n }\n\n public async start(): Promise<void> {\n for (const processor of this.processors.values()) {\n await processor.start();\n }\n\n this.emit(ChronoEvents.STARTED, { startedAt: new Date() });\n }\n\n public async stop(): Promise<void> {\n const stopPromises = Array.from(this.processors.values()).map((processor) => processor.stop());\n\n try {\n await promiseWithTimeout(Promise.all(stopPromises), this.exitTimeoutMs);\n } catch (error) {\n this.emit(ChronoEvents.STOP_ABORTED, { error, timestamp: new Date() });\n }\n }\n\n public async scheduleTask<TaskKind extends keyof TaskMapping>(\n input: ScheduleTaskInput<TaskKind, TaskMapping[TaskKind], DatastoreOptions>,\n ): Promise<Task<TaskKind, TaskMapping[TaskKind]>> {\n const task = await this.datastore.schedule({\n when: input.when,\n kind: input.kind,\n data: input.data,\n datastoreOptions: input.datastoreOptions,\n });\n\n return task;\n }\n\n public async deleteTask<TaskKind extends keyof TaskMapping>(\n taskId: string,\n ): Promise<Task<TaskKind, TaskMapping[TaskKind]> | undefined> {\n const task = await this.datastore.delete<TaskKind>(taskId);\n\n return task;\n }\n\n public registerTaskHandler<TaskKind extends Extract<keyof TaskMapping, string>>(\n input: RegisterTaskHandlerInput<TaskKind, TaskMapping[TaskKind]>,\n ): RegisterTaskHandlerResponse<TaskKind, TaskMapping> {\n if (this.processors.has(input.kind)) {\n throw new Error('Handler for task kind already exists');\n }\n\n const processor = createProcessor({\n kind: input.kind,\n datastore: this.datastore,\n handler: input.handler,\n backoffStrategyOptions: input.backoffStrategyOptions,\n configuration: input.processorConfiguration,\n });\n\n this.processors.set(input.kind, processor);\n\n return processor;\n }\n}\n","import type { TaskMappingBase } from './chrono';\n\nexport const TaskStatus = {\n PENDING: 'PENDING',\n CLAIMED: 'CLAIMED',\n COMPLETED: 'COMPLETED',\n FAILED: 'FAILED',\n} as const;\nexport type TaskStatus = (typeof TaskStatus)[keyof typeof TaskStatus];\n\nexport type Task<TaskKind, TaskData> = {\n /** A unique identifier for the task */\n id: string;\n /** A human-readable name or type for the task */\n kind: TaskKind;\n /** The current status of the task */\n status: TaskStatus;\n /** The payload or data associated with the task */\n data: TaskData;\n /** The priority level of the task (lower numbers can indicate higher priority) */\n priority?: number;\n /** A key used for idempotency to prevent duplicate processing */\n idempotencyKey?: string;\n /** The original scheduled date when the task was first intended to run */\n originalScheduleDate: Date;\n /** The current scheduled execution date, which may change if rescheduled */\n scheduledAt: Date;\n /** The date the task is mark 'claimed */\n claimedAt?: Date;\n /** The date the task is mark 'completed' */\n completedAt?: Date;\n /** The date when the task was last executed (if any) */\n lastExecutedAt?: Date;\n /** A counter to track the number of times the task has been retried */\n retryCount: number;\n};\n\nexport type ScheduleInput<TaskKind, TaskData, DatastoreOptions> = {\n when: Date;\n kind: TaskKind;\n data: TaskData;\n priority?: number;\n idempotencyKey?: string;\n datastoreOptions?: DatastoreOptions;\n};\n\nexport type ClaimTaskInput<TaskKind> = {\n kind: TaskKind;\n claimStaleTimeoutMs: number;\n};\n\nexport type DeleteByIdempotencyKeyInput<TaskKind> = {\n kind: TaskKind;\n idempotencyKey: string;\n};\n\nexport type DeleteOptions = {\n force?: boolean;\n};\n\nexport type DeleteInput<TaskKind> = DeleteByIdempotencyKeyInput<TaskKind> | string;\n\nexport interface Datastore<TaskMapping extends TaskMappingBase, DatastoreOptions> {\n schedule<TaskKind extends keyof TaskMapping>(\n input: ScheduleInput<TaskKind, TaskMapping[TaskKind], DatastoreOptions>,\n ): Promise<Task<TaskKind, TaskMapping[TaskKind]>>;\n delete<TaskKind extends keyof TaskMapping>(\n taskId: string,\n options?: DeleteOptions,\n ): Promise<Task<TaskKind, TaskMapping[TaskKind]> | undefined>;\n delete<TaskKind extends keyof TaskMapping>(\n key: DeleteByIdempotencyKeyInput<TaskKind>,\n options?: DeleteOptions,\n ): Promise<Task<TaskKind, TaskMapping[TaskKind]> | undefined>;\n claim<TaskKind extends Extract<keyof TaskMapping, string>>(\n input: ClaimTaskInput<TaskKind>,\n ): Promise<Task<TaskKind, TaskMapping[TaskKind]> | undefined>;\n retry<TaskKind extends keyof TaskMapping>(\n taskId: string,\n retryAt: Date,\n ): Promise<Task<TaskKind, TaskMapping[TaskKind]>>;\n complete<TaskKind extends keyof TaskMapping>(taskId: string): Promise<Task<TaskKind, TaskMapping[TaskKind]>>;\n fail<TaskKind extends keyof TaskMapping>(taskId: string): Promise<Task<TaskKind, TaskMapping[TaskKind]>>;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neofinancial/chrono",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Core package for Chrono task scheduling system",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -11,8 +11,21 @@
|
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "https://github.com/neofinancial/chrono.git"
|
|
13
13
|
},
|
|
14
|
-
"main": "build/index.js",
|
|
15
|
-
"
|
|
14
|
+
"main": "./build/index.js",
|
|
15
|
+
"module": "./build/index.mjs",
|
|
16
|
+
"types": "./build/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": {
|
|
20
|
+
"types": "./build/index.d.mts",
|
|
21
|
+
"default": "./build/index.mjs"
|
|
22
|
+
},
|
|
23
|
+
"require": {
|
|
24
|
+
"types": "./build/index.d.ts",
|
|
25
|
+
"default": "./build/index.js"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
16
29
|
"files": [
|
|
17
30
|
"build/**",
|
|
18
31
|
"README.md"
|
|
@@ -22,7 +35,7 @@
|
|
|
22
35
|
"license": "MIT",
|
|
23
36
|
"scripts": {
|
|
24
37
|
"clean": "rimraf ./build",
|
|
25
|
-
"build": "
|
|
38
|
+
"build": "tsdown",
|
|
26
39
|
"typecheck": "tsc -p ./tsconfig.json --noEmit",
|
|
27
40
|
"test": "NODE_ENV=test TZ=UTC vitest run"
|
|
28
41
|
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/** Input for calculating the next backoff delay. */
|
|
2
|
-
export type BackoffStrategyInput = {
|
|
3
|
-
/** The number of retries already attempted (0 for the first retry). */
|
|
4
|
-
retryAttempt: number;
|
|
5
|
-
};
|
|
6
|
-
export type DelayMs = number;
|
|
7
|
-
/**
|
|
8
|
-
* A function that calculates the backoff delay in milliseconds.
|
|
9
|
-
* @param input - Contains information like the current retry number.
|
|
10
|
-
* @returns The delay duration in milliseconds.
|
|
11
|
-
*/
|
|
12
|
-
export type BackoffStrategy = (input: BackoffStrategyInput) => DelayMs;
|
|
13
|
-
/**
|
|
14
|
-
* Creates a strategy that provides no delay (immediate retry).
|
|
15
|
-
*/
|
|
16
|
-
export declare function createNoBackoffStrategy(): BackoffStrategy;
|
|
17
|
-
export interface FixedBackoffStrategyConfig {
|
|
18
|
-
/** The constant delay in milliseconds. */
|
|
19
|
-
readonly delayMs: number;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Creates a strategy that waits a fixed amount of time.
|
|
23
|
-
*/
|
|
24
|
-
export declare function createFixedBackoffStrategy(config: FixedBackoffStrategyConfig): BackoffStrategy;
|
|
25
|
-
export interface LinearBackoffStrategyConfig {
|
|
26
|
-
/** The base delay for all retires that will be incremented off of */
|
|
27
|
-
readonly baseDelayMs?: number;
|
|
28
|
-
/** The amount to increase the delay by for each subsequent retry in milliseconds. */
|
|
29
|
-
readonly incrementMs: number;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Creates a strategy where the delay increases linearly.
|
|
33
|
-
* Delay = (incrementMs * retryAttempt)
|
|
34
|
-
*/
|
|
35
|
-
export declare function createLinearBackoffStrategy(config: LinearBackoffStrategyConfig): BackoffStrategy;
|
|
36
|
-
export interface ExponentialBackoffStrategyConfig {
|
|
37
|
-
/** The base delay for the first retry (retryAttempt = 0) in milliseconds. */
|
|
38
|
-
readonly baseDelayMs: number;
|
|
39
|
-
/** The maximum delay in milliseconds. Defaults to Infinity. */
|
|
40
|
-
readonly maxDelayMs?: number;
|
|
41
|
-
/** Type of jitter to apply. Defaults to 'none'. */
|
|
42
|
-
readonly jitter?: 'none' | 'full' | 'equal';
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Creates a strategy where the delay increases exponentially, potentially with jitter.
|
|
46
|
-
* Base Delay Formula = baseDelayMs * (2 ** retryAttempt)
|
|
47
|
-
*/
|
|
48
|
-
export declare function createExponentialBackoffStrategy(config: ExponentialBackoffStrategyConfig): BackoffStrategy;
|
|
49
|
-
export type BackoffStrategyType = 'none' | 'fixed' | 'linear' | 'exponential';
|
|
50
|
-
export type BackoffStrategyOptions = {
|
|
51
|
-
type: 'none';
|
|
52
|
-
} | ({
|
|
53
|
-
type: 'fixed';
|
|
54
|
-
} & FixedBackoffStrategyConfig) | ({
|
|
55
|
-
type: 'linear';
|
|
56
|
-
} & LinearBackoffStrategyConfig) | ({
|
|
57
|
-
type: 'exponential';
|
|
58
|
-
} & ExponentialBackoffStrategyConfig);
|
|
59
|
-
/**
|
|
60
|
-
* Factory function to create a backoff strategy based on type and configuration.
|
|
61
|
-
* @param options - Configuration object including the strategy type.
|
|
62
|
-
* @returns A BackoffStrategy function.
|
|
63
|
-
*/
|
|
64
|
-
export declare function backoffStrategyFactory(options?: BackoffStrategyOptions): BackoffStrategy;
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createNoBackoffStrategy = createNoBackoffStrategy;
|
|
4
|
-
exports.createFixedBackoffStrategy = createFixedBackoffStrategy;
|
|
5
|
-
exports.createLinearBackoffStrategy = createLinearBackoffStrategy;
|
|
6
|
-
exports.createExponentialBackoffStrategy = createExponentialBackoffStrategy;
|
|
7
|
-
exports.backoffStrategyFactory = backoffStrategyFactory;
|
|
8
|
-
/**
|
|
9
|
-
* Creates a strategy that provides no delay (immediate retry).
|
|
10
|
-
*/
|
|
11
|
-
function createNoBackoffStrategy() {
|
|
12
|
-
return (_input) => 0;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Creates a strategy that waits a fixed amount of time.
|
|
16
|
-
*/
|
|
17
|
-
function createFixedBackoffStrategy(config) {
|
|
18
|
-
return (_input) => config.delayMs;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Creates a strategy where the delay increases linearly.
|
|
22
|
-
* Delay = (incrementMs * retryAttempt)
|
|
23
|
-
*/
|
|
24
|
-
function createLinearBackoffStrategy(config) {
|
|
25
|
-
const { incrementMs, baseDelayMs = 0 } = config;
|
|
26
|
-
return (input) => {
|
|
27
|
-
return baseDelayMs + input.retryAttempt * incrementMs;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Creates a strategy where the delay increases exponentially, potentially with jitter.
|
|
32
|
-
* Base Delay Formula = baseDelayMs * (2 ** retryAttempt)
|
|
33
|
-
*/
|
|
34
|
-
function createExponentialBackoffStrategy(config) {
|
|
35
|
-
const { baseDelayMs, maxDelayMs = Number.POSITIVE_INFINITY, jitter = 'none' } = config;
|
|
36
|
-
return (input) => {
|
|
37
|
-
const exponentialDelay = baseDelayMs * 2 ** input.retryAttempt;
|
|
38
|
-
const cappedDelay = Math.min(exponentialDelay, maxDelayMs);
|
|
39
|
-
switch (jitter) {
|
|
40
|
-
case 'full':
|
|
41
|
-
// Full Jitter: random_between(0, cappedDelay)
|
|
42
|
-
return Math.floor(Math.random() * cappedDelay);
|
|
43
|
-
case 'equal': {
|
|
44
|
-
// Equal Jitter: (cappedDelay / 2) + random_between(0, cappedDelay / 2)
|
|
45
|
-
const halfDelay = cappedDelay / 2;
|
|
46
|
-
return halfDelay + Math.random() * halfDelay;
|
|
47
|
-
}
|
|
48
|
-
case 'none':
|
|
49
|
-
// No Jitter
|
|
50
|
-
return cappedDelay;
|
|
51
|
-
default: {
|
|
52
|
-
// This should be caught by TypeScript if options type is correct
|
|
53
|
-
const _exhaustiveCheck = jitter;
|
|
54
|
-
throw new Error('Unknown jitter type for exponential backoff strategy');
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
const DEFAULT_BACKOFF_STRATEGY = {
|
|
60
|
-
type: 'linear',
|
|
61
|
-
incrementMs: 2000,
|
|
62
|
-
};
|
|
63
|
-
/**
|
|
64
|
-
* Factory function to create a backoff strategy based on type and configuration.
|
|
65
|
-
* @param options - Configuration object including the strategy type.
|
|
66
|
-
* @returns A BackoffStrategy function.
|
|
67
|
-
*/
|
|
68
|
-
function backoffStrategyFactory(options = DEFAULT_BACKOFF_STRATEGY) {
|
|
69
|
-
switch (options.type) {
|
|
70
|
-
case 'none':
|
|
71
|
-
return createNoBackoffStrategy();
|
|
72
|
-
case 'fixed':
|
|
73
|
-
// No need to destructure 'type', pass the rest
|
|
74
|
-
return createFixedBackoffStrategy(options);
|
|
75
|
-
case 'linear':
|
|
76
|
-
return createLinearBackoffStrategy(options);
|
|
77
|
-
case 'exponential':
|
|
78
|
-
return createExponentialBackoffStrategy(options);
|
|
79
|
-
default: {
|
|
80
|
-
// This should be caught by TypeScript if options type is correct
|
|
81
|
-
const _exhaustiveCheck = options;
|
|
82
|
-
throw new Error('Unknown backoff strategy type');
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
//# sourceMappingURL=backoff-strategy.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"backoff-strategy.js","sourceRoot":"","sources":["../src/backoff-strategy.ts"],"names":[],"mappings":";;AAkBA,0DAEC;AASD,gEAEC;AAaD,kEAKC;AAcD,4EA0BC;AAoBD,wDAiBC;AA/GD;;GAEG;AACH,SAAgB,uBAAuB;IACrC,OAAO,CAAC,MAA4B,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC;AAMD;;GAEG;AACH,SAAgB,0BAA0B,CAAC,MAAkC;IAC3E,OAAO,CAAC,MAA4B,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC;AAC1D,CAAC;AASD;;;GAGG;AACH,SAAgB,2BAA2B,CAAC,MAAmC;IAC7E,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC;IAChD,OAAO,CAAC,KAA2B,EAAE,EAAE;QACrC,OAAO,WAAW,GAAG,KAAK,CAAC,YAAY,GAAG,WAAW,CAAC;IACxD,CAAC,CAAC;AACJ,CAAC;AAUD;;;GAGG;AACH,SAAgB,gCAAgC,CAAC,MAAwC;IACvF,MAAM,EAAE,WAAW,EAAE,UAAU,GAAG,MAAM,CAAC,iBAAiB,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC;IAEvF,OAAO,CAAC,KAA2B,EAAE,EAAE;QACrC,MAAM,gBAAgB,GAAG,WAAW,GAAG,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC;QAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;QAE3D,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,MAAM;gBACT,8CAA8C;gBAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC;YACjD,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,uEAAuE;gBACvE,MAAM,SAAS,GAAG,WAAW,GAAG,CAAC,CAAC;gBAClC,OAAO,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,SAAS,CAAC;YAC/C,CAAC;YACD,KAAK,MAAM;gBACT,YAAY;gBACZ,OAAO,WAAW,CAAC;YACrB,OAAO,CAAC,CAAC,CAAC;gBACR,iEAAiE;gBACjE,MAAM,gBAAgB,GAAU,MAAM,CAAC;gBACvC,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAUD,MAAM,wBAAwB,GAA2B;IACvD,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,IAAI;CACT,CAAC;AAEX;;;;GAIG;AACH,SAAgB,sBAAsB,CAAC,UAAkC,wBAAwB;IAC/F,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,uBAAuB,EAAE,CAAC;QACnC,KAAK,OAAO;YACV,+CAA+C;YAC/C,OAAO,0BAA0B,CAAC,OAAO,CAAC,CAAC;QAC7C,KAAK,QAAQ;YACX,OAAO,2BAA2B,CAAC,OAAO,CAAC,CAAC;QAC9C,KAAK,aAAa;YAChB,OAAO,gCAAgC,CAAC,OAAO,CAAC,CAAC;QACnD,OAAO,CAAC,CAAC,CAAC;YACR,iEAAiE;YACjE,MAAM,gBAAgB,GAAU,OAAO,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/build/chrono.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:stream';
|
|
2
|
-
import type { BackoffStrategyOptions } from './backoff-strategy';
|
|
3
|
-
import type { Datastore, ScheduleInput, Task } from './datastore';
|
|
4
|
-
import { type ChronoEventsMap } from './events';
|
|
5
|
-
import type { ProcessorConfiguration } from './processors/create-processor';
|
|
6
|
-
import type { ProcessorEventsMap } from './processors/events';
|
|
7
|
-
export type TaskMappingBase = Record<string, unknown>;
|
|
8
|
-
export type ScheduleTaskInput<TaskKind, TaskData, DatastoreOptions> = ScheduleInput<TaskKind, TaskData, DatastoreOptions>;
|
|
9
|
-
export type RegisterTaskHandlerInput<TaskKind, TaskData> = {
|
|
10
|
-
kind: TaskKind;
|
|
11
|
-
handler: (task: Task<TaskKind, TaskData>) => Promise<void>;
|
|
12
|
-
backoffStrategyOptions?: BackoffStrategyOptions;
|
|
13
|
-
processorConfiguration?: ProcessorConfiguration;
|
|
14
|
-
};
|
|
15
|
-
export type RegisterTaskHandlerResponse<TaskKind extends keyof TaskMapping, TaskMapping extends TaskMappingBase> = EventEmitter<ProcessorEventsMap<TaskKind, TaskMapping>>;
|
|
16
|
-
/**
|
|
17
|
-
* This is a type that represents the mapping of task kinds to their respective data types.
|
|
18
|
-
*
|
|
19
|
-
* Eg. shape of the TaskMapping type:
|
|
20
|
-
*
|
|
21
|
-
* type TaskMapping = {
|
|
22
|
-
* "async-messaging": { someField: number };
|
|
23
|
-
* "send-email": { url: string };
|
|
24
|
-
* };
|
|
25
|
-
*
|
|
26
|
-
*/
|
|
27
|
-
export declare class Chrono<TaskMapping extends TaskMappingBase, DatastoreOptions> extends EventEmitter<ChronoEventsMap> {
|
|
28
|
-
private readonly datastore;
|
|
29
|
-
private readonly processors;
|
|
30
|
-
readonly exitTimeoutMs = 60000;
|
|
31
|
-
constructor(datastore: Datastore<TaskMapping, DatastoreOptions>);
|
|
32
|
-
start(): Promise<void>;
|
|
33
|
-
stop(): Promise<void>;
|
|
34
|
-
scheduleTask<TaskKind extends keyof TaskMapping>(input: ScheduleTaskInput<TaskKind, TaskMapping[TaskKind], DatastoreOptions>): Promise<Task<TaskKind, TaskMapping[TaskKind]>>;
|
|
35
|
-
deleteTask<TaskKind extends keyof TaskMapping>(taskId: string): Promise<Task<TaskKind, TaskMapping[TaskKind]> | undefined>;
|
|
36
|
-
registerTaskHandler<TaskKind extends Extract<keyof TaskMapping, string>>(input: RegisterTaskHandlerInput<TaskKind, TaskMapping[TaskKind]>): RegisterTaskHandlerResponse<TaskKind, TaskMapping>;
|
|
37
|
-
}
|