@cleverbrush/scheduler 1.0.0-beta.3 → 1.0.0-beta.5
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/dist/ScheduleCalculator.d.ts +10 -0
- package/dist/ScheduleCalculator.js +210 -0
- package/dist/ScheduleCalculator.js.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +376 -0
- package/dist/index.js.map +1 -0
- package/dist/jobRepository.d.ts +34 -0
- package/dist/jobRepository.js +90 -0
- package/dist/jobRepository.js.map +1 -0
- package/dist/types.d.ts +378 -0
- package/dist/types.js +128 -0
- package/dist/types.js.map +1 -0
- package/package.json +6 -2
- package/src/ScheduleCalculator.test.ts +5 -3
- package/src/ScheduleCalculator.ts +5 -2
- package/src/index.ts +37 -14
- package/src/types.ts +10 -3
package/dist/types.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Schemas = exports.schemaRegistry = void 0;
|
|
4
|
+
const schema_1 = require("@cleverbrush/schema");
|
|
5
|
+
const registry = new schema_1.SchemaRegistry()
|
|
6
|
+
.addPreprocessor('StringToDate', (value) => {
|
|
7
|
+
if (typeof value === 'undefined')
|
|
8
|
+
return value;
|
|
9
|
+
if (typeof value === 'string') {
|
|
10
|
+
const time = Date.parse(value);
|
|
11
|
+
if (Number.isNaN(time))
|
|
12
|
+
return value;
|
|
13
|
+
return new Date(time);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
})
|
|
17
|
+
.addSchema('Common.Date', ({ object }) => object()
|
|
18
|
+
.mapToType()
|
|
19
|
+
.addValidator((value) => value instanceof Date && !Number.isNaN(value)
|
|
20
|
+
? {
|
|
21
|
+
valid: true
|
|
22
|
+
}
|
|
23
|
+
: {
|
|
24
|
+
valid: false,
|
|
25
|
+
errors: ['should be a valid Date object']
|
|
26
|
+
}))
|
|
27
|
+
.addSchema('Templates.Schedule', ({ object, number, alias }) => object({
|
|
28
|
+
/** Number of days between repeats */
|
|
29
|
+
interval: number().min(1).max(356),
|
|
30
|
+
/** Hour (0-23) */
|
|
31
|
+
hour: number().min(0).max(23).optional(),
|
|
32
|
+
/** Minute (0-59) */
|
|
33
|
+
minute: number().min(0).max(59).optional(),
|
|
34
|
+
/** Do not start earlier than this date */
|
|
35
|
+
startsOn: alias('Common.Date').optional(),
|
|
36
|
+
/** Do not repeat after this date */
|
|
37
|
+
endsOn: alias('Common.Date').optional(),
|
|
38
|
+
/** Max number of repeats (min 1) */
|
|
39
|
+
maxOccurences: number().min(1).optional(),
|
|
40
|
+
/** Skip this number of repeats. Min value is 1. */
|
|
41
|
+
skipFirst: number().min(1).optional()
|
|
42
|
+
})
|
|
43
|
+
.setPropPreprocessor('endsOn', 'StringToDate')
|
|
44
|
+
.addValidator((val) => {
|
|
45
|
+
if ('endsOn' in val &&
|
|
46
|
+
'maxOccurences' in val &&
|
|
47
|
+
// TODO: remove this clause when object validator is fixed in a new version
|
|
48
|
+
typeof val.endsOf !== 'undefined') {
|
|
49
|
+
return {
|
|
50
|
+
valid: false,
|
|
51
|
+
errors: ['either endsOn or maxOccurences is required']
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return { valid: true };
|
|
55
|
+
}))
|
|
56
|
+
.addSchemaFrom('Templates.Schedule', 'Models.TaskScheduleMinute', ({ string, base }) => base
|
|
57
|
+
.removeProp('hour')
|
|
58
|
+
.removeProp('minute')
|
|
59
|
+
.addProps({
|
|
60
|
+
/** Repeat every minute */
|
|
61
|
+
every: string('minute')
|
|
62
|
+
}))
|
|
63
|
+
.addSchemaFrom('Templates.Schedule', 'Models.TaskScheduleDay', ({ string, base }) => base.addProps({
|
|
64
|
+
/** Repeat every day */
|
|
65
|
+
every: string('day')
|
|
66
|
+
}))
|
|
67
|
+
.addSchemaFrom('Templates.Schedule', 'Models.TaskScheduleWeek', ({ base, number, string, array }) => base.addProps({
|
|
68
|
+
/** Repeat every week */
|
|
69
|
+
every: string('week'),
|
|
70
|
+
/** Day of week: array of no more than 7 numbers numbers (from 1 to 7 where 1 is Monday). */
|
|
71
|
+
dayOfWeek: array()
|
|
72
|
+
.ofType(number().min(1).max(7))
|
|
73
|
+
.minLength(1)
|
|
74
|
+
.maxLength(7)
|
|
75
|
+
.addValidator((val) => {
|
|
76
|
+
const map = {};
|
|
77
|
+
for (let i = 0; i < val.length; i++) {
|
|
78
|
+
if (map[val[i]]) {
|
|
79
|
+
return {
|
|
80
|
+
valid: false,
|
|
81
|
+
errors: ['no duplicates allowed']
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
map[val[i]] = true;
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
valid: true
|
|
88
|
+
};
|
|
89
|
+
})
|
|
90
|
+
}))
|
|
91
|
+
.addSchemaFrom('Templates.Schedule', 'Models.TaskScheduleMonth', ({ base, string, number, union }) => base.addProps({
|
|
92
|
+
/** Repeat every month */
|
|
93
|
+
every: string('month'),
|
|
94
|
+
/** Day - 'last' or number from 1 to 28 */
|
|
95
|
+
day: union(string('last'), number().min(1).max(28))
|
|
96
|
+
}))
|
|
97
|
+
.addSchemaFrom('Templates.Schedule', 'Models.TaskScheduleYear', ({ base, string, number, union }) => base.addProps({
|
|
98
|
+
/** Repeat every year */
|
|
99
|
+
every: string('year'),
|
|
100
|
+
/** Day - 'last' or number from 1 to 28 */
|
|
101
|
+
day: union(string('last')).or(number().min(1).max(28)),
|
|
102
|
+
/** Month - number from 1 to 12 */
|
|
103
|
+
month: number().min(1).max(12)
|
|
104
|
+
}));
|
|
105
|
+
exports.schemaRegistry = registry
|
|
106
|
+
.addSchema('Models.Schedule', ({ alias, union }) => union(alias('Models.TaskScheduleMinute'), alias('Models.TaskScheduleDay'), alias('Models.TaskScheduleWeek'), alias('Models.TaskScheduleMonth'), alias('Models.TaskScheduleYear')))
|
|
107
|
+
.addSchema('Models.CreateJobRequest', ({ object, number, string, alias, union, func }) => object({
|
|
108
|
+
/** Id of job, must be uniq */
|
|
109
|
+
id: string(),
|
|
110
|
+
/** Path to js file (relative to root folder) */
|
|
111
|
+
path: string().minLength(1),
|
|
112
|
+
/** Job's schedule */
|
|
113
|
+
schedule: alias('Models.Schedule'),
|
|
114
|
+
/** Timeout for job (in milliseconds) */
|
|
115
|
+
timeout: number().min(0).optional(),
|
|
116
|
+
/** Arbitrary props for job (can be a callback returning props or Promise<props>) */
|
|
117
|
+
props: union(object().canHaveUnknownProps(), func()).optional(),
|
|
118
|
+
/** Job will be considered as disabled when more than that count of runs fails consequently
|
|
119
|
+
* unlimited if negative
|
|
120
|
+
*/
|
|
121
|
+
maxConsequentFails: number().optional(),
|
|
122
|
+
/**
|
|
123
|
+
* Job will be retried right away this times. Job will be retried on next schedule run if this number is exceeded.
|
|
124
|
+
*/
|
|
125
|
+
maxRetries: number().optional().min(1)
|
|
126
|
+
}));
|
|
127
|
+
exports.Schemas = exports.schemaRegistry;
|
|
128
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleverbrush/scheduler",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.5",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"task scheduler"
|
|
6
6
|
],
|
|
7
7
|
"author": "Andrew Zolotukhin <andrew_zol@cleverbrush.com>",
|
|
8
8
|
"license": "BSD",
|
|
9
|
+
"files": [
|
|
10
|
+
"*",
|
|
11
|
+
"dist/*"
|
|
12
|
+
],
|
|
9
13
|
"main": "./dist/index.js",
|
|
10
14
|
"readme": "https://github.com/cleverbrush/libs/tree/master/libs/scheduler#readme",
|
|
11
15
|
"scripts": {
|
|
@@ -17,7 +21,7 @@
|
|
|
17
21
|
"url": "github:cleverbrush/libs"
|
|
18
22
|
},
|
|
19
23
|
"dependencies": {
|
|
20
|
-
"@cleverbrush/schema": "1.0.0-beta.
|
|
24
|
+
"@cleverbrush/schema": "1.0.0-beta.5"
|
|
21
25
|
},
|
|
22
26
|
"types": "./dist/index.d.ts"
|
|
23
27
|
}
|
|
@@ -86,7 +86,7 @@ test('day - 4', () => {
|
|
|
86
86
|
interval: 2,
|
|
87
87
|
startsOn: new Date(Date.UTC(2022, 9, 12, 21, 0, 0, 0)),
|
|
88
88
|
endsOn: new Date(Date.UTC(2022, 11, 12, 0, 0, 0, 0)),
|
|
89
|
-
|
|
89
|
+
skipFirst: 2,
|
|
90
90
|
maxOccurences: 10
|
|
91
91
|
});
|
|
92
92
|
|
|
@@ -102,8 +102,10 @@ test('day - 4', () => {
|
|
|
102
102
|
];
|
|
103
103
|
|
|
104
104
|
for (let i = 0; i < results.length; i++) {
|
|
105
|
-
|
|
106
|
-
expect(
|
|
105
|
+
const hasNext = calculator.hasNext();
|
|
106
|
+
expect(hasNext).toEqual(true);
|
|
107
|
+
const next = calculator.next();
|
|
108
|
+
expect(next.date.getTime()).toEqual(results[i].getTime());
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
expect(calculator.hasNext()).toEqual(false);
|
|
@@ -86,9 +86,12 @@ export class ScheduleCalculator {
|
|
|
86
86
|
this.#hasNext = true;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
let leftToSkip =
|
|
89
|
+
let leftToSkip =
|
|
90
|
+
typeof this.#schedule.skipFirst === 'number'
|
|
91
|
+
? this.#schedule.skipFirst
|
|
92
|
+
: 0;
|
|
90
93
|
|
|
91
|
-
while (leftToSkip-- >
|
|
94
|
+
while (leftToSkip-- > 0 && this.#hasNext) {
|
|
92
95
|
this.next();
|
|
93
96
|
}
|
|
94
97
|
}
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ const CHECK_INTERVAL = 1000 * 10; // every 10 seconds
|
|
|
35
35
|
const SCHEDULE_JOB_SPAN = 1000 * 60; // 1 hour
|
|
36
36
|
const DEFAULT_JOB_TIMEOUT = 1000 * 20; // 20 seconds
|
|
37
37
|
const DEFAULT_MAX_CONSEQUENT_FAILS = 3;
|
|
38
|
+
const DEFAULT_MAX_RETRIES = 2;
|
|
38
39
|
|
|
39
40
|
type JobStartItem = {
|
|
40
41
|
jobId: string;
|
|
@@ -53,11 +54,16 @@ type JobErrorItem = JobStartItem & {
|
|
|
53
54
|
error?: Error;
|
|
54
55
|
};
|
|
55
56
|
|
|
57
|
+
type JobMessageItem = Omit<JobStartItem, 'stdout' | 'stderr'> & {
|
|
58
|
+
value: any;
|
|
59
|
+
};
|
|
60
|
+
|
|
56
61
|
type Events = {
|
|
57
62
|
'job:start': (job: JobStartItem) => any;
|
|
58
63
|
'job:end': (job: JobEndItem) => any;
|
|
59
64
|
'job:error': (job: JobErrorItem) => any;
|
|
60
65
|
'job:timeout': (job: JobErrorItem) => any;
|
|
66
|
+
'job:message': (msg: JobMessageItem) => any;
|
|
61
67
|
};
|
|
62
68
|
|
|
63
69
|
interface IJobScheduler {
|
|
@@ -102,7 +108,7 @@ export class JobScheduler extends EventEmitter implements IJobScheduler {
|
|
|
102
108
|
}
|
|
103
109
|
|
|
104
110
|
if (typeof job.successfullTimesRunned === 'number') {
|
|
105
|
-
schedule.
|
|
111
|
+
schedule.skipFirst = job.successfullTimesRunned - 1;
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
const res = new ScheduleCalculator(schedule);
|
|
@@ -152,8 +158,7 @@ export class JobScheduler extends EventEmitter implements IJobScheduler {
|
|
|
152
158
|
|
|
153
159
|
return {
|
|
154
160
|
promise,
|
|
155
|
-
|
|
156
|
-
stdout: worker.stdout
|
|
161
|
+
worker
|
|
157
162
|
};
|
|
158
163
|
}
|
|
159
164
|
|
|
@@ -193,45 +198,54 @@ export class JobScheduler extends EventEmitter implements IJobScheduler {
|
|
|
193
198
|
status: 'running'
|
|
194
199
|
});
|
|
195
200
|
|
|
196
|
-
const { promise,
|
|
201
|
+
const { promise, worker } = this.runWorkerWithTimeout(
|
|
197
202
|
fileName,
|
|
198
203
|
finalProps,
|
|
199
204
|
job.timeout
|
|
200
205
|
);
|
|
201
206
|
|
|
202
|
-
const stdOutPass = stdout.pipe(
|
|
207
|
+
const stdOutPass = worker.stdout.pipe(
|
|
203
208
|
new PassThrough({
|
|
204
209
|
highWaterMark: MAX_BUFFER_SIZE
|
|
205
210
|
})
|
|
206
211
|
);
|
|
207
|
-
const stdErrPass = stderr.pipe(
|
|
212
|
+
const stdErrPass = worker.stderr.pipe(
|
|
208
213
|
new PassThrough({
|
|
209
214
|
highWaterMark: MAX_BUFFER_SIZE
|
|
210
215
|
})
|
|
211
216
|
);
|
|
212
217
|
|
|
213
|
-
const stdOutForJobStart = stdout.pipe(
|
|
218
|
+
const stdOutForJobStart = worker.stdout.pipe(
|
|
214
219
|
new PassThrough({
|
|
215
220
|
highWaterMark: MAX_BUFFER_SIZE
|
|
216
221
|
})
|
|
217
222
|
);
|
|
218
|
-
const stdErrForJobStart = stderr.pipe(
|
|
223
|
+
const stdErrForJobStart = worker.stderr.pipe(
|
|
219
224
|
new PassThrough({
|
|
220
225
|
highWaterMark: MAX_BUFFER_SIZE
|
|
221
226
|
})
|
|
222
227
|
);
|
|
223
228
|
|
|
224
|
-
const stdOutForJobEnd = stdout.pipe(
|
|
229
|
+
const stdOutForJobEnd = worker.stdout.pipe(
|
|
225
230
|
new PassThrough({
|
|
226
231
|
highWaterMark: MAX_BUFFER_SIZE
|
|
227
232
|
})
|
|
228
233
|
);
|
|
229
|
-
const stdErrForJobEnd = stderr.pipe(
|
|
234
|
+
const stdErrForJobEnd = worker.stderr.pipe(
|
|
230
235
|
new PassThrough({
|
|
231
236
|
highWaterMark: MAX_BUFFER_SIZE
|
|
232
237
|
})
|
|
233
238
|
);
|
|
234
239
|
|
|
240
|
+
worker.on('message', (value) => {
|
|
241
|
+
this.emit('job:message', {
|
|
242
|
+
instanceId: instance.id,
|
|
243
|
+
jobId: job.id,
|
|
244
|
+
startDate,
|
|
245
|
+
value
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
235
249
|
this.emit('job:start', {
|
|
236
250
|
instanceId: instance.id,
|
|
237
251
|
jobId: job.id,
|
|
@@ -323,8 +337,11 @@ export class JobScheduler extends EventEmitter implements IJobScheduler {
|
|
|
323
337
|
|
|
324
338
|
await this._jobsRepository.saveJob(job);
|
|
325
339
|
|
|
326
|
-
if (shouldRetry) {
|
|
327
|
-
await this.startJobInstance(
|
|
340
|
+
if (shouldRetry && instance.retryIndex < instance.maxRetries) {
|
|
341
|
+
await this.startJobInstance({
|
|
342
|
+
...instance,
|
|
343
|
+
retryIndex: instance.retryIndex + 1
|
|
344
|
+
});
|
|
328
345
|
}
|
|
329
346
|
}
|
|
330
347
|
}
|
|
@@ -347,7 +364,9 @@ export class JobScheduler extends EventEmitter implements IJobScheduler {
|
|
|
347
364
|
scheduledTo: date,
|
|
348
365
|
status: 'scheduled',
|
|
349
366
|
timeout: job.timeout,
|
|
350
|
-
index
|
|
367
|
+
index,
|
|
368
|
+
retryIndex: 0,
|
|
369
|
+
maxRetries: job.maxRetries
|
|
351
370
|
});
|
|
352
371
|
|
|
353
372
|
timer = setTimeout(async () => {
|
|
@@ -467,7 +486,11 @@ export class JobScheduler extends EventEmitter implements IJobScheduler {
|
|
|
467
486
|
maxConsequentFails:
|
|
468
487
|
typeof job.maxConsequentFails === 'number'
|
|
469
488
|
? job.maxConsequentFails
|
|
470
|
-
: DEFAULT_MAX_CONSEQUENT_FAILS
|
|
489
|
+
: DEFAULT_MAX_CONSEQUENT_FAILS,
|
|
490
|
+
maxRetries:
|
|
491
|
+
typeof job.maxRetries === 'number'
|
|
492
|
+
? job.maxRetries
|
|
493
|
+
: DEFAULT_MAX_RETRIES
|
|
471
494
|
});
|
|
472
495
|
}
|
|
473
496
|
|
package/src/types.ts
CHANGED
|
@@ -40,8 +40,8 @@ const registry = new SchemaRegistry()
|
|
|
40
40
|
endsOn: alias('Common.Date').optional(),
|
|
41
41
|
/** Max number of repeats (min 1) */
|
|
42
42
|
maxOccurences: number().min(1).optional(),
|
|
43
|
-
/** Skip this number of repeats
|
|
44
|
-
|
|
43
|
+
/** Skip this number of repeats. Min value is 1. */
|
|
44
|
+
skipFirst: number().min(1).optional()
|
|
45
45
|
})
|
|
46
46
|
.setPropPreprocessor('endsOn', 'StringToDate')
|
|
47
47
|
.addValidator((val) => {
|
|
@@ -161,7 +161,11 @@ export const schemaRegistry = registry
|
|
|
161
161
|
/** Job will be considered as disabled when more than that count of runs fails consequently
|
|
162
162
|
* unlimited if negative
|
|
163
163
|
*/
|
|
164
|
-
maxConsequentFails: number().optional()
|
|
164
|
+
maxConsequentFails: number().optional(),
|
|
165
|
+
/**
|
|
166
|
+
* Job will be retried right away this times. Job will be retried on next schedule run if this number is exceeded.
|
|
167
|
+
*/
|
|
168
|
+
maxRetries: number().optional().min(1)
|
|
165
169
|
})
|
|
166
170
|
);
|
|
167
171
|
|
|
@@ -188,6 +192,7 @@ export type Job = {
|
|
|
188
192
|
successfullTimesRunned?: number;
|
|
189
193
|
consequentFailsCount: number;
|
|
190
194
|
maxConsequentFails: number;
|
|
195
|
+
maxRetries: number;
|
|
191
196
|
};
|
|
192
197
|
|
|
193
198
|
export type JobInstance = {
|
|
@@ -202,6 +207,8 @@ export type JobInstance = {
|
|
|
202
207
|
stdOut?: string;
|
|
203
208
|
stdErr?: string;
|
|
204
209
|
exitCode?: number;
|
|
210
|
+
retryIndex: number;
|
|
211
|
+
maxRetries: number;
|
|
205
212
|
};
|
|
206
213
|
|
|
207
214
|
export type JobSchedulerProps = {
|