@twin.org/background-task-scheduler 0.0.2-next.9 → 0.0.3-next.10
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 +1 -1
- package/dist/es/entities/scheduledTask.js +29 -0
- package/dist/es/entities/scheduledTask.js.map +1 -0
- package/dist/es/index.js +8 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/models/ITaskSchedulerConfig.js +4 -0
- package/dist/es/models/ITaskSchedulerConfig.js.map +1 -0
- package/dist/es/models/ITaskSchedulerConstructorOptions.js +2 -0
- package/dist/es/models/ITaskSchedulerConstructorOptions.js.map +1 -0
- package/dist/es/schema.js +11 -0
- package/dist/es/schema.js.map +1 -0
- package/dist/{esm/index.mjs → es/taskSchedulerService.js} +100 -24
- package/dist/es/taskSchedulerService.js.map +1 -0
- package/dist/types/entities/scheduledTask.d.ts +13 -0
- package/dist/types/index.d.ts +5 -3
- package/dist/types/models/ITaskSchedulerConfig.d.ts +6 -1
- package/dist/types/models/ITaskSchedulerConstructorOptions.d.ts +6 -1
- package/dist/types/schema.d.ts +4 -0
- package/dist/types/taskSchedulerService.d.ts +7 -3
- package/docs/changelog.md +203 -31
- package/docs/examples.md +95 -1
- package/docs/reference/classes/ScheduledTask.md +29 -0
- package/docs/reference/classes/TaskSchedulerService.md +25 -13
- package/docs/reference/functions/initSchema.md +9 -0
- package/docs/reference/index.md +5 -0
- package/docs/reference/interfaces/ITaskSchedulerConfig.md +16 -2
- package/docs/reference/interfaces/ITaskSchedulerConstructorOptions.md +18 -4
- package/locales/en.json +5 -0
- package/package.json +10 -11
- package/dist/cjs/index.cjs +0 -209
package/README.md
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { entity, property } from "@twin.org/entity";
|
|
4
|
+
/**
|
|
5
|
+
* Class defining a scheduled task.
|
|
6
|
+
*/
|
|
7
|
+
let ScheduledTask = class ScheduledTask {
|
|
8
|
+
/**
|
|
9
|
+
* The id.
|
|
10
|
+
*/
|
|
11
|
+
id;
|
|
12
|
+
/**
|
|
13
|
+
* The last run time of the task, if undefined waiting for next run.
|
|
14
|
+
*/
|
|
15
|
+
lastRunTime;
|
|
16
|
+
};
|
|
17
|
+
__decorate([
|
|
18
|
+
property({ type: "string", isPrimary: true }),
|
|
19
|
+
__metadata("design:type", String)
|
|
20
|
+
], ScheduledTask.prototype, "id", void 0);
|
|
21
|
+
__decorate([
|
|
22
|
+
property({ type: "integer", optional: true, format: "uint64" }),
|
|
23
|
+
__metadata("design:type", Number)
|
|
24
|
+
], ScheduledTask.prototype, "lastRunTime", void 0);
|
|
25
|
+
ScheduledTask = __decorate([
|
|
26
|
+
entity()
|
|
27
|
+
], ScheduledTask);
|
|
28
|
+
export { ScheduledTask };
|
|
29
|
+
//# sourceMappingURL=scheduledTask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduledTask.js","sourceRoot":"","sources":["../../../src/entities/scheduledTask.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;GAEG;AAEI,IAAM,aAAa,GAAnB,MAAM,aAAa;IACzB;;OAEG;IAEI,EAAE,CAAU;IAEnB;;OAEG;IAEI,WAAW,CAAU;CAC5B,CAAA;AAPO;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;;yCAC3B;AAMZ;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;;kDACpC;AAXhB,aAAa;IADzB,MAAM,EAAE;GACI,aAAa,CAYzB","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { entity, property } from \"@twin.org/entity\";\n\n/**\n * Class defining a scheduled task.\n */\n@entity()\nexport class ScheduledTask {\n\t/**\n\t * The id.\n\t */\n\t@property({ type: \"string\", isPrimary: true })\n\tpublic id!: string;\n\n\t/**\n\t * The last run time of the task, if undefined waiting for next run.\n\t */\n\t@property({ type: \"integer\", optional: true, format: \"uint64\" })\n\tpublic lastRunTime?: number;\n}\n"]}
|
package/dist/es/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
export * from "./entities/scheduledTask.js";
|
|
4
|
+
export * from "./models/ITaskSchedulerConfig.js";
|
|
5
|
+
export * from "./models/ITaskSchedulerConstructorOptions.js";
|
|
6
|
+
export * from "./schema.js";
|
|
7
|
+
export * from "./taskSchedulerService.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,aAAa,CAAC;AAC5B,cAAc,2BAA2B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./entities/scheduledTask.js\";\nexport * from \"./models/ITaskSchedulerConfig.js\";\nexport * from \"./models/ITaskSchedulerConstructorOptions.js\";\nexport * from \"./schema.js\";\nexport * from \"./taskSchedulerService.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITaskSchedulerConfig.js","sourceRoot":"","sources":["../../../src/models/ITaskSchedulerConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Interface for the task scheduler configuration.\n */\nexport interface ITaskSchedulerConfig {\n\t/**\n\t * The interval between checks for running tasks, defaults to 1 minute since that is the resolution of the tasks.\n\t * @default 60000\n\t */\n\tintervalMs?: number;\n\n\t/**\n\t * The time in milliseconds after which a task that is still marked as running is considered stalled and can be run again.\n\t * @default 300000\n\t */\n\tstalledTaskTimeoutMs?: number;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ITaskSchedulerConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/ITaskSchedulerConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITaskSchedulerConfig } from \"./ITaskSchedulerConfig.js\";\n\n/**\n * Options for the task scheduler constructor.\n */\nexport interface ITaskSchedulerConstructorOptions {\n\t/**\n\t * The logging component type.\n\t * @default logging\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * The scheduled task entity storage connector type.\n\t * @default scheduled-task\n\t */\n\tscheduledTaskEntityStorageType?: string;\n\n\t/**\n\t * The configuration for the task scheduler.\n\t */\n\tconfig?: ITaskSchedulerConfig;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { EntitySchemaFactory, EntitySchemaHelper } from "@twin.org/entity";
|
|
4
|
+
import { ScheduledTask } from "./entities/scheduledTask.js";
|
|
5
|
+
/**
|
|
6
|
+
* Initialize the schema for the scheduled task service entity storage.
|
|
7
|
+
*/
|
|
8
|
+
export function initSchema() {
|
|
9
|
+
EntitySchemaFactory.register("ScheduledTask", () => EntitySchemaHelper.getSchema(ScheduledTask));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3E,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D;;GAEG;AACH,MAAM,UAAU,UAAU;IACzB,mBAAmB,CAAC,QAAQ,kBAA0B,GAAG,EAAE,CAC1D,kBAAkB,CAAC,SAAS,CAAC,aAAa,CAAC,CAC3C,CAAC;AACH,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { EntitySchemaFactory, EntitySchemaHelper } from \"@twin.org/entity\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { ScheduledTask } from \"./entities/scheduledTask.js\";\n\n/**\n * Initialize the schema for the scheduled task service entity storage.\n */\nexport function initSchema(): void {\n\tEntitySchemaFactory.register(nameof<ScheduledTask>(), () =>\n\t\tEntitySchemaHelper.getSchema(ScheduledTask)\n\t);\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ComponentFactory, Is
|
|
2
|
-
|
|
1
|
+
import { BaseError, ComponentFactory, Is } from "@twin.org/core";
|
|
2
|
+
import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
|
|
3
3
|
/**
|
|
4
4
|
* Class for scheduling tasks.
|
|
5
5
|
*/
|
|
6
|
-
class TaskSchedulerService {
|
|
6
|
+
export class TaskSchedulerService {
|
|
7
7
|
/**
|
|
8
8
|
* Runtime name for the class.
|
|
9
9
|
*/
|
|
@@ -13,16 +13,31 @@ class TaskSchedulerService {
|
|
|
13
13
|
* @internal
|
|
14
14
|
*/
|
|
15
15
|
_logging;
|
|
16
|
+
/**
|
|
17
|
+
* The entity storage for the scheduled tasks.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
_scheduledTaskEntityStorageConnector;
|
|
16
21
|
/**
|
|
17
22
|
* The interval in milliseconds at which the tasks are checked.
|
|
18
23
|
* @internal
|
|
19
24
|
*/
|
|
20
|
-
|
|
25
|
+
_tickIntervalMs;
|
|
26
|
+
/**
|
|
27
|
+
* The timeout in milliseconds after which an in-progress task is considered stalled.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
_stalledTaskTimeoutMs;
|
|
21
31
|
/**
|
|
22
32
|
* The tasks that are scheduled.
|
|
23
33
|
* @internal
|
|
24
34
|
*/
|
|
25
35
|
_tasks;
|
|
36
|
+
/**
|
|
37
|
+
* The tasks currently running in this scheduler instance.
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
_runningTasks;
|
|
26
41
|
/**
|
|
27
42
|
* The timer for running scheduled tasks.
|
|
28
43
|
* @internal
|
|
@@ -33,18 +48,36 @@ class TaskSchedulerService {
|
|
|
33
48
|
* @param options The options for the scheduler.
|
|
34
49
|
*/
|
|
35
50
|
constructor(options) {
|
|
51
|
+
this._scheduledTaskEntityStorageConnector = EntityStorageConnectorFactory.get(options?.scheduledTaskEntityStorageType ?? "scheduled-task");
|
|
36
52
|
this._logging = ComponentFactory.getIfExists(options?.loggingComponentType ?? "logging");
|
|
37
53
|
this._tasks = {};
|
|
38
|
-
this.
|
|
54
|
+
this._runningTasks = [];
|
|
55
|
+
this._tickIntervalMs = options?.config?.intervalMs ?? 60 * 1000; // 1 minute
|
|
56
|
+
this._stalledTaskTimeoutMs = options?.config?.stalledTaskTimeoutMs ?? 5 * 60 * 1000; // 5 minutes
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns the class name of the component.
|
|
60
|
+
* @returns The class name of the component.
|
|
61
|
+
*/
|
|
62
|
+
className() {
|
|
63
|
+
return TaskSchedulerService.CLASS_NAME;
|
|
39
64
|
}
|
|
40
65
|
/**
|
|
41
66
|
* The component needs to be stopped when the node is closed.
|
|
42
|
-
* @param nodeIdentity The identity of the node stopping the component.
|
|
43
67
|
* @param nodeLoggingComponentType The node logging component type.
|
|
44
68
|
* @returns Nothing.
|
|
45
69
|
*/
|
|
46
|
-
async stop(
|
|
70
|
+
async stop(nodeLoggingComponentType) {
|
|
47
71
|
this.stopTimer();
|
|
72
|
+
// If we had any running tasks, we reset their last run time to allow them to be
|
|
73
|
+
// triggered by other components
|
|
74
|
+
for (const taskId of this._runningTasks) {
|
|
75
|
+
await this._scheduledTaskEntityStorageConnector.set({
|
|
76
|
+
id: taskId,
|
|
77
|
+
lastRunTime: undefined
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
this._runningTasks = [];
|
|
48
81
|
}
|
|
49
82
|
/**
|
|
50
83
|
* Add a task to the scheduler.
|
|
@@ -63,7 +96,7 @@ class TaskSchedulerService {
|
|
|
63
96
|
})),
|
|
64
97
|
taskCallback
|
|
65
98
|
};
|
|
66
|
-
this._logging?.log({
|
|
99
|
+
await this._logging?.log({
|
|
67
100
|
level: "info",
|
|
68
101
|
source: TaskSchedulerService.CLASS_NAME,
|
|
69
102
|
ts: Date.now(),
|
|
@@ -81,7 +114,7 @@ class TaskSchedulerService {
|
|
|
81
114
|
*/
|
|
82
115
|
async removeTask(taskId) {
|
|
83
116
|
if (!Is.empty(this._tasks[taskId])) {
|
|
84
|
-
this._logging?.log({
|
|
117
|
+
await this._logging?.log({
|
|
85
118
|
level: "info",
|
|
86
119
|
source: TaskSchedulerService.CLASS_NAME,
|
|
87
120
|
ts: Date.now(),
|
|
@@ -140,7 +173,7 @@ class TaskSchedulerService {
|
|
|
140
173
|
// Trigger immediately to catch up on any missed tasks
|
|
141
174
|
await this.triggerScheduledTasks();
|
|
142
175
|
// Set the timer to run at the specified interval
|
|
143
|
-
this._timer = setInterval(async () => this.triggerScheduledTasks(), this.
|
|
176
|
+
this._timer = setInterval(async () => this.triggerScheduledTasks(), this._tickIntervalMs);
|
|
144
177
|
}
|
|
145
178
|
}
|
|
146
179
|
/**
|
|
@@ -163,21 +196,51 @@ class TaskSchedulerService {
|
|
|
163
196
|
const task = this._tasks[taskId];
|
|
164
197
|
for (const taskTime of task.times) {
|
|
165
198
|
if (!Is.empty(taskTime.nextTriggerTime) && taskTime.nextTriggerTime <= now) {
|
|
166
|
-
|
|
167
|
-
level: "info",
|
|
168
|
-
source: TaskSchedulerService.CLASS_NAME,
|
|
169
|
-
ts: Date.now(),
|
|
170
|
-
message: "taskTriggered",
|
|
171
|
-
data: {
|
|
172
|
-
id: taskId,
|
|
173
|
-
time: new Date(taskTime.nextTriggerTime).toISOString()
|
|
174
|
-
}
|
|
175
|
-
});
|
|
199
|
+
let taskStarted = false;
|
|
176
200
|
try {
|
|
177
|
-
await
|
|
201
|
+
const scheduledTask = await this._scheduledTaskEntityStorageConnector.get(taskId);
|
|
202
|
+
const lastRunTime = scheduledTask?.lastRunTime;
|
|
203
|
+
const taskInProgress = !Is.empty(lastRunTime);
|
|
204
|
+
const taskStalled = taskInProgress && now - lastRunTime >= this._stalledTaskTimeoutMs;
|
|
205
|
+
if (taskStalled) {
|
|
206
|
+
await this._logging?.log({
|
|
207
|
+
level: "warn",
|
|
208
|
+
source: TaskSchedulerService.CLASS_NAME,
|
|
209
|
+
ts: Date.now(),
|
|
210
|
+
message: "taskStalled",
|
|
211
|
+
data: {
|
|
212
|
+
id: taskId,
|
|
213
|
+
lastRunTime,
|
|
214
|
+
stalledForMs: now - (lastRunTime ?? now),
|
|
215
|
+
stalledTaskTimeoutMs: this._stalledTaskTimeoutMs
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
if (!taskInProgress || taskStalled) {
|
|
220
|
+
await this._logging?.log({
|
|
221
|
+
level: "info",
|
|
222
|
+
source: TaskSchedulerService.CLASS_NAME,
|
|
223
|
+
ts: Date.now(),
|
|
224
|
+
message: "taskTriggered",
|
|
225
|
+
data: {
|
|
226
|
+
id: taskId,
|
|
227
|
+
time: new Date(taskTime.nextTriggerTime).toISOString()
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
// Update the last run time of the task to prevent multiple triggers in case of long running tasks
|
|
231
|
+
await this._scheduledTaskEntityStorageConnector.set({
|
|
232
|
+
id: taskId,
|
|
233
|
+
lastRunTime: Date.now()
|
|
234
|
+
});
|
|
235
|
+
if (!this._runningTasks.includes(taskId)) {
|
|
236
|
+
this._runningTasks.push(taskId);
|
|
237
|
+
}
|
|
238
|
+
taskStarted = true;
|
|
239
|
+
await task.taskCallback();
|
|
240
|
+
}
|
|
178
241
|
}
|
|
179
242
|
catch (error) {
|
|
180
|
-
this._logging?.log({
|
|
243
|
+
await this._logging?.log({
|
|
181
244
|
level: "error",
|
|
182
245
|
source: TaskSchedulerService.CLASS_NAME,
|
|
183
246
|
ts: Date.now(),
|
|
@@ -188,6 +251,20 @@ class TaskSchedulerService {
|
|
|
188
251
|
error: BaseError.fromError(error)
|
|
189
252
|
});
|
|
190
253
|
}
|
|
254
|
+
finally {
|
|
255
|
+
if (taskStarted) {
|
|
256
|
+
// Reset the last run time to allow future triggers, even if the task callback fails
|
|
257
|
+
await this._scheduledTaskEntityStorageConnector.set({
|
|
258
|
+
id: taskId,
|
|
259
|
+
lastRunTime: undefined
|
|
260
|
+
});
|
|
261
|
+
// Remove the task from the running tasks list
|
|
262
|
+
const index = this._runningTasks.indexOf(taskId);
|
|
263
|
+
if (index >= 0) {
|
|
264
|
+
this._runningTasks.splice(index, 1);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
191
268
|
// If the intervals are empty, we do not recalculate a next run time
|
|
192
269
|
if (Is.empty(taskTime.intervalDays) &&
|
|
193
270
|
Is.empty(taskTime.intervalHours) &&
|
|
@@ -203,5 +280,4 @@ class TaskSchedulerService {
|
|
|
203
280
|
}
|
|
204
281
|
}
|
|
205
282
|
}
|
|
206
|
-
|
|
207
|
-
export { TaskSchedulerService };
|
|
283
|
+
//# sourceMappingURL=taskSchedulerService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taskSchedulerService.js","sourceRoot":"","sources":["../../src/taskSchedulerService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIScheduledTaskInfo,\n\tIScheduledTaskTime,\n\tITaskSchedulerComponent\n} from \"@twin.org/background-task-models\";\nimport { BaseError, ComponentFactory, Is } from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { ScheduledTask } from \"./entities/scheduledTask.js\";\nimport type { ITaskSchedulerConstructorOptions } from \"./models/ITaskSchedulerConstructorOptions.js\";\n\n/**\n * Class for scheduling tasks.\n */\nexport class TaskSchedulerService implements ITaskSchedulerComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TaskSchedulerService>();\n\n\t/**\n\t * The logger for the task service.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The entity storage for the scheduled tasks.\n\t * @internal\n\t */\n\tprivate readonly _scheduledTaskEntityStorageConnector: IEntityStorageConnector<ScheduledTask>;\n\n\t/**\n\t * The interval in milliseconds at which the tasks are checked.\n\t * @internal\n\t */\n\tprivate readonly _tickIntervalMs: number;\n\n\t/**\n\t * The timeout in milliseconds after which an in-progress task is considered stalled.\n\t * @internal\n\t */\n\tprivate readonly _stalledTaskTimeoutMs: number;\n\n\t/**\n\t * The tasks that are scheduled.\n\t * @internal\n\t */\n\tprivate readonly _tasks: {\n\t\t[id: string]: {\n\t\t\ttimes: IScheduledTaskTime[];\n\t\t\ttaskCallback: () => Promise<void>;\n\t\t};\n\t};\n\n\t/**\n\t * The tasks currently running in this scheduler instance.\n\t * @internal\n\t */\n\tprivate _runningTasks: string[];\n\n\t/**\n\t * The timer for running scheduled tasks.\n\t * @internal\n\t */\n\tprivate _timer?: NodeJS.Timeout;\n\n\t/**\n\t * Create a new instance of TaskSchedulerComponent.\n\t * @param options The options for the scheduler.\n\t */\n\tconstructor(options?: ITaskSchedulerConstructorOptions) {\n\t\tthis._scheduledTaskEntityStorageConnector = EntityStorageConnectorFactory.get(\n\t\t\toptions?.scheduledTaskEntityStorageType ?? \"scheduled-task\"\n\t\t);\n\t\tthis._logging = ComponentFactory.getIfExists(options?.loggingComponentType ?? \"logging\");\n\t\tthis._tasks = {};\n\t\tthis._runningTasks = [];\n\t\tthis._tickIntervalMs = options?.config?.intervalMs ?? 60 * 1000; // 1 minute\n\t\tthis._stalledTaskTimeoutMs = options?.config?.stalledTaskTimeoutMs ?? 5 * 60 * 1000; // 5 minutes\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn TaskSchedulerService.CLASS_NAME;\n\t}\n\n\t/**\n\t * The component needs to be stopped when the node is closed.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async stop(nodeLoggingComponentType?: string): Promise<void> {\n\t\tthis.stopTimer();\n\n\t\t// If we had any running tasks, we reset their last run time to allow them to be\n\t\t// triggered by other components\n\t\tfor (const taskId of this._runningTasks) {\n\t\t\tawait this._scheduledTaskEntityStorageConnector.set({\n\t\t\t\tid: taskId,\n\t\t\t\tlastRunTime: undefined\n\t\t\t});\n\t\t}\n\t\tthis._runningTasks = [];\n\t}\n\n\t/**\n\t * Add a task to the scheduler.\n\t * @param taskId The id of the task to add.\n\t * @param times The times at which the task should be scheduled.\n\t * @param taskCallback The callback to execute when the task is scheduled.\n\t * @returns Nothing.\n\t */\n\tpublic async addTask(\n\t\ttaskId: string,\n\t\ttimes: IScheduledTaskTime[],\n\t\ttaskCallback: () => Promise<void>\n\t): Promise<void> {\n\t\tthis._tasks[taskId] = {\n\t\t\ttimes: times.map(time => ({\n\t\t\t\t...time,\n\t\t\t\tnextTriggerTime: Is.empty(time.nextTriggerTime)\n\t\t\t\t\t? this.calculateNextTriggerTime(time)\n\t\t\t\t\t: time.nextTriggerTime\n\t\t\t})),\n\t\t\ttaskCallback\n\t\t};\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: TaskSchedulerService.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"taskAdded\",\n\t\t\tdata: {\n\t\t\t\tid: taskId\n\t\t\t}\n\t\t});\n\n\t\tawait this.startTimer();\n\t}\n\n\t/**\n\t * Remove a task from the scheduler.\n\t * @param taskId The id of the task to remove.\n\t * @returns Nothing.\n\t */\n\tpublic async removeTask(taskId: string): Promise<void> {\n\t\tif (!Is.empty(this._tasks[taskId])) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: TaskSchedulerService.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"taskRemoved\",\n\t\t\t\tdata: {\n\t\t\t\t\tid: taskId\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tdelete this._tasks[taskId];\n\n\t\t\tif (Object.keys(this._tasks).length === 0) {\n\t\t\t\tthis.stopTimer();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the information about the tasks.\n\t * @returns The tasks information.\n\t */\n\tpublic async tasksInfo(): Promise<IScheduledTaskInfo> {\n\t\tconst tasksInfo: IScheduledTaskInfo = {\n\t\t\ttasks: {}\n\t\t};\n\t\tfor (const taskId in this._tasks) {\n\t\t\ttasksInfo.tasks[taskId] = this._tasks[taskId].times;\n\t\t}\n\t\treturn tasksInfo;\n\t}\n\n\t/**\n\t * Calculate the next run time for a task based on its scheduled times.\n\t * @param time The times at which the task should be scheduled.\n\t * @returns The update time with the next run.\n\t * @internal\n\t */\n\tprivate calculateNextTriggerTime(time: IScheduledTaskTime): number {\n\t\tlet nextTriggerTime = time.nextTriggerTime;\n\n\t\tif (Is.empty(nextTriggerTime)) {\n\t\t\tnextTriggerTime = Date.now();\n\t\t}\n\n\t\tif (!Is.empty(time.intervalDays)) {\n\t\t\tnextTriggerTime += time.intervalDays * 24 * 60 * 60 * 1000;\n\t\t}\n\n\t\tif (!Is.empty(time.intervalHours)) {\n\t\t\tnextTriggerTime += time.intervalHours * 60 * 60 * 1000;\n\t\t}\n\n\t\tif (!Is.empty(time.intervalMinutes)) {\n\t\t\tnextTriggerTime += time.intervalMinutes * 60 * 1000;\n\t\t}\n\n\t\treturn nextTriggerTime;\n\t}\n\n\t/**\n\t * Start the timer for running scheduled tasks.\n\t * @internal\n\t */\n\tprivate async startTimer(): Promise<void> {\n\t\tif (Is.empty(this._timer)) {\n\t\t\t// Trigger immediately to catch up on any missed tasks\n\t\t\tawait this.triggerScheduledTasks();\n\t\t\t// Set the timer to run at the specified interval\n\t\t\tthis._timer = setInterval(async () => this.triggerScheduledTasks(), this._tickIntervalMs);\n\t\t}\n\t}\n\n\t/**\n\t * Stop the timer for running scheduled tasks.\n\t * @internal\n\t */\n\tprivate stopTimer(): void {\n\t\tif (!Is.empty(this._timer)) {\n\t\t\tclearInterval(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Trigger scheduled tasks based on their next run times.\n\t * @internal\n\t */\n\tprivate async triggerScheduledTasks(): Promise<void> {\n\t\tconst now = Date.now();\n\n\t\tfor (const taskId in this._tasks) {\n\t\t\tconst task = this._tasks[taskId];\n\n\t\t\tfor (const taskTime of task.times) {\n\t\t\t\tif (!Is.empty(taskTime.nextTriggerTime) && taskTime.nextTriggerTime <= now) {\n\t\t\t\t\tlet taskStarted = false;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst scheduledTask = await this._scheduledTaskEntityStorageConnector.get(taskId);\n\t\t\t\t\t\tconst lastRunTime = scheduledTask?.lastRunTime;\n\t\t\t\t\t\tconst taskInProgress = !Is.empty(lastRunTime);\n\t\t\t\t\t\tconst taskStalled = taskInProgress && now - lastRunTime >= this._stalledTaskTimeoutMs;\n\n\t\t\t\t\t\tif (taskStalled) {\n\t\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\t\tsource: TaskSchedulerService.CLASS_NAME,\n\t\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t\t\tmessage: \"taskStalled\",\n\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\tid: taskId,\n\t\t\t\t\t\t\t\t\tlastRunTime,\n\t\t\t\t\t\t\t\t\tstalledForMs: now - (lastRunTime ?? now),\n\t\t\t\t\t\t\t\t\tstalledTaskTimeoutMs: this._stalledTaskTimeoutMs\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!taskInProgress || taskStalled) {\n\t\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\t\t\tsource: TaskSchedulerService.CLASS_NAME,\n\t\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t\t\tmessage: \"taskTriggered\",\n\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\tid: taskId,\n\t\t\t\t\t\t\t\t\ttime: new Date(taskTime.nextTriggerTime).toISOString()\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t// Update the last run time of the task to prevent multiple triggers in case of long running tasks\n\t\t\t\t\t\t\tawait this._scheduledTaskEntityStorageConnector.set({\n\t\t\t\t\t\t\t\tid: taskId,\n\t\t\t\t\t\t\t\tlastRunTime: Date.now()\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tif (!this._runningTasks.includes(taskId)) {\n\t\t\t\t\t\t\t\tthis._runningTasks.push(taskId);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttaskStarted = true;\n\t\t\t\t\t\t\tawait task.taskCallback();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\tsource: TaskSchedulerService.CLASS_NAME,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t\tmessage: \"taskFailed\",\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\tid: taskId\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\terror: BaseError.fromError(error)\n\t\t\t\t\t\t});\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (taskStarted) {\n\t\t\t\t\t\t\t// Reset the last run time to allow future triggers, even if the task callback fails\n\t\t\t\t\t\t\tawait this._scheduledTaskEntityStorageConnector.set({\n\t\t\t\t\t\t\t\tid: taskId,\n\t\t\t\t\t\t\t\tlastRunTime: undefined\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t// Remove the task from the running tasks list\n\t\t\t\t\t\t\tconst index = this._runningTasks.indexOf(taskId);\n\t\t\t\t\t\t\tif (index >= 0) {\n\t\t\t\t\t\t\t\tthis._runningTasks.splice(index, 1);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the intervals are empty, we do not recalculate a next run time\n\t\t\t\t\tif (\n\t\t\t\t\t\tIs.empty(taskTime.intervalDays) &&\n\t\t\t\t\t\tIs.empty(taskTime.intervalHours) &&\n\t\t\t\t\t\tIs.empty(taskTime.intervalMinutes)\n\t\t\t\t\t) {\n\t\t\t\t\t\ttaskTime.nextTriggerTime = undefined;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Recalculate the next run time based on the current time and the intervals\n\t\t\t\t\t\ttaskTime.nextTriggerTime = this.calculateNextTriggerTime(taskTime);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./models/
|
|
3
|
-
export * from "./
|
|
1
|
+
export * from "./entities/scheduledTask.js";
|
|
2
|
+
export * from "./models/ITaskSchedulerConfig.js";
|
|
3
|
+
export * from "./models/ITaskSchedulerConstructorOptions.js";
|
|
4
|
+
export * from "./schema.js";
|
|
5
|
+
export * from "./taskSchedulerService.js";
|
|
@@ -6,5 +6,10 @@ export interface ITaskSchedulerConfig {
|
|
|
6
6
|
* The interval between checks for running tasks, defaults to 1 minute since that is the resolution of the tasks.
|
|
7
7
|
* @default 60000
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
intervalMs?: number;
|
|
10
|
+
/**
|
|
11
|
+
* The time in milliseconds after which a task that is still marked as running is considered stalled and can be run again.
|
|
12
|
+
* @default 300000
|
|
13
|
+
*/
|
|
14
|
+
stalledTaskTimeoutMs?: number;
|
|
10
15
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ITaskSchedulerConfig } from "./ITaskSchedulerConfig";
|
|
1
|
+
import type { ITaskSchedulerConfig } from "./ITaskSchedulerConfig.js";
|
|
2
2
|
/**
|
|
3
3
|
* Options for the task scheduler constructor.
|
|
4
4
|
*/
|
|
@@ -8,6 +8,11 @@ export interface ITaskSchedulerConstructorOptions {
|
|
|
8
8
|
* @default logging
|
|
9
9
|
*/
|
|
10
10
|
loggingComponentType?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The scheduled task entity storage connector type.
|
|
13
|
+
* @default scheduled-task
|
|
14
|
+
*/
|
|
15
|
+
scheduledTaskEntityStorageType?: string;
|
|
11
16
|
/**
|
|
12
17
|
* The configuration for the task scheduler.
|
|
13
18
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IScheduledTaskInfo, IScheduledTaskTime, ITaskSchedulerComponent } from "@twin.org/background-task-models";
|
|
2
|
-
import type { ITaskSchedulerConstructorOptions } from "./models/ITaskSchedulerConstructorOptions";
|
|
2
|
+
import type { ITaskSchedulerConstructorOptions } from "./models/ITaskSchedulerConstructorOptions.js";
|
|
3
3
|
/**
|
|
4
4
|
* Class for scheduling tasks.
|
|
5
5
|
*/
|
|
@@ -13,13 +13,17 @@ export declare class TaskSchedulerService implements ITaskSchedulerComponent {
|
|
|
13
13
|
* @param options The options for the scheduler.
|
|
14
14
|
*/
|
|
15
15
|
constructor(options?: ITaskSchedulerConstructorOptions);
|
|
16
|
+
/**
|
|
17
|
+
* Returns the class name of the component.
|
|
18
|
+
* @returns The class name of the component.
|
|
19
|
+
*/
|
|
20
|
+
className(): string;
|
|
16
21
|
/**
|
|
17
22
|
* The component needs to be stopped when the node is closed.
|
|
18
|
-
* @param nodeIdentity The identity of the node stopping the component.
|
|
19
23
|
* @param nodeLoggingComponentType The node logging component type.
|
|
20
24
|
* @returns Nothing.
|
|
21
25
|
*/
|
|
22
|
-
stop(
|
|
26
|
+
stop(nodeLoggingComponentType?: string): Promise<void>;
|
|
23
27
|
/**
|
|
24
28
|
* Add a task to the scheduler.
|
|
25
29
|
* @param taskId The id of the task to add.
|