@cleverbrush/scheduler 1.0.0-beta.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 ADDED
@@ -0,0 +1 @@
1
+ # Task scheduler for NodeJS
@@ -0,0 +1,9 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { Readable } from 'stream';
4
+ export declare class CloneReadableStream extends Readable {
5
+ private _chunks;
6
+ constructor(source: Readable);
7
+ toBuffer(): Buffer;
8
+ _read(): void;
9
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloneReadableStream.d.ts","sourceRoot":"","sources":["../src/CloneReadableStream.ts"],"names":[],"mappings":";;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,qBAAa,mBAAoB,SAAQ,QAAQ;IAC7C,OAAO,CAAC,OAAO,CAAM;gBAET,MAAM,EAAE,QAAQ;IAarB,QAAQ,IAAI,MAAM;IAKzB,KAAK;CACR"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CloneReadableStream = void 0;
4
+ const stream_1 = require("stream");
5
+ class CloneReadableStream extends stream_1.Readable {
6
+ _chunks = [];
7
+ constructor(source) {
8
+ super();
9
+ if (!source)
10
+ throw new Error('source is required');
11
+ source.on('data', (chunk) => {
12
+ this.push(chunk);
13
+ this._chunks.push(chunk);
14
+ });
15
+ source.on('end', () => this.push(null));
16
+ source.on('error', (err) => this.emit('error', err));
17
+ }
18
+ toBuffer() {
19
+ return Buffer.concat(this._chunks);
20
+ }
21
+ // eslint-disable-next-line
22
+ _read() { }
23
+ }
24
+ exports.CloneReadableStream = CloneReadableStream;
25
+ //# sourceMappingURL=CloneReadableStream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloneReadableStream.js","sourceRoot":"","sources":["../src/CloneReadableStream.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAElC,MAAa,mBAAoB,SAAQ,iBAAQ;IACrC,OAAO,GAAG,EAAE,CAAC;IAErB,YAAY,MAAgB;QACxB,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAEnD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YACxB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IACzD,CAAC;IAEM,QAAQ;QACX,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,2BAA2B;IAC3B,KAAK,KAAI,CAAC;CACb;AAtBD,kDAsBC"}
@@ -0,0 +1,10 @@
1
+ import { Schedule } from './types.js';
2
+ export declare class ScheduleCalculator {
3
+ #private;
4
+ constructor(schedule: Schedule);
5
+ hasNext(span?: number): boolean;
6
+ next(): {
7
+ date: Date;
8
+ index: number;
9
+ };
10
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScheduleCalculator.d.ts","sourceRoot":"","sources":["../src/ScheduleCalculator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAiBtC,qBAAa,kBAAkB;;gBAWf,QAAQ,EAAE,QAAQ;IA+PvB,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO;IAY/B,IAAI,IAAI;QACX,IAAI,EAAE,IAAI,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;KACjB;CA0BJ"}
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScheduleCalculator = void 0;
4
+ const MS_IN_DAY = 1000 * 60 * 60 * 24;
5
+ const MS_IN_WEEK = MS_IN_DAY * 7;
6
+ const getDayOfWeek = (date) => {
7
+ const res = date.getUTCDay();
8
+ if (res === 0)
9
+ return 7;
10
+ return res;
11
+ };
12
+ const getNumberOfDaysInMonth = (date) => {
13
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 0, 0, 0, 0, 0)).getDate();
14
+ };
15
+ class ScheduleCalculator {
16
+ #schedule;
17
+ #currentDate = new Date();
18
+ #hour = 9;
19
+ #minute = 0;
20
+ #maxRepeat = -1;
21
+ #repeatCount = 0;
22
+ #hasNext = false;
23
+ #next;
24
+ constructor(schedule) {
25
+ if (!schedule)
26
+ throw new Error('schedule is required');
27
+ this.#schedule = { ...schedule };
28
+ if (typeof schedule.startsOn !== 'undefined') {
29
+ this.#currentDate = schedule.startsOn;
30
+ }
31
+ else {
32
+ this.#schedule.startsOn = new Date();
33
+ this.#currentDate = this.#schedule.startsOn;
34
+ }
35
+ if (schedule.every !== 'minute') {
36
+ if (typeof schedule.hour === 'number') {
37
+ this.#hour = schedule.hour;
38
+ }
39
+ if (typeof schedule.minute === 'number') {
40
+ this.#minute = schedule.minute;
41
+ }
42
+ if (schedule.every === 'day' &&
43
+ new Date(Date.UTC(this.#currentDate.getUTCFullYear(), this.#currentDate.getUTCMonth(), this.#currentDate.getUTCDate(), this.#hour, this.#minute, 0, 0)).getTime() < this.#currentDate.getTime()) {
44
+ const date = new Date(Date.UTC(this.#currentDate.getUTCFullYear(), this.#currentDate.getUTCMonth(), this.#currentDate.getUTCDate() + 1, this.#hour, this.#minute, 0, 0));
45
+ this.#currentDate = date;
46
+ }
47
+ }
48
+ if (typeof schedule.maxOccurences === 'number') {
49
+ this.#maxRepeat = schedule.maxOccurences;
50
+ }
51
+ const next = this.#getNext();
52
+ if (typeof next !== 'undefined') {
53
+ this.#next = next;
54
+ this.#hasNext = true;
55
+ }
56
+ let leftToSkip = this.#schedule.startingFromIndex || 1;
57
+ while (leftToSkip-- > 1 && this.#hasNext) {
58
+ this.next();
59
+ }
60
+ }
61
+ #getNext() {
62
+ let candidate = null;
63
+ let dayOfWeek;
64
+ switch (this.#schedule.every) {
65
+ case 'minute':
66
+ candidate =
67
+ this.#repeatCount === 0
68
+ ? this.#schedule.startsOn
69
+ : new Date(Date.UTC(this.#currentDate.getUTCFullYear(), this.#currentDate.getUTCMonth(), this.#currentDate.getUTCDate(), this.#currentDate.getUTCHours(), this.#currentDate.getUTCMinutes() +
70
+ this.#schedule.interval, this.#currentDate.getUTCSeconds(), this.#currentDate.getUTCMilliseconds()));
71
+ break;
72
+ case 'day':
73
+ {
74
+ const date = new Date(this.#currentDate.getTime() +
75
+ MS_IN_DAY *
76
+ (this.#repeatCount === 0
77
+ ? 0
78
+ : this.#schedule.interval - 1));
79
+ candidate = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), this.#hour, this.#minute, 0, 0));
80
+ }
81
+ break;
82
+ case 'week':
83
+ {
84
+ let date = this.#currentDate;
85
+ do {
86
+ let found = false;
87
+ dayOfWeek = getDayOfWeek(date);
88
+ if (Number.isNaN(dayOfWeek))
89
+ return;
90
+ for (let i = 0; i < 7 - dayOfWeek + 1; i++) {
91
+ date = new Date(date.getTime() + (i == 0 ? 0 : MS_IN_DAY));
92
+ if (this.#schedule.endsOn &&
93
+ date > this.#schedule.endsOn) {
94
+ return;
95
+ }
96
+ if (this.#schedule.dayOfWeek.includes(getDayOfWeek(date))) {
97
+ const dateWithTime = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), this.#hour, this.#minute, 0, 0));
98
+ if (dateWithTime > this.#schedule.startsOn) {
99
+ candidate = dateWithTime;
100
+ found = true;
101
+ break;
102
+ }
103
+ }
104
+ }
105
+ if (found)
106
+ break;
107
+ date = new Date(date.getTime() +
108
+ MS_IN_DAY +
109
+ (this.#repeatCount == 0
110
+ ? 0
111
+ : (this.#schedule.interval - 1) *
112
+ MS_IN_WEEK));
113
+ } while ((this.#schedule.endsOn &&
114
+ date <= this.#schedule.endsOn) ||
115
+ !this.#schedule.endsOn);
116
+ }
117
+ break;
118
+ case 'month':
119
+ {
120
+ let dateTime;
121
+ const cDate = this.#currentDate;
122
+ let iteration = 0;
123
+ do {
124
+ const date = this.#schedule.day === 'last'
125
+ ? getNumberOfDaysInMonth(new Date(Date.UTC(cDate.getUTCFullYear(), cDate.getUTCMonth() +
126
+ iteration *
127
+ (this.#repeatCount === 0
128
+ ? 1
129
+ : this.#schedule
130
+ .interval), 1, 0, 0, 0, 0)))
131
+ : this.#schedule.day;
132
+ dateTime = new Date(Date.UTC(cDate.getUTCFullYear(), cDate.getUTCMonth() +
133
+ iteration *
134
+ (this.#repeatCount === 0
135
+ ? 1
136
+ : this.#schedule.interval), date, this.#hour, this.#minute, 0, 0));
137
+ iteration++;
138
+ } while (dateTime < this.#schedule.startsOn ||
139
+ dateTime <= this.#currentDate);
140
+ candidate = dateTime;
141
+ }
142
+ break;
143
+ case 'year':
144
+ {
145
+ let dateTime;
146
+ const cDate = this.#currentDate;
147
+ let iteration = 0;
148
+ do {
149
+ const date = this.#schedule.day === 'last'
150
+ ? getNumberOfDaysInMonth(new Date(Date.UTC(cDate.getUTCFullYear() +
151
+ iteration *
152
+ (this.#repeatCount === 0
153
+ ? 1
154
+ : this.#schedule
155
+ .interval), this.#schedule.month - 1, 1, 0, 0, 0, 0)))
156
+ : this.#schedule.day;
157
+ dateTime = new Date(Date.UTC(cDate.getUTCFullYear() +
158
+ iteration *
159
+ (this.#repeatCount === 0
160
+ ? 1
161
+ : this.#schedule.interval), this.#schedule.month - 1, date, this.#hour, this.#minute, 0, 0));
162
+ iteration++;
163
+ } while (dateTime < this.#schedule.startsOn ||
164
+ dateTime <= this.#currentDate);
165
+ candidate = dateTime;
166
+ }
167
+ break;
168
+ default:
169
+ throw new Error('unknown schedule type');
170
+ }
171
+ if (!candidate)
172
+ return;
173
+ if (typeof this.#schedule.endsOn !== 'undefined' &&
174
+ candidate > this.#schedule.endsOn) {
175
+ return;
176
+ }
177
+ return candidate;
178
+ }
179
+ hasNext(span) {
180
+ if (!this.#hasNext) {
181
+ return false;
182
+ }
183
+ if (typeof span !== 'number')
184
+ return this.#hasNext;
185
+ return this.#next.getTime() - new Date().getTime() <= span;
186
+ }
187
+ next() {
188
+ if (!this.#hasNext)
189
+ throw new Error('schedule is over');
190
+ const result = this.#next;
191
+ this.#currentDate = new Date(result.getTime() +
192
+ (['day', 'week'].includes(this.#schedule.every) ? MS_IN_DAY : 0));
193
+ this.#repeatCount++;
194
+ const next = this.#getNext();
195
+ this.#next = next;
196
+ this.#hasNext = typeof next !== 'undefined';
197
+ if (this.#maxRepeat > 0 && this.#repeatCount >= this.#maxRepeat) {
198
+ this.#next = undefined;
199
+ this.#hasNext = false;
200
+ }
201
+ return {
202
+ date: result,
203
+ index: this.#repeatCount
204
+ };
205
+ }
206
+ }
207
+ exports.ScheduleCalculator = ScheduleCalculator;
208
+ //# sourceMappingURL=ScheduleCalculator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScheduleCalculator.js","sourceRoot":"","sources":["../src/ScheduleCalculator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { EventEmitter } from 'events';
4
+ import { Readable } from 'stream';
5
+ import { JobSchedulerProps, CreateJobRequest, SchedulerStatus, Schedule, Job, JobInstance, JobInstanceStatus, Schemas } from './types.js';
6
+ import { ScheduleCalculator } from './ScheduleCalculator.js';
7
+ import { IJobRepository } from './jobRepository.js';
8
+ export { ScheduleCalculator, Schedule as TaskSchedule, Schemas };
9
+ declare type WorkerResult = {
10
+ status: JobInstanceStatus;
11
+ exitCode: number;
12
+ };
13
+ declare type JobStartItem = {
14
+ jobId: string;
15
+ instanceId: number;
16
+ stdout: Readable;
17
+ stderr: Readable;
18
+ startDate: Date;
19
+ };
20
+ declare type JobEndItem = JobStartItem & {
21
+ endDate: Date;
22
+ };
23
+ declare type Events = {
24
+ 'job:start': (job: JobStartItem) => any;
25
+ 'job:end': (job: JobEndItem) => any;
26
+ };
27
+ interface IJobScheduler {
28
+ on<T extends keyof Events>(name: T, callback: Events[T]): this;
29
+ }
30
+ export declare class JobScheduler extends EventEmitter implements IJobScheduler {
31
+ protected _rootFolder: string;
32
+ protected _status: SchedulerStatus;
33
+ protected _defaultTimezone: string;
34
+ protected _checkTimer: any;
35
+ protected _jobsRepository: IJobRepository;
36
+ protected _jobProps: Map<string, any>;
37
+ get status(): SchedulerStatus;
38
+ protected set status(val: SchedulerStatus);
39
+ private scheduleCalculatorCache;
40
+ protected getJobSchedule(job: Job): Promise<ScheduleCalculator>;
41
+ protected runWorkerWithTimeout(file: string, props: any, timeout: number): {
42
+ promise: Promise<WorkerResult>;
43
+ stderr: Readable;
44
+ stdout: Readable;
45
+ };
46
+ private readToEnd;
47
+ protected startJobInstance(instance: JobInstance): Promise<void>;
48
+ protected scheduleJobTo(job: Job, date: Date, index: number): Promise<JobInstance | null>;
49
+ protected checkForUpcomingJobs(): Promise<void>;
50
+ start(): Promise<void>;
51
+ stop(): void;
52
+ addJob(job: CreateJobRequest): Promise<void>;
53
+ constructor(props: JobSchedulerProps);
54
+ on<T extends keyof Events>(name: T, callback: Events[T]): this;
55
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAe,MAAM,QAAQ,CAAC;AAO/C,OAAO,EAEH,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,GAAG,EACH,WAAW,EACX,iBAAiB,EACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,EAAE,cAAc,EAAyB,MAAM,oBAAoB,CAAC;AAE3E,aAAK,YAAY,GAAG;IAChB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAQF,aAAK,YAAY,GAAG;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;CACnB,CAAC;AAEF,aAAK,UAAU,GAAG,YAAY,GAAG;IAC7B,OAAO,EAAE,IAAI,CAAC;CACjB,CAAC;AAEF,aAAK,MAAM,GAAG;IACV,WAAW,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,GAAG,CAAC;IACxC,SAAS,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,GAAG,CAAC;CACvC,CAAC;AAEF,UAAU,aAAa;IACnB,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAClE;AAED,qBAAa,YAAa,SAAQ,YAAa,YAAW,aAAa;IACnE,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC;IAC9B,SAAS,CAAC,OAAO,EAAE,eAAe,CAAa;IAC/C,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAEnC,SAAS,CAAC,WAAW,MAAC;IAEtB,SAAS,CAAC,eAAe,EAAE,cAAc,CAA+B;IAExE,SAAS,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAA0B;IAE/D,IAAW,MAAM,IAIS,eAAe,CAFxC;IAED,SAAS,KAAK,MAAM,CAAC,GAAG,EAAE,eAAe,EAGxC;cAEe,cAAc,CAAC,GAAG,EAAE,GAAG;IAgBvC,SAAS,CAAC,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM;;;;;IA0CxE,OAAO,CAAC,SAAS;cASD,gBAAgB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;cAgHtD,aAAa,CACzB,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;cAoCd,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BxC,KAAK;IAeX,IAAI;IAWE,MAAM,CAAC,GAAG,EAAE,gBAAgB;gBAgC7B,KAAK,EAAE,iBAAiB;IAqB7B,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI;CAIxE"}
package/dist/index.js ADDED
@@ -0,0 +1,307 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.JobScheduler = exports.Schemas = exports.ScheduleCalculator = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const events_1 = require("events");
9
+ const stream_1 = require("stream");
10
+ const promises_1 = require("fs/promises");
11
+ const path_1 = require("path");
12
+ const worker_threads_1 = require("worker_threads");
13
+ const types_js_1 = require("./types.js");
14
+ Object.defineProperty(exports, "Schemas", { enumerable: true, get: function () { return types_js_1.Schemas; } });
15
+ const ScheduleCalculator_js_1 = require("./ScheduleCalculator.js");
16
+ Object.defineProperty(exports, "ScheduleCalculator", { enumerable: true, get: function () { return ScheduleCalculator_js_1.ScheduleCalculator; } });
17
+ const jobRepository_js_1 = require("./jobRepository.js");
18
+ const CHECK_INTERVAL = 1000 * 10; // every 10 seconds
19
+ // const SCHEDULE_JOB_SPAN = 1000 * 60 * 60; // 1 hour
20
+ const SCHEDULE_JOB_SPAN = 1000 * 60; // 1 hour
21
+ const DEFAULT_JOB_TIMEOUT = 1000 * 20; // 20 seconds
22
+ const DEFAULT_MAX_CONSEQUENT_FAILS = 3;
23
+ class JobScheduler extends events_1.EventEmitter {
24
+ _rootFolder;
25
+ _status = 'stopped';
26
+ _defaultTimezone;
27
+ _checkTimer;
28
+ _jobsRepository = new jobRepository_js_1.InMemoryJobRepository();
29
+ _jobProps = new Map();
30
+ get status() {
31
+ return this._status;
32
+ }
33
+ set status(val) {
34
+ if (val === this._status)
35
+ return;
36
+ this._status = val;
37
+ }
38
+ scheduleCalculatorCache = new Map();
39
+ async getJobSchedule(job) {
40
+ if (this.scheduleCalculatorCache.has(job.id)) {
41
+ return this.scheduleCalculatorCache.get(job.id);
42
+ }
43
+ const schedule = {
44
+ ...job.schedule
45
+ };
46
+ if (typeof job.firstInstanceEndedAt !== 'undefined') {
47
+ schedule.startsOn = job.firstInstanceEndedAt;
48
+ }
49
+ if (typeof job.successfullTimesRunned === 'number') {
50
+ schedule.startingFromIndex = job.successfullTimesRunned + 1;
51
+ }
52
+ const res = new ScheduleCalculator_js_1.ScheduleCalculator(schedule);
53
+ this.scheduleCalculatorCache.set(job.id, res);
54
+ return res;
55
+ }
56
+ runWorkerWithTimeout(file, props, timeout) {
57
+ const worker = new worker_threads_1.Worker(file, {
58
+ workerData: props,
59
+ execArgv: ['--unhandled-rejections=strict'],
60
+ stderr: true,
61
+ stdout: true
62
+ });
63
+ const promise = new Promise((resolve) => {
64
+ let timedOut = false;
65
+ let isFinished = false;
66
+ const timeoutTimer = setTimeout(() => {
67
+ if (isFinished)
68
+ return;
69
+ timedOut = true;
70
+ worker.terminate();
71
+ resolve({
72
+ exitCode: 1,
73
+ status: 'timedout'
74
+ });
75
+ }, timeout);
76
+ worker.on('error', (e) => e);
77
+ worker.on('exit', (exitCode) => {
78
+ if (isFinished)
79
+ return;
80
+ if (timedOut)
81
+ return;
82
+ clearTimeout(timeoutTimer);
83
+ isFinished = true;
84
+ resolve({
85
+ status: exitCode === 0 ? 'succeeded' : 'errored',
86
+ exitCode
87
+ });
88
+ });
89
+ });
90
+ return {
91
+ promise,
92
+ stderr: worker.stderr,
93
+ stdout: worker.stdout
94
+ };
95
+ }
96
+ readToEnd(source) {
97
+ return new Promise((res, rej) => {
98
+ const chunks = [];
99
+ source.on('data', (chunk) => chunks.push(chunk));
100
+ source.on('end', () => res(Buffer.concat(chunks)));
101
+ source.on('error', (err) => rej(err));
102
+ });
103
+ }
104
+ async startJobInstance(instance) {
105
+ const startDate = new Date();
106
+ instance = await this._jobsRepository.saveInstance({
107
+ ...instance,
108
+ status: 'running',
109
+ startDate
110
+ });
111
+ let status, exitCode;
112
+ try {
113
+ const job = await this._jobsRepository.getJobById(instance.jobId);
114
+ const fileName = (0, path_1.join)(this._rootFolder, job.path);
115
+ const props = this._jobProps.get(job.id);
116
+ let finalProps = typeof props === 'function' ? props() : props;
117
+ if (finalProps instanceof Promise) {
118
+ finalProps = await finalProps;
119
+ }
120
+ instance = await this._jobsRepository.saveInstance({
121
+ ...instance,
122
+ status: 'running'
123
+ });
124
+ const { promise, stderr, stdout } = this.runWorkerWithTimeout(fileName, finalProps, job.timeout);
125
+ const stdOutPass = new stream_1.PassThrough();
126
+ stdout.pipe(stdOutPass);
127
+ const stdErrPass = new stream_1.PassThrough();
128
+ stderr.pipe(stdErrPass);
129
+ const stdOutForJobStart = stdout.pipe(new stream_1.PassThrough());
130
+ const stdErrForJobStart = stderr.pipe(new stream_1.PassThrough());
131
+ const stdOutForJobEnd = stdout.pipe(new stream_1.PassThrough());
132
+ const stdErrForJobEnd = stderr.pipe(new stream_1.PassThrough());
133
+ this.emit('job:start', {
134
+ instanceId: instance.id,
135
+ jobId: job.id,
136
+ stderr: stdErrForJobStart,
137
+ stdout: stdOutForJobStart,
138
+ startDate
139
+ });
140
+ const stdOutStr = (await this.readToEnd(stdOutPass)).toString();
141
+ const stdErrStr = (await this.readToEnd(stdErrPass)).toString();
142
+ const result = await promise;
143
+ status = result.status;
144
+ exitCode = result.exitCode;
145
+ const endDate = new Date();
146
+ this.emit('job:end', {
147
+ instanceId: instance.id,
148
+ jobId: job.id,
149
+ stderr: stdErrForJobEnd,
150
+ stdout: stdOutForJobEnd,
151
+ startDate,
152
+ endDate
153
+ });
154
+ instance = await this._jobsRepository.saveInstance({
155
+ ...instance,
156
+ status,
157
+ exitCode,
158
+ stdErr: stdErrStr,
159
+ stdOut: stdOutStr,
160
+ endDate
161
+ });
162
+ }
163
+ finally {
164
+ const job = {
165
+ ...(await this._jobsRepository.getJobById(instance.jobId))
166
+ };
167
+ job.timesRunned++;
168
+ let shouldRetry = false;
169
+ const schedule = await this.getJobSchedule(job);
170
+ if (status !== 'succeeded') {
171
+ if (job.consequentFailsCount + 1 >= job.maxConsequentFails) {
172
+ job.status = 'disabled';
173
+ }
174
+ else {
175
+ job.consequentFailsCount += 1;
176
+ shouldRetry = true;
177
+ }
178
+ }
179
+ else {
180
+ job.successfullTimesRunned++;
181
+ job.consequentFailsCount = 0;
182
+ if (!schedule.hasNext()) {
183
+ job.status = 'finished';
184
+ }
185
+ }
186
+ await this._jobsRepository.saveJob(job);
187
+ if (shouldRetry) {
188
+ await this.startJobInstance(instance);
189
+ }
190
+ }
191
+ }
192
+ async scheduleJobTo(job, date, index) {
193
+ let timer;
194
+ try {
195
+ const now = new Date();
196
+ let interval = date.getTime() - now.getTime();
197
+ if (interval < 0) {
198
+ interval = 0;
199
+ }
200
+ const instance = await this._jobsRepository.addInstance(job.id, {
201
+ scheduledTo: date,
202
+ status: 'scheduled',
203
+ timeout: job.timeout,
204
+ index
205
+ });
206
+ timer = setTimeout(async () => {
207
+ const actualJob = await this._jobsRepository.getJobById(job.id);
208
+ if (actualJob.status !== 'active') {
209
+ instance.status = 'canceled';
210
+ await this._jobsRepository.saveInstance(instance);
211
+ return;
212
+ }
213
+ await this.startJobInstance(instance);
214
+ }, interval);
215
+ return instance;
216
+ }
217
+ catch (e) {
218
+ clearTimeout(timer);
219
+ // console.log(e);
220
+ // console.log('task failed!');
221
+ return null;
222
+ }
223
+ }
224
+ async checkForUpcomingJobs() {
225
+ const jobs = await this._jobsRepository.getJobs();
226
+ for (let i = 0; i < jobs.length; i++) {
227
+ if (jobs[i].status !== 'active')
228
+ continue;
229
+ const schedule = await this.getJobSchedule(jobs[i]);
230
+ if (schedule.hasNext()) {
231
+ const scheduledInstances = await this._jobsRepository.getInstancesWithStatus(jobs[i].id, 'scheduled');
232
+ while (schedule.hasNext(SCHEDULE_JOB_SPAN)) {
233
+ const { date: nextRun, index } = schedule.next();
234
+ const alreadyScheduled = scheduledInstances.find((i) => i.index === index);
235
+ if (alreadyScheduled)
236
+ continue;
237
+ await this.scheduleJobTo(jobs[i], nextRun, index);
238
+ }
239
+ }
240
+ else {
241
+ jobs[i].status = 'finished';
242
+ await this._jobsRepository.saveJob(jobs[i]);
243
+ }
244
+ }
245
+ }
246
+ async start() {
247
+ if (this._status === 'started') {
248
+ throw new Error('Scheduler is already started');
249
+ }
250
+ this.status = 'started';
251
+ this._checkTimer = setInterval(this.checkForUpcomingJobs.bind(this), CHECK_INTERVAL);
252
+ // TODO: add logic
253
+ }
254
+ stop() {
255
+ if (this._status === 'stopped') {
256
+ throw new Error('Scheduler is already stopped');
257
+ }
258
+ clearInterval(this._checkTimer);
259
+ this.status = 'stopped';
260
+ // TODO: add logic
261
+ }
262
+ async addJob(job) {
263
+ const validationResult = await types_js_1.schemaRegistry.schemas.Models.CreateJobRequest.validate(job);
264
+ if (!validationResult.valid) {
265
+ throw new Error(`Invalid CreateJobRequest: ${validationResult.errors?.join('; ')}`);
266
+ }
267
+ const path = (0, path_1.join)(this._rootFolder, job.path);
268
+ await (0, promises_1.access)(path, fs_1.default.constants.R_OK);
269
+ this._jobProps.set(job.id, job.props);
270
+ await this._jobsRepository.createJob({
271
+ id: job.id,
272
+ createdAt: new Date(),
273
+ schedule: job.schedule,
274
+ timeout: job.timeout || DEFAULT_JOB_TIMEOUT,
275
+ path: job.path,
276
+ consequentFailsCount: 0,
277
+ timesRunned: 0,
278
+ successfullTimesRunned: 0,
279
+ maxConsequentFails: typeof job.maxConsequentFails === 'number'
280
+ ? job.maxConsequentFails
281
+ : DEFAULT_MAX_CONSEQUENT_FAILS
282
+ });
283
+ }
284
+ constructor(props) {
285
+ super();
286
+ if (typeof props.rootFolder !== 'string') {
287
+ throw new Error('rootFolder must be a string');
288
+ }
289
+ if (typeof props.defaultTimeZone === 'string') {
290
+ this._defaultTimezone = props.defaultTimeZone;
291
+ }
292
+ if (typeof props.persistRepository === 'object') {
293
+ this._jobsRepository = props.persistRepository;
294
+ }
295
+ this._rootFolder = props.rootFolder;
296
+ setInterval(() => {
297
+ this._jobsRepository.dumpJobs();
298
+ this._jobsRepository.dumpInstances();
299
+ }, 10 * 1000);
300
+ }
301
+ on(name, callback) {
302
+ super.on(name, callback);
303
+ return this;
304
+ }
305
+ }
306
+ exports.JobScheduler = JobScheduler;
307
+ //# sourceMappingURL=index.js.map