@orion-js/dogs 3.1.22 → 3.1.25
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/lib/defineJob/index.js +5 -0
- package/lib/history.test.js +15 -28
- package/lib/repos/JobsHistoryRepo.js +9 -1
- package/lib/repos/JobsRepo.d.ts +2 -0
- package/lib/repos/JobsRepo.js +9 -4
- package/lib/services/EventsService.js +1 -1
- package/lib/services/Executor.d.ts +1 -1
- package/lib/services/Executor.js +12 -6
- package/lib/services/WorkerService.d.ts +4 -3
- package/lib/services/WorkerService.js +33 -13
- package/lib/stale.test.d.ts +1 -0
- package/lib/stale.test.js +108 -0
- package/lib/types/HistoryRecord.d.ts +1 -0
- package/lib/types/HistoryRecord.js +4 -0
- package/lib/types/JobsDefinition.d.ts +1 -1
- package/lib/types/Worker.d.ts +9 -1
- package/package.json +3 -3
package/lib/defineJob/index.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.defineJob = void 0;
|
|
4
4
|
const defineJob = (options) => {
|
|
5
|
+
if (options.type === 'recurrent') {
|
|
6
|
+
if (!options.hasOwnProperty('priority')) {
|
|
7
|
+
options.priority = 100;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
5
10
|
return options;
|
|
6
11
|
};
|
|
7
12
|
exports.defineJob = defineJob;
|
package/lib/history.test.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const helpers_1 = require("@orion-js/helpers");
|
|
4
4
|
const _1 = require(".");
|
|
5
5
|
const logger_1 = require("@orion-js/logger");
|
|
6
|
-
(0, logger_1.setLogLevel)('
|
|
6
|
+
(0, logger_1.setLogLevel)('none');
|
|
7
7
|
describe('Test Jobs History', () => {
|
|
8
8
|
it('Should save success history types', async () => {
|
|
9
9
|
const jobId = (0, helpers_1.generateId)();
|
|
@@ -34,10 +34,11 @@ describe('Test Jobs History', () => {
|
|
|
34
34
|
expect(execution.duration).toBeGreaterThan(49);
|
|
35
35
|
expect(execution).toEqual({
|
|
36
36
|
_id: expect.any(String),
|
|
37
|
+
jobId: expect.any(String),
|
|
37
38
|
executionId: expect.any(String),
|
|
38
39
|
jobName: jobId,
|
|
39
40
|
type: 'event',
|
|
40
|
-
priority:
|
|
41
|
+
priority: 100,
|
|
41
42
|
tries: 1,
|
|
42
43
|
startedAt: expect.any(Date),
|
|
43
44
|
endedAt: expect.any(Date),
|
|
@@ -74,10 +75,11 @@ describe('Test Jobs History', () => {
|
|
|
74
75
|
const execution = executions[0];
|
|
75
76
|
expect(execution).toEqual({
|
|
76
77
|
_id: expect.any(String),
|
|
78
|
+
jobId: expect.any(String),
|
|
77
79
|
executionId: expect.any(String),
|
|
78
80
|
jobName: jobId,
|
|
79
81
|
type: 'event',
|
|
80
|
-
priority:
|
|
82
|
+
priority: 100,
|
|
81
83
|
tries: 1,
|
|
82
84
|
startedAt: expect.any(Date),
|
|
83
85
|
endedAt: expect.any(Date),
|
|
@@ -93,8 +95,10 @@ describe('Test Jobs History', () => {
|
|
|
93
95
|
const jobId = (0, helpers_1.generateId)();
|
|
94
96
|
const job = (0, _1.defineJob)({
|
|
95
97
|
type: 'event',
|
|
96
|
-
async resolve() {
|
|
97
|
-
|
|
98
|
+
async resolve(_, context) {
|
|
99
|
+
if (context.tries === 1) {
|
|
100
|
+
await (0, helpers_1.sleep)(100);
|
|
101
|
+
}
|
|
98
102
|
return { status: 'ok' };
|
|
99
103
|
}
|
|
100
104
|
});
|
|
@@ -112,15 +116,16 @@ describe('Test Jobs History', () => {
|
|
|
112
116
|
await instance.stop();
|
|
113
117
|
const executions = await _1.jobsHistoryRepo.getExecutions(jobId);
|
|
114
118
|
expect(executions.length).toBe(2);
|
|
115
|
-
const
|
|
116
|
-
expect(
|
|
117
|
-
expect(
|
|
118
|
-
expect(
|
|
119
|
+
const execution = executions.find(e => e.status === 'stale');
|
|
120
|
+
expect(execution.duration).toBeGreaterThanOrEqual(100);
|
|
121
|
+
expect(execution.duration).toBeLessThan(200);
|
|
122
|
+
expect(execution).toEqual({
|
|
119
123
|
_id: expect.any(String),
|
|
124
|
+
jobId: expect.any(String),
|
|
120
125
|
executionId: expect.any(String),
|
|
121
126
|
jobName: jobId,
|
|
122
127
|
type: 'event',
|
|
123
|
-
priority:
|
|
128
|
+
priority: 100,
|
|
124
129
|
tries: 1,
|
|
125
130
|
startedAt: expect.any(Date),
|
|
126
131
|
endedAt: expect.any(Date),
|
|
@@ -129,24 +134,6 @@ describe('Test Jobs History', () => {
|
|
|
129
134
|
status: 'stale',
|
|
130
135
|
errorMessage: null,
|
|
131
136
|
params: null,
|
|
132
|
-
result: null
|
|
133
|
-
});
|
|
134
|
-
const execution2 = executions[0];
|
|
135
|
-
expect(execution2.duration).toBeGreaterThanOrEqual(100);
|
|
136
|
-
expect(execution2).toEqual({
|
|
137
|
-
_id: expect.any(String),
|
|
138
|
-
executionId: expect.any(String),
|
|
139
|
-
jobName: jobId,
|
|
140
|
-
type: 'event',
|
|
141
|
-
priority: 1,
|
|
142
|
-
tries: 1,
|
|
143
|
-
startedAt: expect.any(Date),
|
|
144
|
-
endedAt: expect.any(Date),
|
|
145
|
-
duration: expect.any(Number),
|
|
146
|
-
expiresAt: expect.any(Date),
|
|
147
|
-
status: 'success',
|
|
148
|
-
errorMessage: null,
|
|
149
|
-
params: null,
|
|
150
137
|
result: { status: 'ok' }
|
|
151
138
|
});
|
|
152
139
|
});
|
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.JobsHistoryRepo = void 0;
|
|
10
10
|
const mongodb_1 = require("@orion-js/mongodb");
|
|
11
11
|
const services_1 = require("@orion-js/services");
|
|
12
|
+
const lodash_1 = require("lodash");
|
|
12
13
|
const HistoryRecord_1 = require("../types/HistoryRecord");
|
|
13
14
|
let JobsHistoryRepo = class JobsHistoryRepo {
|
|
14
15
|
constructor() {
|
|
@@ -34,7 +35,14 @@ let JobsHistoryRepo = class JobsHistoryRepo {
|
|
|
34
35
|
});
|
|
35
36
|
}
|
|
36
37
|
async saveExecution(record) {
|
|
37
|
-
await this.history.
|
|
38
|
+
await this.history.upsert({ executionId: record.executionId }, {
|
|
39
|
+
$setOnInsert: {
|
|
40
|
+
status: record.status
|
|
41
|
+
},
|
|
42
|
+
$set: {
|
|
43
|
+
...(0, lodash_1.omit)(record, 'status')
|
|
44
|
+
}
|
|
45
|
+
});
|
|
38
46
|
}
|
|
39
47
|
async getExecutions(jobName, limit, skip) {
|
|
40
48
|
const cursor = this.history.find({ jobName }).sort({ startedAt: -1 });
|
package/lib/repos/JobsRepo.d.ts
CHANGED
|
@@ -5,10 +5,12 @@ import { JobToRun } from '../types/Worker';
|
|
|
5
5
|
export declare class JobsRepo {
|
|
6
6
|
jobs: import("@orion-js/mongodb").Collection<JobRecord>;
|
|
7
7
|
getJobAndLock(jobNames: string[], lockTime: number): Promise<JobToRun>;
|
|
8
|
+
setJobRecordPriority(jobId: string, priority: number): Promise<void>;
|
|
8
9
|
scheduleNextRun(options: {
|
|
9
10
|
jobId: string;
|
|
10
11
|
nextRunAt: Date;
|
|
11
12
|
addTries: boolean;
|
|
13
|
+
priority: number;
|
|
12
14
|
}): Promise<void>;
|
|
13
15
|
deleteEventJob(jobId: string): Promise<void>;
|
|
14
16
|
extendLockTime(jobId: string, extraTime: number): Promise<void>;
|
package/lib/repos/JobsRepo.js
CHANGED
|
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
};
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.JobsRepo = void 0;
|
|
10
|
+
const helpers_1 = require("@orion-js/helpers");
|
|
10
11
|
const logger_1 = require("@orion-js/logger");
|
|
11
12
|
const mongodb_1 = require("@orion-js/mongodb");
|
|
12
13
|
const services_1 = require("@orion-js/services");
|
|
@@ -22,7 +23,7 @@ let JobsRepo = class JobsRepo {
|
|
|
22
23
|
keys: {
|
|
23
24
|
jobName: 1,
|
|
24
25
|
nextRunAt: 1,
|
|
25
|
-
priority: 1,
|
|
26
|
+
priority: -1,
|
|
26
27
|
lockedUntil: 1
|
|
27
28
|
}
|
|
28
29
|
},
|
|
@@ -58,7 +59,7 @@ let JobsRepo = class JobsRepo {
|
|
|
58
59
|
}, {
|
|
59
60
|
mongoOptions: {
|
|
60
61
|
sort: {
|
|
61
|
-
priority: 1,
|
|
62
|
+
priority: -1,
|
|
62
63
|
nextRunAt: 1
|
|
63
64
|
},
|
|
64
65
|
returnDocument: 'before'
|
|
@@ -74,6 +75,7 @@ let JobsRepo = class JobsRepo {
|
|
|
74
75
|
}
|
|
75
76
|
return {
|
|
76
77
|
jobId: job._id,
|
|
78
|
+
executionId: (0, helpers_1.generateId)(),
|
|
77
79
|
name: job.jobName,
|
|
78
80
|
params: job.params,
|
|
79
81
|
type: job.type,
|
|
@@ -83,9 +85,12 @@ let JobsRepo = class JobsRepo {
|
|
|
83
85
|
uniqueIdentifier: job.uniqueIdentifier
|
|
84
86
|
};
|
|
85
87
|
}
|
|
88
|
+
async setJobRecordPriority(jobId, priority) {
|
|
89
|
+
await this.jobs.updateOne(jobId, { $set: { priority } });
|
|
90
|
+
}
|
|
86
91
|
async scheduleNextRun(options) {
|
|
87
92
|
const updator = {
|
|
88
|
-
$set: { nextRunAt: options.nextRunAt },
|
|
93
|
+
$set: { nextRunAt: options.nextRunAt, priority: options.priority },
|
|
89
94
|
$unset: { lockedUntil: '' }
|
|
90
95
|
};
|
|
91
96
|
if (options.addTries) {
|
|
@@ -111,7 +116,7 @@ let JobsRepo = class JobsRepo {
|
|
|
111
116
|
}, {
|
|
112
117
|
$set: {
|
|
113
118
|
type: job.type,
|
|
114
|
-
priority: job.priority
|
|
119
|
+
priority: job.priority
|
|
115
120
|
},
|
|
116
121
|
$setOnInsert: {
|
|
117
122
|
nextRunAt: new Date()
|
|
@@ -19,7 +19,7 @@ let EventsService = class EventsService {
|
|
|
19
19
|
logger_1.logger.debug('Scheduling job...', options);
|
|
20
20
|
await this.jobsRepo.scheduleJob({
|
|
21
21
|
name: options.name,
|
|
22
|
-
priority: options.priority ||
|
|
22
|
+
priority: options.priority || 100,
|
|
23
23
|
nextRunAt: (0, getNextRunDate_1.getNextRunDate)(options),
|
|
24
24
|
params: options.params || null,
|
|
25
25
|
uniqueIdentifier: options.uniqueIdentifier
|
|
@@ -16,5 +16,5 @@ export declare class Executor {
|
|
|
16
16
|
jobToRun: JobToRun;
|
|
17
17
|
}): Promise<void>;
|
|
18
18
|
afterExecutionSuccess(job: JobDefinition, jobToRun: JobToRun, context: ExecutionContext): Promise<void>;
|
|
19
|
-
executeJob(jobs: JobsDefinition, jobToRun: JobToRun): Promise<void>;
|
|
19
|
+
executeJob(jobs: JobsDefinition, jobToRun: JobToRun, respawnWorker: Function): Promise<void>;
|
|
20
20
|
}
|
package/lib/services/Executor.js
CHANGED
|
@@ -48,7 +48,8 @@ let Executor = class Executor {
|
|
|
48
48
|
await this.jobsRepo.scheduleNextRun({
|
|
49
49
|
jobId: jobToRun.jobId,
|
|
50
50
|
nextRunAt: (0, getNextRunDate_1.getNextRunDate)(job),
|
|
51
|
-
addTries: false
|
|
51
|
+
addTries: false,
|
|
52
|
+
priority: job.priority
|
|
52
53
|
});
|
|
53
54
|
}
|
|
54
55
|
};
|
|
@@ -69,7 +70,8 @@ let Executor = class Executor {
|
|
|
69
70
|
await this.jobsRepo.scheduleNextRun({
|
|
70
71
|
jobId: jobToRun.jobId,
|
|
71
72
|
nextRunAt: (0, getNextRunDate_1.getNextRunDate)(result),
|
|
72
|
-
addTries: true
|
|
73
|
+
addTries: true,
|
|
74
|
+
priority: job.type === 'recurrent' ? job.priority : jobToRun.priority
|
|
73
75
|
});
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -80,7 +82,8 @@ let Executor = class Executor {
|
|
|
80
82
|
const oneWeek = 1000 * 60 * 60 * 24 * 7;
|
|
81
83
|
const saveExecutionsFor = job.saveExecutionsFor || oneWeek;
|
|
82
84
|
await this.jobsHistoryRepo.saveExecution({
|
|
83
|
-
|
|
85
|
+
jobId: jobToRun.jobId,
|
|
86
|
+
executionId: jobToRun.executionId,
|
|
84
87
|
jobName: jobToRun.name,
|
|
85
88
|
type: jobToRun.type,
|
|
86
89
|
priority: jobToRun.priority,
|
|
@@ -103,7 +106,8 @@ let Executor = class Executor {
|
|
|
103
106
|
await this.jobsRepo.scheduleNextRun({
|
|
104
107
|
jobId: jobToRun.jobId,
|
|
105
108
|
nextRunAt: (0, getNextRunDate_1.getNextRunDate)(job),
|
|
106
|
-
addTries: false
|
|
109
|
+
addTries: false,
|
|
110
|
+
priority: job.priority
|
|
107
111
|
});
|
|
108
112
|
}
|
|
109
113
|
if (job.type === 'event') {
|
|
@@ -111,12 +115,12 @@ let Executor = class Executor {
|
|
|
111
115
|
await this.jobsRepo.deleteEventJob(jobToRun.jobId);
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
|
-
async executeJob(jobs, jobToRun) {
|
|
118
|
+
async executeJob(jobs, jobToRun, respawnWorker) {
|
|
115
119
|
const job = this.getJobDefinition(jobToRun, jobs);
|
|
116
120
|
if (!job)
|
|
117
121
|
return;
|
|
118
122
|
const startedAt = new Date();
|
|
119
|
-
const onStale = () => {
|
|
123
|
+
const onStale = async () => {
|
|
120
124
|
if (job.onStale) {
|
|
121
125
|
context.logger.info(`Job "${jobToRun.name}" is stale`);
|
|
122
126
|
job.onStale(jobToRun.params, context);
|
|
@@ -124,6 +128,8 @@ let Executor = class Executor {
|
|
|
124
128
|
else {
|
|
125
129
|
context.logger.error(`Job "${jobToRun.name}" is stale`);
|
|
126
130
|
}
|
|
131
|
+
await this.jobsRepo.setJobRecordPriority(jobToRun.jobId, 0);
|
|
132
|
+
respawnWorker();
|
|
127
133
|
this.saveExecution({
|
|
128
134
|
startedAt,
|
|
129
135
|
status: 'stale',
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { JobDefinitionWithName, JobsDefinition } from '../types/JobsDefinition';
|
|
2
2
|
import { StartWorkersConfig } from '../types/StartConfig';
|
|
3
|
-
import { WorkersInstance } from '../types/Worker';
|
|
3
|
+
import { WorkerInstance, WorkersInstance } from '../types/Worker';
|
|
4
4
|
export declare class WorkerService {
|
|
5
5
|
private jobsRepo;
|
|
6
6
|
private executor;
|
|
7
7
|
getJobNames(jobs: JobsDefinition): string[];
|
|
8
8
|
getJobs(jobs: JobsDefinition): JobDefinitionWithName[];
|
|
9
|
-
runWorkerLoop(config: StartWorkersConfig): Promise<boolean>;
|
|
10
|
-
startWorker(config: StartWorkersConfig,
|
|
9
|
+
runWorkerLoop(config: StartWorkersConfig, workerInstance: WorkerInstance): Promise<boolean>;
|
|
10
|
+
startWorker(config: StartWorkersConfig, workerInstance: WorkerInstance): Promise<void>;
|
|
11
11
|
createWorkersInstanceDefinition(config: StartWorkersConfig): WorkersInstance;
|
|
12
12
|
ensureRecords(config: StartWorkersConfig): Promise<void>;
|
|
13
|
+
startANewWorker(config: StartWorkersConfig, workersInstance: WorkersInstance): Promise<void>;
|
|
13
14
|
runWorkers(config: StartWorkersConfig, workersInstance: WorkersInstance): Promise<void>;
|
|
14
15
|
startWorkers(userConfig: Partial<StartWorkersConfig>): WorkersInstance;
|
|
15
16
|
}
|
|
@@ -28,33 +28,33 @@ let WorkerService = class WorkerService {
|
|
|
28
28
|
};
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
|
-
async runWorkerLoop(config) {
|
|
31
|
+
async runWorkerLoop(config, workerInstance) {
|
|
32
32
|
const names = this.getJobNames(config.jobs);
|
|
33
|
-
logger_1.logger.debug(`Running worker loop for jobs "${names.join(', ')}"...`);
|
|
33
|
+
logger_1.logger.debug(`Running worker loop [w${workerInstance.workerIndex}] for jobs "${names.join(', ')}"...`);
|
|
34
34
|
const jobToRun = await this.jobsRepo.getJobAndLock(names, config.lockTime);
|
|
35
35
|
if (!jobToRun) {
|
|
36
36
|
logger_1.logger.debug('No job to run');
|
|
37
37
|
return false;
|
|
38
38
|
}
|
|
39
|
-
logger_1.logger.debug(`Got job to run:`, jobToRun);
|
|
40
|
-
await this.executor.executeJob(config.jobs, jobToRun);
|
|
39
|
+
logger_1.logger.debug(`Got job [w${workerInstance.workerIndex}] to run:`, jobToRun);
|
|
40
|
+
await this.executor.executeJob(config.jobs, jobToRun, workerInstance.respawn);
|
|
41
41
|
return true;
|
|
42
42
|
}
|
|
43
|
-
async startWorker(config,
|
|
43
|
+
async startWorker(config, workerInstance) {
|
|
44
44
|
while (true) {
|
|
45
|
-
if (!
|
|
46
|
-
logger_1.logger.info(
|
|
45
|
+
if (!workerInstance.running) {
|
|
46
|
+
logger_1.logger.info(`Got signal to stop. Stopping worker [w${workerInstance.workerIndex}]...`);
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
try {
|
|
50
|
-
const didRun = await this.runWorkerLoop(config);
|
|
50
|
+
const didRun = await this.runWorkerLoop(config, workerInstance);
|
|
51
51
|
if (!didRun)
|
|
52
52
|
await (0, helpers_1.sleep)(config.pollInterval);
|
|
53
53
|
if (didRun)
|
|
54
54
|
await (0, helpers_1.sleep)(config.cooldownPeriod);
|
|
55
55
|
}
|
|
56
56
|
catch (error) {
|
|
57
|
-
logger_1.logger.error(`Error in job runner
|
|
57
|
+
logger_1.logger.error(`Error in job runner.`, error);
|
|
58
58
|
await (0, helpers_1.sleep)(config.pollInterval);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -67,7 +67,8 @@ let WorkerService = class WorkerService {
|
|
|
67
67
|
stop: async () => {
|
|
68
68
|
logger_1.logger.debug('Stopping workers...', workersInstance.workers);
|
|
69
69
|
workersInstance.running = false;
|
|
70
|
-
|
|
70
|
+
const stopingPromises = workersInstance.workers.map(worker => worker.stop());
|
|
71
|
+
await Promise.all(stopingPromises);
|
|
71
72
|
}
|
|
72
73
|
};
|
|
73
74
|
return workersInstance;
|
|
@@ -81,13 +82,32 @@ let WorkerService = class WorkerService {
|
|
|
81
82
|
await this.jobsRepo.ensureJobRecord(job);
|
|
82
83
|
}));
|
|
83
84
|
}
|
|
85
|
+
async startANewWorker(config, workersInstance) {
|
|
86
|
+
const workerIndex = workersInstance.workers.length;
|
|
87
|
+
logger_1.logger.info(`Starting worker [w${workerIndex}]`);
|
|
88
|
+
const workerInstance = {
|
|
89
|
+
running: true,
|
|
90
|
+
workerIndex,
|
|
91
|
+
stop: async () => {
|
|
92
|
+
logger_1.logger.info(`Stopping worker [w${workerIndex}]...`);
|
|
93
|
+
workerInstance.running = false;
|
|
94
|
+
await workerInstance.promise;
|
|
95
|
+
},
|
|
96
|
+
respawn: async () => {
|
|
97
|
+
logger_1.logger.info(`Respawning worker [w${workerIndex}]...`);
|
|
98
|
+
workerInstance.stop();
|
|
99
|
+
await this.startANewWorker(config, workersInstance);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const workerPromise = this.startWorker(config, workerInstance);
|
|
103
|
+
workerInstance.promise = workerPromise;
|
|
104
|
+
workersInstance.workers.push(workerInstance);
|
|
105
|
+
}
|
|
84
106
|
async runWorkers(config, workersInstance) {
|
|
85
107
|
logger_1.logger.debug('Will ensure records for recurrent jobs');
|
|
86
108
|
await this.ensureRecords(config);
|
|
87
109
|
for (const workerIndex of (0, lodash_1.range)(config.workersCount)) {
|
|
88
|
-
|
|
89
|
-
const workerPromise = this.startWorker(config, workersInstance);
|
|
90
|
-
workersInstance.workers.push(workerPromise);
|
|
110
|
+
this.startANewWorker(config, workersInstance);
|
|
91
111
|
}
|
|
92
112
|
}
|
|
93
113
|
startWorkers(userConfig) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const helpers_1 = require("@orion-js/helpers");
|
|
4
|
+
const logger_1 = require("@orion-js/logger");
|
|
5
|
+
const _1 = require(".");
|
|
6
|
+
(0, logger_1.setLogLevel)('none');
|
|
7
|
+
describe('Stale Jobs Management', () => {
|
|
8
|
+
it('Should spawn a new worker when a job is stale and kill the stale worker after it ends', async () => {
|
|
9
|
+
const jobName1 = 'job1' + (0, helpers_1.generateId)();
|
|
10
|
+
const job1 = (0, _1.defineJob)({
|
|
11
|
+
type: 'event',
|
|
12
|
+
async resolve(_, context) {
|
|
13
|
+
if (context.tries === 1) {
|
|
14
|
+
await (0, helpers_1.sleep)(100);
|
|
15
|
+
}
|
|
16
|
+
return { status: 'finished' };
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const jobName2 = 'job2' + (0, helpers_1.generateId)();
|
|
20
|
+
const job2 = (0, _1.defineJob)({
|
|
21
|
+
type: 'event',
|
|
22
|
+
async resolve() {
|
|
23
|
+
return { status: 'finished' };
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const instance = (0, _1.startWorkers)({
|
|
27
|
+
jobs: { [jobName1]: job1, [jobName2]: job2 },
|
|
28
|
+
workersCount: 1,
|
|
29
|
+
pollInterval: 5,
|
|
30
|
+
cooldownPeriod: 5,
|
|
31
|
+
lockTime: 10
|
|
32
|
+
});
|
|
33
|
+
await (0, _1.scheduleJob)({ name: jobName1 });
|
|
34
|
+
await (0, _1.scheduleJob)({ name: jobName2 });
|
|
35
|
+
await (0, helpers_1.sleep)(150);
|
|
36
|
+
await instance.stop();
|
|
37
|
+
const executions1 = await _1.jobsHistoryRepo.getExecutions(jobName1);
|
|
38
|
+
expect(executions1.length).toBe(2);
|
|
39
|
+
const executions2 = await _1.jobsHistoryRepo.getExecutions(jobName2);
|
|
40
|
+
expect(executions2.length).toBe(1);
|
|
41
|
+
expect(instance.workers.length).toBe(2);
|
|
42
|
+
});
|
|
43
|
+
it('Should run stale jobs in the lowest priority', async () => {
|
|
44
|
+
const executions = [];
|
|
45
|
+
const priotities = [];
|
|
46
|
+
const jobName1 = 'job1' + (0, helpers_1.generateId)();
|
|
47
|
+
const job1 = (0, _1.defineJob)({
|
|
48
|
+
type: 'event',
|
|
49
|
+
async resolve(_, context) {
|
|
50
|
+
executions.push('stale');
|
|
51
|
+
priotities.push(context.record.priority);
|
|
52
|
+
if (context.tries === 1) {
|
|
53
|
+
await (0, helpers_1.sleep)(100);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const jobName2 = 'job2' + (0, helpers_1.generateId)();
|
|
58
|
+
const job2 = (0, _1.defineJob)({
|
|
59
|
+
type: 'event',
|
|
60
|
+
async resolve() {
|
|
61
|
+
executions.push('success');
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const instance = (0, _1.startWorkers)({
|
|
65
|
+
jobs: { [jobName1]: job1, [jobName2]: job2 },
|
|
66
|
+
workersCount: 1,
|
|
67
|
+
pollInterval: 5,
|
|
68
|
+
cooldownPeriod: 5,
|
|
69
|
+
lockTime: 10
|
|
70
|
+
});
|
|
71
|
+
await (0, _1.scheduleJob)({ name: jobName1 });
|
|
72
|
+
await (0, _1.scheduleJob)({ name: jobName2 });
|
|
73
|
+
await (0, helpers_1.sleep)(150);
|
|
74
|
+
await instance.stop();
|
|
75
|
+
expect(priotities).toEqual([100, 0]);
|
|
76
|
+
expect(executions).toEqual(['stale', 'success', 'stale']);
|
|
77
|
+
});
|
|
78
|
+
it('Should revert to original priority when execution was stale on recurrent jobs', async () => {
|
|
79
|
+
const priotities = [];
|
|
80
|
+
let didStale = false;
|
|
81
|
+
const jobName = 'job' + (0, helpers_1.generateId)();
|
|
82
|
+
const job = (0, _1.defineJob)({
|
|
83
|
+
type: 'recurrent',
|
|
84
|
+
runEvery: 5,
|
|
85
|
+
async resolve(_, context) {
|
|
86
|
+
priotities.push(context.record.priority);
|
|
87
|
+
if (!didStale) {
|
|
88
|
+
didStale = true;
|
|
89
|
+
await (0, helpers_1.sleep)(100);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
const instance = (0, _1.startWorkers)({
|
|
94
|
+
jobs: { [jobName]: job },
|
|
95
|
+
workersCount: 1,
|
|
96
|
+
pollInterval: 5,
|
|
97
|
+
cooldownPeriod: 5,
|
|
98
|
+
lockTime: 10
|
|
99
|
+
});
|
|
100
|
+
await (0, helpers_1.sleep)(150);
|
|
101
|
+
await instance.stop();
|
|
102
|
+
const [first, second, ...others] = priotities;
|
|
103
|
+
expect(first).toBe(100);
|
|
104
|
+
expect(second).toBe(0);
|
|
105
|
+
expect(others.length).toBeGreaterThan(0);
|
|
106
|
+
expect(others).toEqual(Array(others.length).fill(100));
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -17,6 +17,10 @@ __decorate([
|
|
|
17
17
|
(0, typed_model_1.Prop)(),
|
|
18
18
|
__metadata("design:type", String)
|
|
19
19
|
], HistoryRecord.prototype, "_id", void 0);
|
|
20
|
+
__decorate([
|
|
21
|
+
(0, typed_model_1.Prop)(),
|
|
22
|
+
__metadata("design:type", String)
|
|
23
|
+
], HistoryRecord.prototype, "jobId", void 0);
|
|
20
24
|
__decorate([
|
|
21
25
|
(0, typed_model_1.Prop)(),
|
|
22
26
|
__metadata("design:type", String)
|
|
@@ -42,7 +42,7 @@ export interface RecurrentJobDefinition extends BaseJobDefinition {
|
|
|
42
42
|
*/
|
|
43
43
|
runEvery?: number;
|
|
44
44
|
/**
|
|
45
|
-
* The priority of the job. Higher is more priority. Default is
|
|
45
|
+
* The priority of the job. Higher is more priority. Default is 100.
|
|
46
46
|
*/
|
|
47
47
|
priority?: number;
|
|
48
48
|
}
|
package/lib/types/Worker.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { PlainObject } from './HistoryRecord';
|
|
|
3
3
|
import { JobDefinition } from './JobsDefinition';
|
|
4
4
|
export interface JobToRun {
|
|
5
5
|
jobId: string;
|
|
6
|
+
executionId: string;
|
|
6
7
|
name: string;
|
|
7
8
|
type: 'event' | 'recurrent';
|
|
8
9
|
params: PlainObject;
|
|
@@ -19,10 +20,17 @@ export interface ExecutionContext {
|
|
|
19
20
|
extendLockTime: (extraTime: number) => Promise<void>;
|
|
20
21
|
clearStaleTimeout: () => void;
|
|
21
22
|
}
|
|
23
|
+
export interface WorkerInstance {
|
|
24
|
+
running: boolean;
|
|
25
|
+
workerIndex: number;
|
|
26
|
+
stop: () => Promise<void>;
|
|
27
|
+
respawn: () => Promise<void>;
|
|
28
|
+
promise?: Promise<any>;
|
|
29
|
+
}
|
|
22
30
|
export interface WorkersInstance {
|
|
23
31
|
running: boolean;
|
|
24
32
|
workersCount: number;
|
|
25
|
-
workers:
|
|
33
|
+
workers: WorkerInstance[];
|
|
26
34
|
/**
|
|
27
35
|
* Stop all workers and wait for them to finish
|
|
28
36
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orion-js/dogs",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.25",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@orion-js/helpers": "^3.1.12",
|
|
21
|
-
"@orion-js/mongodb": "^3.1.
|
|
21
|
+
"@orion-js/mongodb": "^3.1.25",
|
|
22
22
|
"@orion-js/services": "^3.1.12",
|
|
23
23
|
"@orion-js/typed-model": "^3.1.13"
|
|
24
24
|
},
|
|
@@ -39,5 +39,5 @@
|
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "48f30c51fea96518fab7fedf7643d6493f4f56bc"
|
|
43
43
|
}
|