@n8n/scheduler 0.1.0 → 0.2.1
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/LICENSE_EE.md +27 -0
- package/README.md +2 -2
- package/dist/build.tsbuildinfo +1 -1
- package/dist/core/enums.d.ts +1 -0
- package/dist/core/enums.js +10 -0
- package/dist/core/enums.js.map +1 -0
- package/dist/core/errors.d.ts +9 -0
- package/dist/core/errors.js +25 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/index.js +24 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/materializer/index.d.ts +4 -0
- package/dist/core/materializer/index.js +10 -0
- package/dist/core/materializer/index.js.map +1 -0
- package/dist/core/materializer/materialize.d.ts +11 -0
- package/dist/core/materializer/materialize.js +49 -0
- package/dist/core/materializer/materialize.js.map +1 -0
- package/dist/core/materializer/options.d.ts +7 -0
- package/dist/core/materializer/options.js +11 -0
- package/dist/core/materializer/options.js.map +1 -0
- package/dist/core/materializer/plan.d.ts +10 -0
- package/dist/core/materializer/plan.js +22 -0
- package/dist/core/materializer/plan.js.map +1 -0
- package/dist/core/materializer/transaction.d.ts +16 -0
- package/dist/core/materializer/transaction.js +3 -0
- package/dist/core/materializer/transaction.js.map +1 -0
- package/dist/core/recurrence/next-run.js.map +1 -0
- package/dist/core/recurrence/validate.js.map +1 -0
- package/dist/core/retention/index.d.ts +3 -0
- package/dist/core/retention/index.js +8 -0
- package/dist/core/retention/index.js.map +1 -0
- package/dist/core/retention/options.d.ts +7 -0
- package/dist/core/retention/options.js +11 -0
- package/dist/core/retention/options.js.map +1 -0
- package/dist/core/retention/prune.d.ts +7 -0
- package/dist/core/retention/prune.js +61 -0
- package/dist/core/retention/prune.js.map +1 -0
- package/dist/core/retention/store.d.ts +9 -0
- package/dist/{storage/storage.js → core/retention/store.js} +1 -1
- package/dist/core/retention/store.js.map +1 -0
- package/dist/core/types/index.d.ts +2 -0
- package/dist/core/types/index.js +3 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/{types → core/types}/schedule.d.ts +3 -2
- package/dist/core/types/schedule.js +3 -0
- package/dist/core/types/schedule.js.map +1 -0
- package/dist/core/types/task.d.ts +18 -0
- package/dist/core/types/task.js +3 -0
- package/dist/core/types/task.js.map +1 -0
- package/dist/executor/backoff.d.ts +6 -0
- package/dist/executor/backoff.js +15 -0
- package/dist/executor/backoff.js.map +1 -0
- package/dist/executor/executor.d.ts +23 -0
- package/dist/executor/executor.js +133 -0
- package/dist/executor/executor.js.map +1 -0
- package/dist/executor/precision-timer.d.ts +13 -0
- package/dist/executor/precision-timer.js +35 -0
- package/dist/executor/precision-timer.js.map +1 -0
- package/dist/executor/task-handler.d.ts +10 -0
- package/dist/executor/task-handler.js +23 -0
- package/dist/executor/task-handler.js.map +1 -0
- package/dist/index.d.ts +1 -6
- package/dist/index.js +15 -10
- package/dist/index.js.map +1 -1
- package/dist/reaper/reaper.d.ts +27 -0
- package/dist/reaper/reaper.js +37 -0
- package/dist/reaper/reaper.js.map +1 -0
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.js +12 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/mappers.d.ts +6 -0
- package/dist/storage/mappers.js +72 -0
- package/dist/storage/mappers.js.map +1 -0
- package/dist/storage/materializer-store.d.ts +14 -0
- package/dist/storage/materializer-store.js +83 -0
- package/dist/storage/materializer-store.js.map +1 -0
- package/dist/storage/scheduler.service.d.ts +16 -0
- package/dist/storage/scheduler.service.js +70 -0
- package/dist/storage/scheduler.service.js.map +1 -0
- package/package.json +41 -6
- package/dist/errors.d.ts +0 -3
- package/dist/errors.js +0 -11
- package/dist/errors.js.map +0 -1
- package/dist/recurrence/next-run.js.map +0 -1
- package/dist/recurrence/validate.js.map +0 -1
- package/dist/storage/storage.d.ts +0 -6
- package/dist/storage/storage.js.map +0 -1
- package/dist/types/index.d.ts +0 -2
- package/dist/types/index.js +0 -19
- package/dist/types/index.js.map +0 -1
- package/dist/types/schedule.js +0 -5
- package/dist/types/schedule.js.map +0 -1
- package/dist/types/task.d.ts +0 -13
- package/dist/types/task.js +0 -12
- package/dist/types/task.js.map +0 -1
- /package/dist/{recurrence → core/recurrence}/next-run.d.ts +0 -0
- /package/dist/{recurrence → core/recurrence}/next-run.js +0 -0
- /package/dist/{recurrence → core/recurrence}/validate.d.ts +0 -0
- /package/dist/{recurrence → core/recurrence}/validate.js +0 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Executor = void 0;
|
|
4
|
+
const constants_1 = require("@n8n/constants");
|
|
5
|
+
const ensure_error_1 = require("@n8n/utils/errors/ensure-error");
|
|
6
|
+
const backoff_1 = require("./backoff");
|
|
7
|
+
const mappers_1 = require("../storage/mappers");
|
|
8
|
+
class Executor {
|
|
9
|
+
constructor(taskRepository, registry, timer, logger, config) {
|
|
10
|
+
this.taskRepository = taskRepository;
|
|
11
|
+
this.registry = registry;
|
|
12
|
+
this.timer = timer;
|
|
13
|
+
this.claimedTaskById = new Map();
|
|
14
|
+
this.logger = logger.scoped('scheduler');
|
|
15
|
+
this.leaseMs = config.leaseDurationSeconds * constants_1.Time.seconds.toMilliseconds;
|
|
16
|
+
this.lookaheadMs = config.executorIntervalSeconds * constants_1.Time.seconds.toMilliseconds;
|
|
17
|
+
this.batchSize = config.claimBatchSize;
|
|
18
|
+
if (this.lookaheadMs >= this.leaseMs) {
|
|
19
|
+
this.logger.warn('Scheduler executorInterval >= leaseDuration: a claimed task may lose its lease before firing; set executorInterval below leaseDuration', {
|
|
20
|
+
executorIntervalSeconds: config.executorIntervalSeconds,
|
|
21
|
+
leaseDurationSeconds: config.leaseDurationSeconds,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async claimAndSchedule(host) {
|
|
26
|
+
const taskTypes = this.registry.registeredTypes();
|
|
27
|
+
if (taskTypes.length === 0)
|
|
28
|
+
return [];
|
|
29
|
+
const claim = {
|
|
30
|
+
host,
|
|
31
|
+
taskTypes,
|
|
32
|
+
lookaheadMs: this.lookaheadMs,
|
|
33
|
+
leaseMs: this.leaseMs,
|
|
34
|
+
batchSize: this.batchSize,
|
|
35
|
+
};
|
|
36
|
+
const rows = await this.taskRepository.claimDueTasks(claim);
|
|
37
|
+
const tasks = [];
|
|
38
|
+
for (const row of rows) {
|
|
39
|
+
let task;
|
|
40
|
+
try {
|
|
41
|
+
task = (0, mappers_1.entityToClaimedTask)(row);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
await this.releaseUnmappableRow(host, row, error);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
tasks.push(task);
|
|
48
|
+
this.scheduleClaimed(host, task);
|
|
49
|
+
}
|
|
50
|
+
return tasks;
|
|
51
|
+
}
|
|
52
|
+
scheduleClaimed(host, task) {
|
|
53
|
+
this.claimedTaskById.set(task.id, { host, task });
|
|
54
|
+
this.timer.schedule(task.runAt, () => {
|
|
55
|
+
this.claimedTaskById.delete(task.id);
|
|
56
|
+
this.fire(host, task).catch((error) => {
|
|
57
|
+
this.logger.error('Scheduler executor failed to fire task', {
|
|
58
|
+
taskId: task.id,
|
|
59
|
+
taskType: task.taskType,
|
|
60
|
+
error,
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async fire(host, task) {
|
|
66
|
+
const claim = { host, id: task.id, claimedEpoch: task.leaseEpoch };
|
|
67
|
+
const handler = this.registry.resolve(task.taskType);
|
|
68
|
+
if (handler === undefined) {
|
|
69
|
+
this.logger.warn('Scheduler executor found no handler for claimed task; releasing it', {
|
|
70
|
+
taskId: task.id,
|
|
71
|
+
taskType: task.taskType,
|
|
72
|
+
});
|
|
73
|
+
await this.releaseClaimBestEffort(claim);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const started = await this.taskRepository.markStarted(claim);
|
|
77
|
+
if (started === 0)
|
|
78
|
+
return;
|
|
79
|
+
try {
|
|
80
|
+
await handler.execute(task);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
const message = (0, ensure_error_1.ensureError)(error).message;
|
|
84
|
+
const nextAttempts = task.attempts + 1;
|
|
85
|
+
if (nextAttempts >= task.maxAttempts) {
|
|
86
|
+
await this.taskRepository.failTaskTerminal(claim, message);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
await this.taskRepository.rescheduleTask(claim, (0, backoff_1.backoff)(nextAttempts), message);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
await this.taskRepository.completeTask(claim);
|
|
94
|
+
}
|
|
95
|
+
async releaseUnmappableRow(host, row, error) {
|
|
96
|
+
this.logger.error('Scheduler executor could not map claimed task; releasing it', {
|
|
97
|
+
taskId: row.id,
|
|
98
|
+
error,
|
|
99
|
+
});
|
|
100
|
+
await this.releaseClaimBestEffort({ host, id: row.id, claimedEpoch: row.leaseEpoch });
|
|
101
|
+
}
|
|
102
|
+
async releaseClaimBestEffort(claim) {
|
|
103
|
+
try {
|
|
104
|
+
await this.taskRepository.releaseClaim(claim);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
this.logger.error('Scheduler executor failed to release claim', {
|
|
108
|
+
taskId: claim.id,
|
|
109
|
+
error,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async stop() {
|
|
114
|
+
this.timer.cancelAll();
|
|
115
|
+
const entries = [...this.claimedTaskById.values()];
|
|
116
|
+
const results = await Promise.allSettled(entries.map(async ({ host, task }) => await this.taskRepository.releaseClaim({
|
|
117
|
+
host,
|
|
118
|
+
id: task.id,
|
|
119
|
+
claimedEpoch: task.leaseEpoch,
|
|
120
|
+
})));
|
|
121
|
+
results.forEach((result, index) => {
|
|
122
|
+
if (result.status === 'rejected') {
|
|
123
|
+
this.logger.error('Scheduler executor failed to release claim on shutdown', {
|
|
124
|
+
taskId: entries[index].task.id,
|
|
125
|
+
error: result.reason,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
this.claimedTaskById.clear();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
exports.Executor = Executor;
|
|
133
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/executor/executor.ts"],"names":[],"mappings":";;;AAEA,8CAAsC;AAOtC,iEAA6D;AAE7D,uCAAoC;AAIpC,gDAAyD;AAsBzD,MAAa,QAAQ;IAgBpB,YACkB,cAAuC,EACvC,QAA6B,EAC7B,KAAqB,EACtC,MAAc,EACd,MAAuB;QAJN,mBAAc,GAAd,cAAc,CAAyB;QACvC,aAAQ,GAAR,QAAQ,CAAqB;QAC7B,UAAK,GAAL,KAAK,CAAgB;QALtB,oBAAe,GAAG,IAAI,GAAG,EAAwB,CAAC;QASlE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,oBAAoB,GAAG,gBAAI,CAAC,OAAO,CAAC,cAAc,CAAC;QAGzE,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,uBAAuB,GAAG,gBAAI,CAAC,OAAO,CAAC,cAAc,CAAC;QAChF,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;QAKvC,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACtC,IAAI,CAAC,MAAM,CAAC,IAAI,CACf,wIAAwI,EACxI;gBACC,uBAAuB,EAAE,MAAM,CAAC,uBAAuB;gBACvD,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;aACjD,CACD,CAAC;QACH,CAAC;IACF,CAAC;IAQD,KAAK,CAAC,gBAAgB,CAAC,IAAY;QAClC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC;QAClD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEtC,MAAM,KAAK,GAAyB;YACnC,IAAI;YACJ,SAAS;YACT,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,SAAS;SACzB,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC5D,MAAM,KAAK,GAAkB,EAAE,CAAC;QAChC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACxB,IAAI,IAAiB,CAAC;YACtB,IAAI,CAAC;gBACJ,IAAI,GAAG,IAAA,6BAAmB,EAAC,GAAG,CAAC,CAAC;YACjC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;gBAClD,SAAS;YACV,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;QAED,OAAO,KAAK,CAAC;IACd,CAAC;IAGO,eAAe,CAAC,IAAY,EAAE,IAAiB;QAEtD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE;YAEpC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAGrC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACrC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,wCAAwC,EAAE;oBAC3D,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,KAAK;iBACL,CAAC,CAAC;YACJ,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACJ,CAAC;IAOD,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,IAAiB;QACzC,MAAM,KAAK,GAAqB,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QAMrF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oEAAoE,EAAE;gBACtF,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACvB,CAAC,CAAC;YACH,MAAM,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;YACzC,OAAO;QACR,CAAC;QAID,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC7D,IAAI,OAAO,KAAK,CAAC;YAAE,OAAO;QAK1B,IAAI,CAAC;YACJ,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,IAAA,0BAAW,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC;YAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;YACvC,IAAI,YAAY,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtC,MAAM,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAC5D,CAAC;iBAAM,CAAC;gBACP,MAAM,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,KAAK,EAAE,IAAA,iBAAO,EAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;YACjF,CAAC;YACD,OAAO;QACR,CAAC;QAED,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC;IAMO,KAAK,CAAC,oBAAoB,CACjC,IAAY,EACZ,GAAkB,EAClB,KAAc;QAEd,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6DAA6D,EAAE;YAChF,MAAM,EAAE,GAAG,CAAC,EAAE;YACd,KAAK;SACL,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,sBAAsB,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,YAAY,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IACvF,CAAC;IAGO,KAAK,CAAC,sBAAsB,CAAC,KAAuB;QAC3D,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,EAAE;gBAC/D,MAAM,EAAE,KAAK,CAAC,EAAE;gBAChB,KAAK;aACL,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAWD,KAAK,CAAC,IAAI;QACT,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QAEvB,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACvC,OAAO,CAAC,GAAG,CACV,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CACxB,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC;YACtC,IAAI;YACJ,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,YAAY,EAAE,IAAI,CAAC,UAAU;SAC7B,CAAC,CACH,CACD,CAAC;QAEF,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YACjC,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,wDAAwD,EAAE;oBAC3E,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE;oBAC9B,KAAK,EAAE,MAAM,CAAC,MAAM;iBACpB,CAAC,CAAC;YACJ,CAAC;QACF,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;CACD;AA7MD,4BA6MC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface TimerBackend {
|
|
2
|
+
now(): number;
|
|
3
|
+
setTimer(fn: () => void, delayMs: number): NodeJS.Timeout;
|
|
4
|
+
clearTimer(handle: NodeJS.Timeout): void;
|
|
5
|
+
}
|
|
6
|
+
export declare class PrecisionTimer {
|
|
7
|
+
private readonly backend;
|
|
8
|
+
private readonly pending;
|
|
9
|
+
constructor(backend?: TimerBackend);
|
|
10
|
+
schedule(runAt: Date, fn: () => void): void;
|
|
11
|
+
cancelAll(): void;
|
|
12
|
+
get pendingCount(): number;
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PrecisionTimer = void 0;
|
|
4
|
+
const constants_1 = require("@n8n/constants");
|
|
5
|
+
const defaultBackend = {
|
|
6
|
+
now: () => Date.now(),
|
|
7
|
+
setTimer: (fn, delayMs) => setTimeout(fn, delayMs),
|
|
8
|
+
clearTimer: (handle) => clearTimeout(handle),
|
|
9
|
+
};
|
|
10
|
+
const MAX_DELAY_MS = constants_1.MAX_INTEGER_32BITS_SIGNED;
|
|
11
|
+
class PrecisionTimer {
|
|
12
|
+
constructor(backend = defaultBackend) {
|
|
13
|
+
this.backend = backend;
|
|
14
|
+
this.pending = new Set();
|
|
15
|
+
}
|
|
16
|
+
schedule(runAt, fn) {
|
|
17
|
+
const delay = Math.min(Math.max(0, runAt.getTime() - this.backend.now()), MAX_DELAY_MS);
|
|
18
|
+
const handle = this.backend.setTimer(() => {
|
|
19
|
+
this.pending.delete(handle);
|
|
20
|
+
fn();
|
|
21
|
+
}, delay);
|
|
22
|
+
this.pending.add(handle);
|
|
23
|
+
}
|
|
24
|
+
cancelAll() {
|
|
25
|
+
const handles = [...this.pending];
|
|
26
|
+
this.pending.clear();
|
|
27
|
+
for (const handle of handles)
|
|
28
|
+
this.backend.clearTimer(handle);
|
|
29
|
+
}
|
|
30
|
+
get pendingCount() {
|
|
31
|
+
return this.pending.size;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.PrecisionTimer = PrecisionTimer;
|
|
35
|
+
//# sourceMappingURL=precision-timer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"precision-timer.js","sourceRoot":"","sources":["../../src/executor/precision-timer.ts"],"names":[],"mappings":";;;AAAA,8CAA2D;AAY3D,MAAM,cAAc,GAAiB;IACpC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;IACrB,QAAQ,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,OAAO,CAAC;IAClD,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC;CAC5C,CAAC;AAGF,MAAM,YAAY,GAAG,qCAAyB,CAAC;AAY/C,MAAa,cAAc;IAG1B,YAA6B,UAAwB,cAAc;QAAtC,YAAO,GAAP,OAAO,CAA+B;QAFlD,YAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEiB,CAAC;IAGvE,QAAQ,CAAC,KAAW,EAAE,EAAc;QAInC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC;QAGxF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE;YACzC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC5B,EAAE,EAAE,CAAC;QACN,CAAC,EAAE,KAAK,CAAC,CAAC;QACV,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IAGD,SAAS;QAGR,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,KAAK,MAAM,MAAM,IAAI,OAAO;YAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;IAED,IAAI,YAAY;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC1B,CAAC;CACD;AAhCD,wCAgCC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ClaimedTask } from '../core/types';
|
|
2
|
+
export interface TaskHandler {
|
|
3
|
+
execute(task: ClaimedTask): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
export declare class TaskHandlerRegistry {
|
|
6
|
+
private readonly handlers;
|
|
7
|
+
register(taskType: string, handler: TaskHandler): void;
|
|
8
|
+
resolve(taskType: string): TaskHandler | undefined;
|
|
9
|
+
registeredTypes(): string[];
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TaskHandlerRegistry = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
class TaskHandlerRegistry {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.handlers = new Map();
|
|
8
|
+
}
|
|
9
|
+
register(taskType, handler) {
|
|
10
|
+
if (this.handlers.has(taskType)) {
|
|
11
|
+
throw new n8n_workflow_1.UnexpectedError(`A handler for task type '${taskType}' is already registered`);
|
|
12
|
+
}
|
|
13
|
+
this.handlers.set(taskType, handler);
|
|
14
|
+
}
|
|
15
|
+
resolve(taskType) {
|
|
16
|
+
return this.handlers.get(taskType);
|
|
17
|
+
}
|
|
18
|
+
registeredTypes() {
|
|
19
|
+
return [...this.handlers.keys()];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.TaskHandlerRegistry = TaskHandlerRegistry;
|
|
23
|
+
//# sourceMappingURL=task-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-handler.js","sourceRoot":"","sources":["../../src/executor/task-handler.ts"],"names":[],"mappings":";;;AAAA,+CAA+C;AAkB/C,MAAa,mBAAmB;IAAhC;QACkB,aAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IAkB5D,CAAC;IAfA,QAAQ,CAAC,QAAgB,EAAE,OAAoB;QAC9C,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,8BAAe,CAAC,4BAA4B,QAAQ,yBAAyB,CAAC,CAAC;QAC1F,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,CAAC,QAAgB;QACvB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAGD,eAAe;QACd,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC;CACD;AAnBD,kDAmBC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export type { ScheduleKind, CronSchedule, IntervalSchedule, OneOffSchedule, Schedule, TaskStatus, ScheduledJob, ScheduledTask, } from './types';
|
|
3
|
-
export { InvalidScheduleError } from './errors';
|
|
4
|
-
export { computeNextRunAt } from './recurrence/next-run';
|
|
5
|
-
export { validateSchedule } from './recurrence/validate';
|
|
6
|
-
export type { SchedulerStore } from './storage/storage';
|
|
1
|
+
export * from './core';
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
var types_1 = require("./types");
|
|
5
|
-
Object.defineProperty(exports, "ScheduleKindList", { enumerable: true, get: function () { return types_1.ScheduleKindList; } });
|
|
6
|
-
Object.defineProperty(exports, "TaskStatusList", { enumerable: true, get: function () { return types_1.TaskStatusList; } });
|
|
7
|
-
var errors_1 = require("./errors");
|
|
8
|
-
Object.defineProperty(exports, "InvalidScheduleError", { enumerable: true, get: function () { return errors_1.InvalidScheduleError; } });
|
|
9
|
-
var next_run_1 = require("./recurrence/next-run");
|
|
10
|
-
Object.defineProperty(exports, "computeNextRunAt", { enumerable: true, get: function () { return next_run_1.computeNextRunAt; } });
|
|
11
|
-
var validate_1 = require("./recurrence/validate");
|
|
12
|
-
Object.defineProperty(exports, "validateSchedule", { enumerable: true, get: function () { return validate_1.validateSchedule; } });
|
|
17
|
+
__exportStar(require("./core"), exports);
|
|
13
18
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yCAAuB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Logger } from '@n8n/backend-common';
|
|
2
|
+
export interface ReapResult {
|
|
3
|
+
reclaimed: number;
|
|
4
|
+
deadLettered: number;
|
|
5
|
+
}
|
|
6
|
+
interface ExpiredLeaseRef {
|
|
7
|
+
id: string;
|
|
8
|
+
claimedEpoch: number;
|
|
9
|
+
}
|
|
10
|
+
interface ExpiredLeaseRow {
|
|
11
|
+
id: string;
|
|
12
|
+
attempts: number;
|
|
13
|
+
maxAttempts: number;
|
|
14
|
+
leaseEpoch: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ReaperTaskStore {
|
|
17
|
+
findExpiredLeases(limit: number): Promise<ExpiredLeaseRow[]>;
|
|
18
|
+
reclaimExpired(ref: ExpiredLeaseRef, backoffMs: number, errorMessage: string): Promise<number>;
|
|
19
|
+
deadLetterExpired(ref: ExpiredLeaseRef, errorMessage: string): Promise<number>;
|
|
20
|
+
}
|
|
21
|
+
export interface ReapDeps {
|
|
22
|
+
store: ReaperTaskStore;
|
|
23
|
+
logger: Logger;
|
|
24
|
+
batchSize: number;
|
|
25
|
+
}
|
|
26
|
+
export declare function reap({ store, logger, batchSize }: ReapDeps): Promise<ReapResult>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reap = reap;
|
|
4
|
+
const backoff_1 = require("../executor/backoff");
|
|
5
|
+
const LEASE_EXPIRED_MESSAGE = 'Lease expired before completion';
|
|
6
|
+
async function reap({ store, logger, batchSize }) {
|
|
7
|
+
const expired = await store.findExpiredLeases(batchSize);
|
|
8
|
+
if (expired.length === 0)
|
|
9
|
+
return { reclaimed: 0, deadLettered: 0 };
|
|
10
|
+
let reclaimed = 0;
|
|
11
|
+
let deadLettered = 0;
|
|
12
|
+
for (const task of expired) {
|
|
13
|
+
try {
|
|
14
|
+
const nextAttempts = task.attempts + 1;
|
|
15
|
+
if (nextAttempts >= task.maxAttempts) {
|
|
16
|
+
deadLettered += await store.deadLetterExpired({ id: task.id, claimedEpoch: task.leaseEpoch }, LEASE_EXPIRED_MESSAGE);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
reclaimed += await store.reclaimExpired({ id: task.id, claimedEpoch: task.leaseEpoch }, (0, backoff_1.backoff)(nextAttempts), LEASE_EXPIRED_MESSAGE);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
logger.error('Scheduler reaper failed to recover an expired-lease task', {
|
|
24
|
+
taskId: task.id,
|
|
25
|
+
error,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (reclaimed > 0 || deadLettered > 0) {
|
|
30
|
+
logger.info('Scheduler reaper recovered expired-lease tasks', {
|
|
31
|
+
reclaimed,
|
|
32
|
+
deadLettered,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return { reclaimed, deadLettered };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=reaper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reaper.js","sourceRoot":"","sources":["../../src/reaper/reaper.ts"],"names":[],"mappings":";;AAwEA,oBAwCC;AA9GD,iDAA8C;AAG9C,MAAM,qBAAqB,GAAG,iCAAiC,CAAC;AAmEzD,KAAK,UAAU,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAY;IAChE,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACzD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IAEnE,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAG5B,IAAI,CAAC;YAGJ,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;YACvC,IAAI,YAAY,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtC,YAAY,IAAI,MAAM,KAAK,CAAC,iBAAiB,CAC5C,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,UAAU,EAAE,EAC9C,qBAAqB,CACrB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACP,SAAS,IAAI,MAAM,KAAK,CAAC,cAAc,CACtC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,UAAU,EAAE,EAC9C,IAAA,iBAAO,EAAC,YAAY,CAAC,EACrB,qBAAqB,CACrB,CAAC;YACH,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,CAAC,KAAK,CAAC,0DAA0D,EAAE;gBACxE,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,KAAK;aACL,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED,IAAI,SAAS,GAAG,CAAC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,gDAAgD,EAAE;YAC7D,SAAS;YACT,YAAY;SACZ,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scheduledTaskToRow = exports.entityToClaimedTask = exports.entityToScheduledJob = exports.SchedulerService = exports.MaterializerStore = void 0;
|
|
4
|
+
var materializer_store_1 = require("./materializer-store");
|
|
5
|
+
Object.defineProperty(exports, "MaterializerStore", { enumerable: true, get: function () { return materializer_store_1.MaterializerStore; } });
|
|
6
|
+
var scheduler_service_1 = require("./scheduler.service");
|
|
7
|
+
Object.defineProperty(exports, "SchedulerService", { enumerable: true, get: function () { return scheduler_service_1.SchedulerService; } });
|
|
8
|
+
var mappers_1 = require("./mappers");
|
|
9
|
+
Object.defineProperty(exports, "entityToScheduledJob", { enumerable: true, get: function () { return mappers_1.entityToScheduledJob; } });
|
|
10
|
+
Object.defineProperty(exports, "entityToClaimedTask", { enumerable: true, get: function () { return mappers_1.entityToClaimedTask; } });
|
|
11
|
+
Object.defineProperty(exports, "scheduledTaskToRow", { enumerable: true, get: function () { return mappers_1.scheduledTaskToRow; } });
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":";;;AAAA,2DAAyD;AAAhD,uHAAA,iBAAiB,OAAA;AAC1B,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AACzB,qCAA0F;AAAjF,+GAAA,oBAAoB,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,6GAAA,kBAAkB,OAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ScheduledJob as ScheduledJobEntity, ScheduledTask as ScheduledTaskEntity } from '@n8n/db';
|
|
2
|
+
import type { QueryDeepPartialEntity } from '@n8n/typeorm/query-builder/QueryPartialEntity';
|
|
3
|
+
import type { ClaimedTask, ScheduledJob, ScheduledTask } from '../core/types';
|
|
4
|
+
export declare function entityToScheduledJob(entity: ScheduledJobEntity, instanceTimezone: string): ScheduledJob;
|
|
5
|
+
export declare function scheduledTaskToRow(task: ScheduledTask): QueryDeepPartialEntity<ScheduledTaskEntity>;
|
|
6
|
+
export declare function entityToClaimedTask(entity: ScheduledTaskEntity): ClaimedTask;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.entityToScheduledJob = entityToScheduledJob;
|
|
4
|
+
exports.scheduledTaskToRow = scheduledTaskToRow;
|
|
5
|
+
exports.entityToClaimedTask = entityToClaimedTask;
|
|
6
|
+
const errors_1 = require("../core/errors");
|
|
7
|
+
function toSchedule(entity, instanceTimezone) {
|
|
8
|
+
switch (entity.kind) {
|
|
9
|
+
case 'cron':
|
|
10
|
+
return {
|
|
11
|
+
kind: 'cron',
|
|
12
|
+
cronExpression: required(entity, 'cronExpression'),
|
|
13
|
+
timezone: entity.timezone ?? instanceTimezone,
|
|
14
|
+
};
|
|
15
|
+
case 'interval':
|
|
16
|
+
return { kind: 'interval', intervalSeconds: required(entity, 'intervalSeconds') };
|
|
17
|
+
case 'one_off':
|
|
18
|
+
return { kind: 'one_off', fireAt: required(entity, 'fireAt') };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function required(entity, key) {
|
|
22
|
+
const value = entity[key];
|
|
23
|
+
if (value === null || value === undefined) {
|
|
24
|
+
throw new errors_1.CorruptStorageRowError(`scheduled_job ${entity.id} of kind '${entity.kind}' is missing '${String(key)}'`);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function entityToScheduledJob(entity, instanceTimezone) {
|
|
29
|
+
return {
|
|
30
|
+
id: String(entity.id),
|
|
31
|
+
schedule: toSchedule(entity, instanceTimezone),
|
|
32
|
+
enabled: entity.enabled,
|
|
33
|
+
nextRunAt: entity.nextRunAt,
|
|
34
|
+
lastFiredAt: entity.lastFiredAt,
|
|
35
|
+
taskType: entity.taskType,
|
|
36
|
+
payload: entity.payload,
|
|
37
|
+
maxAttempts: entity.maxAttempts,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function scheduledTaskToRow(task) {
|
|
41
|
+
return {
|
|
42
|
+
jobId: Number(task.jobId),
|
|
43
|
+
taskType: task.taskType,
|
|
44
|
+
payload: task.payload,
|
|
45
|
+
scheduledFor: task.scheduledFor,
|
|
46
|
+
runAt: task.runAt,
|
|
47
|
+
status: task.status,
|
|
48
|
+
attempts: task.attempts,
|
|
49
|
+
maxAttempts: task.maxAttempts,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function entityToClaimedTask(entity) {
|
|
53
|
+
if (entity.claimedBy === null || entity.leaseExpiresAt === null) {
|
|
54
|
+
throw new errors_1.CorruptStorageRowError(`scheduled_task ${entity.id} was claimed but is missing 'claimedBy' or 'leaseExpiresAt'`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
id: String(entity.id),
|
|
58
|
+
jobId: String(entity.jobId),
|
|
59
|
+
taskType: entity.taskType,
|
|
60
|
+
payload: entity.payload,
|
|
61
|
+
scheduledFor: entity.scheduledFor,
|
|
62
|
+
runAt: entity.runAt,
|
|
63
|
+
status: entity.status,
|
|
64
|
+
attempts: entity.attempts,
|
|
65
|
+
maxAttempts: entity.maxAttempts,
|
|
66
|
+
claimedBy: entity.claimedBy,
|
|
67
|
+
leaseExpiresAt: entity.leaseExpiresAt,
|
|
68
|
+
leaseEpoch: entity.leaseEpoch,
|
|
69
|
+
startedAt: entity.startedAt,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=mappers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mappers.js","sourceRoot":"","sources":["../../src/storage/mappers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Logger } from '@n8n/backend-common';
|
|
2
|
+
import { GlobalConfig } from '@n8n/config';
|
|
3
|
+
import { ScheduledJobRepository, ScheduledTaskRepository } from '@n8n/db';
|
|
4
|
+
import { DataSource } from '@n8n/typeorm';
|
|
5
|
+
import type { RunInTransaction } from '../core/materializer';
|
|
6
|
+
export declare class MaterializerStore {
|
|
7
|
+
private readonly dataSource;
|
|
8
|
+
private readonly jobs;
|
|
9
|
+
private readonly tasks;
|
|
10
|
+
private readonly logger;
|
|
11
|
+
private readonly globalConfig;
|
|
12
|
+
constructor(dataSource: DataSource, jobs: ScheduledJobRepository, tasks: ScheduledTaskRepository, logger: Logger, globalConfig: GlobalConfig);
|
|
13
|
+
readonly runInTransaction: RunInTransaction;
|
|
14
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MaterializerStore = void 0;
|
|
13
|
+
const backend_common_1 = require("@n8n/backend-common");
|
|
14
|
+
const config_1 = require("@n8n/config");
|
|
15
|
+
const db_1 = require("@n8n/db");
|
|
16
|
+
const di_1 = require("@n8n/di");
|
|
17
|
+
const typeorm_1 = require("@n8n/typeorm");
|
|
18
|
+
const mappers_1 = require("./mappers");
|
|
19
|
+
class DatabaseMaterializerTransaction {
|
|
20
|
+
constructor(manager, jobs, tasks, logger, instanceTimezone) {
|
|
21
|
+
this.manager = manager;
|
|
22
|
+
this.jobs = jobs;
|
|
23
|
+
this.tasks = tasks;
|
|
24
|
+
this.logger = logger;
|
|
25
|
+
this.instanceTimezone = instanceTimezone;
|
|
26
|
+
}
|
|
27
|
+
async claimDueJobs(limit) {
|
|
28
|
+
const claimed = await this.jobs.claimDue(this.manager, limit);
|
|
29
|
+
if (claimed === undefined) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
now: claimed.now,
|
|
34
|
+
jobs: claimed.jobs.map((entity) => (0, mappers_1.entityToScheduledJob)(entity, this.instanceTimezone)),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async recordOccurrences(planned) {
|
|
38
|
+
const occurrences = planned.flatMap(({ job, plan }) => plan.occurrences.map((when) => ({
|
|
39
|
+
jobId: Number(job.id),
|
|
40
|
+
taskType: job.taskType,
|
|
41
|
+
payload: job.payload,
|
|
42
|
+
scheduledFor: when,
|
|
43
|
+
runAt: when,
|
|
44
|
+
maxAttempts: job.maxAttempts,
|
|
45
|
+
})));
|
|
46
|
+
const recorded = await this.tasks.insertIgnoringDuplicates(this.manager, occurrences);
|
|
47
|
+
if (recorded < occurrences.length) {
|
|
48
|
+
this.logger.debug('Materializer skipped occurrences that were already recorded', {
|
|
49
|
+
jobs: planned.length,
|
|
50
|
+
planned: occurrences.length,
|
|
51
|
+
recorded,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return recorded;
|
|
55
|
+
}
|
|
56
|
+
async advanceJobs(planned) {
|
|
57
|
+
await this.jobs.advanceMany(this.manager, planned.map(({ job, plan }) => ({
|
|
58
|
+
id: Number(job.id),
|
|
59
|
+
nextRunAt: plan.nextRunAt,
|
|
60
|
+
lastFiredAt: plan.lastFiredAt,
|
|
61
|
+
})));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let MaterializerStore = class MaterializerStore {
|
|
65
|
+
constructor(dataSource, jobs, tasks, logger, globalConfig) {
|
|
66
|
+
this.dataSource = dataSource;
|
|
67
|
+
this.jobs = jobs;
|
|
68
|
+
this.tasks = tasks;
|
|
69
|
+
this.logger = logger;
|
|
70
|
+
this.globalConfig = globalConfig;
|
|
71
|
+
this.runInTransaction = async (work) => await this.dataSource.transaction(async (manager) => await work(new DatabaseMaterializerTransaction(manager, this.jobs, this.tasks, this.logger, this.globalConfig.generic.timezone)));
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
exports.MaterializerStore = MaterializerStore;
|
|
75
|
+
exports.MaterializerStore = MaterializerStore = __decorate([
|
|
76
|
+
(0, di_1.Service)(),
|
|
77
|
+
__metadata("design:paramtypes", [typeorm_1.DataSource,
|
|
78
|
+
db_1.ScheduledJobRepository,
|
|
79
|
+
db_1.ScheduledTaskRepository,
|
|
80
|
+
backend_common_1.Logger,
|
|
81
|
+
config_1.GlobalConfig])
|
|
82
|
+
], MaterializerStore);
|
|
83
|
+
//# sourceMappingURL=materializer-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materializer-store.js","sourceRoot":"","sources":["../../src/storage/materializer-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Logger } from '@n8n/backend-common';
|
|
2
|
+
import { SchedulerConfig } from '@n8n/config';
|
|
3
|
+
import { ScheduledTaskRepository } from '@n8n/db';
|
|
4
|
+
import { MaterializerStore } from './materializer-store';
|
|
5
|
+
import type { MaterializerSummary } from '../core/materializer';
|
|
6
|
+
import type { RetentionSummary } from '../core/retention';
|
|
7
|
+
export declare class SchedulerService {
|
|
8
|
+
private readonly store;
|
|
9
|
+
private readonly tasks;
|
|
10
|
+
private readonly logger;
|
|
11
|
+
private readonly materializerOptions;
|
|
12
|
+
private readonly retentionOptions;
|
|
13
|
+
constructor(store: MaterializerStore, tasks: ScheduledTaskRepository, logger: Logger, config: SchedulerConfig);
|
|
14
|
+
materialize(): Promise<MaterializerSummary>;
|
|
15
|
+
prune(): Promise<RetentionSummary>;
|
|
16
|
+
}
|