@tngtech/momo-scheduler
Advanced tools
Comparing version 2.0.1 to 2.0.2
@@ -6,4 +6,7 @@ # Change Log | ||
## v2.0.2 (2023-09-15) | ||
- Fix: prevent a race condition when starting jobs ([#603](https://github.com/TNG/momo-scheduler/issues/603) and [#604](https://github.com/TNG/momo-scheduler/issues/604)) | ||
- Chore: add [mongodb](https://www.npmjs.com/package/mongodb) v6 support | ||
## v2.0.1 (2023-06-12) | ||
- Fix: fixed [CVE](https://github.com/advisories/GHSA-6w63-h3fj-q4vw) in [fast-xml-parser](https://www.npmjs.com/package/fast-xml-parser) | ||
@@ -10,0 +13,0 @@ |
@@ -9,2 +9,6 @@ "use strict"; | ||
exports.SCHEDULES_COLLECTION_NAME = 'schedules'; | ||
const mongoOptions = { | ||
returnDocument: 'after', | ||
includeResultMetadata: true, // ensures backwards compatibility with mongodb <6 | ||
}; | ||
class SchedulesRepository extends Repository_1.Repository { | ||
@@ -40,6 +44,3 @@ constructor(mongoClient, deadScheduleThreshold, scheduleId, name, collectionPrefix) { | ||
}, | ||
}, { | ||
upsert: true, | ||
returnDocument: 'after', | ||
}); | ||
}, Object.assign(Object.assign({}, mongoOptions), { upsert: true })); | ||
return result.value === null ? false : result.value.scheduleId === this.scheduleId; | ||
@@ -84,3 +85,3 @@ } | ||
if (maxRunning < 1) { | ||
const schedule = yield this.collection.findOneAndUpdate({ name: this.name }, { $inc: { [`executions.${name}`]: 1 } }, { returnDocument: 'after' }); | ||
const schedule = yield this.collection.findOneAndUpdate({ name: this.name }, { $inc: { [`executions.${name}`]: 1 } }, mongoOptions); | ||
return { added: schedule.value !== null, running: (_b = (_a = schedule.value) === null || _a === void 0 ? void 0 : _a.executions[name]) !== null && _b !== void 0 ? _b : 0 }; | ||
@@ -91,3 +92,3 @@ } | ||
$or: [{ [`executions.${name}`]: { $lt: maxRunning } }, { [`executions.${name}`]: { $exists: false } }], | ||
}, { $inc: { [`executions.${name}`]: 1 } }, { returnDocument: 'after' }); | ||
}, { $inc: { [`executions.${name}`]: 1 } }, mongoOptions); | ||
return { added: schedule.value !== null, running: (_d = (_c = schedule.value) === null || _c === void 0 ? void 0 : _c.executions[name]) !== null && _d !== void 0 ? _d : maxRunning }; | ||
@@ -94,0 +95,0 @@ }); |
@@ -11,3 +11,3 @@ import { SchedulesRepository } from '../repository/SchedulesRepository'; | ||
private handle?; | ||
private startedJobs; | ||
private startJobsStatus; | ||
constructor(scheduleId: string, scheduleName: string, schedulesRepository: SchedulesRepository, logger: Logger, interval: number, startAllJobs: () => Promise<void>); | ||
@@ -14,0 +14,0 @@ start(): Promise<void>; |
@@ -7,2 +7,8 @@ "use strict"; | ||
const MomoErrorType_1 = require("../logging/error/MomoErrorType"); | ||
var StartJobsStatus; | ||
(function (StartJobsStatus) { | ||
StartJobsStatus[StartJobsStatus["notStarted"] = 0] = "notStarted"; | ||
StartJobsStatus[StartJobsStatus["inProgress"] = 1] = "inProgress"; | ||
StartJobsStatus[StartJobsStatus["finished"] = 2] = "finished"; | ||
})(StartJobsStatus || (StartJobsStatus = {})); | ||
class SchedulePing { | ||
@@ -16,3 +22,3 @@ constructor(scheduleId, scheduleName, schedulesRepository, logger, interval, startAllJobs) { | ||
this.startAllJobs = startAllJobs; | ||
this.startedJobs = false; | ||
this.startJobsStatus = StartJobsStatus.notStarted; | ||
} | ||
@@ -40,6 +46,8 @@ start() { | ||
yield this.schedulesRepository.ping(this.scheduleId); | ||
if (!this.startedJobs) { | ||
this.logger.debug(`This schedule just turned active`); | ||
if (this.startJobsStatus === StartJobsStatus.notStarted) { | ||
this.startJobsStatus = StartJobsStatus.inProgress; | ||
this.logger.debug('This schedule just turned active'); | ||
yield this.startAllJobs(); | ||
this.startedJobs = true; | ||
this.startJobsStatus = StartJobsStatus.finished; | ||
this.logger.debug('Finished starting scheduled jobs'); | ||
} | ||
@@ -46,0 +54,0 @@ } |
{ | ||
"name": "@tngtech/momo-scheduler", | ||
"version": "2.0.1", | ||
"version": "2.0.2", | ||
"description": "momo is a scheduler that persists jobs in mongodb", | ||
@@ -33,44 +33,44 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"cron": "2.3.1", | ||
"cron-parser": "4.8.1", | ||
"cron": "2.4.3", | ||
"cron-parser": "4.9.0", | ||
"human-interval": "2.0.1", | ||
"lodash": "4.17.21", | ||
"luxon": "3.3.0", | ||
"luxon": "3.4.3", | ||
"neverthrow": "6.0.0", | ||
"typed-emitter": "2.1.0", | ||
"uuid": "9.0.0" | ||
"uuid": "9.0.1" | ||
}, | ||
"peerDependencies": { | ||
"mongodb": "4 || 5" | ||
"mongodb": "4 || 5 || 6" | ||
}, | ||
"devDependencies": { | ||
"@sinonjs/fake-timers": "10.2.0", | ||
"@sinonjs/fake-timers": "11.1.0", | ||
"@types/cron": "2.0.1", | ||
"@types/human-interval": "1.0.0", | ||
"@types/jest": "29.5.1", | ||
"@types/lodash": "4.14.195", | ||
"@types/luxon": "3.3.0", | ||
"@types/node": "16.18.34", | ||
"@types/jest": "29.5.4", | ||
"@types/lodash": "4.14.198", | ||
"@types/luxon": "3.3.2", | ||
"@types/node": "16.18.51", | ||
"@types/pino": "7.0.4", | ||
"@types/sinonjs__fake-timers": "8.1.2", | ||
"@types/uuid": "9.0.1", | ||
"@typescript-eslint/eslint-plugin": "5.59.7", | ||
"@typescript-eslint/parser": "5.59.7", | ||
"eslint": "8.42.0", | ||
"eslint-config-prettier": "8.8.0", | ||
"eslint-plugin-import": "2.27.5", | ||
"eslint-plugin-jest": "27.2.1", | ||
"eslint-plugin-jsdoc": "46.2.6", | ||
"eslint-plugin-markdown": "3.0.0", | ||
"@types/uuid": "9.0.4", | ||
"@typescript-eslint/eslint-plugin": "6.7.0", | ||
"@typescript-eslint/parser": "6.7.0", | ||
"eslint": "8.49.0", | ||
"eslint-config-prettier": "9.0.0", | ||
"eslint-plugin-import": "2.28.1", | ||
"eslint-plugin-jest": "27.2.3", | ||
"eslint-plugin-jsdoc": "46.8.0", | ||
"eslint-plugin-markdown": "3.0.1", | ||
"eslint-plugin-prefer-arrow": "1.2.3", | ||
"eslint-plugin-prettier": "4.2.1", | ||
"jest": "29.5.0", | ||
"mongodb-memory-server": "8.13.0", | ||
"pino": "8.14.1", | ||
"prettier": "2.8.8", | ||
"ts-jest": "29.1.0", | ||
"eslint-plugin-prettier": "5.0.0", | ||
"jest": "29.7.0", | ||
"mongodb-memory-server": "8.15.1", | ||
"pino": "8.15.1", | ||
"prettier": "3.0.3", | ||
"ts-jest": "29.1.1", | ||
"ts-mockito": "2.6.1", | ||
"ts-node": "10.9.1", | ||
"typescript": "5.1.3" | ||
"typescript": "5.2.2" | ||
} | ||
} |
@@ -21,3 +21,3 @@ import { MongoClient } from 'mongodb'; | ||
private readonly schedulesRepository: SchedulesRepository, | ||
private readonly jobRepository: JobRepository | ||
private readonly jobRepository: JobRepository, | ||
) {} | ||
@@ -29,3 +29,3 @@ | ||
scheduleId: string, | ||
scheduleName: string | ||
scheduleName: string, | ||
): Promise<Connection> { | ||
@@ -40,3 +40,3 @@ const mongoClient = new MongoClient(url); | ||
scheduleName, | ||
collectionsPrefix | ||
collectionsPrefix, | ||
); | ||
@@ -43,0 +43,0 @@ await schedulesRepository.createIndex(); |
@@ -18,3 +18,3 @@ import { DateTime } from 'luxon'; | ||
private readonly jobRepository: JobRepository, | ||
private readonly logger: Logger | ||
private readonly logger: Logger, | ||
) {} | ||
@@ -63,3 +63,3 @@ | ||
jobEntity: JobEntity, | ||
parameters?: JobParameters | ||
parameters?: JobParameters, | ||
): Promise<{ started: DateTime; result: JobResult }> { | ||
@@ -66,0 +66,0 @@ this.logger.debug('run job', { name: jobEntity.name }); |
@@ -108,5 +108,5 @@ import humanInterval from 'human-interval'; | ||
Schedule extends ParsedIntervalSchedule | CronSchedule, | ||
Type extends JobDefinition<Schedule> | ||
Type extends JobDefinition<Schedule>, | ||
>({ name, schedule, maxRunning, concurrency, parameters }: Type): JobDefinition<Schedule> { | ||
return { name, schedule, maxRunning, concurrency, parameters }; | ||
} |
@@ -20,5 +20,5 @@ import TypedEmitter from 'typed-emitter'; | ||
data?: MomoEventData, | ||
error?: unknown | ||
error?: unknown, | ||
): void { | ||
this.emit('error', { message, type, data, error }); | ||
} |
@@ -8,3 +8,3 @@ import { ObjectId } from 'mongodb'; | ||
export interface JobEntity< | ||
Schedule extends ParsedIntervalSchedule | CronSchedule = ParsedIntervalSchedule | CronSchedule | ||
Schedule extends ParsedIntervalSchedule | CronSchedule = ParsedIntervalSchedule | CronSchedule, | ||
> extends JobDefinition<Schedule> { | ||
@@ -11,0 +11,0 @@ _id?: ObjectId; |
import { DateTime } from 'luxon'; | ||
import { MongoClient } from 'mongodb'; | ||
import { FindOneAndUpdateOptions, MongoClient } from 'mongodb'; | ||
@@ -11,2 +11,7 @@ import { ScheduleEntity } from './ScheduleEntity'; | ||
const mongoOptions: FindOneAndUpdateOptions & { includeResultMetadata: true } = { | ||
returnDocument: 'after', | ||
includeResultMetadata: true, // ensures backwards compatibility with mongodb <6 | ||
}; | ||
export class SchedulesRepository extends Repository<ScheduleEntity> { | ||
@@ -20,3 +25,3 @@ private logger: Logger | undefined; | ||
private readonly name: string, | ||
collectionPrefix?: string | ||
collectionPrefix?: string, | ||
) { | ||
@@ -52,5 +57,5 @@ super(mongoClient, SCHEDULES_COLLECTION_NAME, collectionPrefix); | ||
{ | ||
...mongoOptions, | ||
upsert: true, | ||
returnDocument: 'after', | ||
} | ||
}, | ||
); | ||
@@ -67,3 +72,3 @@ | ||
{ scheduleId: this.scheduleId }, | ||
error | ||
error, | ||
); | ||
@@ -101,3 +106,3 @@ } | ||
{ $inc: { [`executions.${name}`]: 1 } }, | ||
{ returnDocument: 'after' } | ||
mongoOptions, | ||
); | ||
@@ -113,3 +118,3 @@ return { added: schedule.value !== null, running: schedule.value?.executions[name] ?? 0 }; | ||
{ $inc: { [`executions.${name}`]: 1 } }, | ||
{ returnDocument: 'after' } | ||
mongoOptions, | ||
); | ||
@@ -116,0 +121,0 @@ |
@@ -30,3 +30,3 @@ import { v4 as uuid } from 'uuid'; | ||
pingIntervalMs: number, | ||
private readonly scheduleName: string | ||
private readonly scheduleName: string, | ||
) { | ||
@@ -48,3 +48,3 @@ const schedulesRepository = connection.getSchedulesRepository(); | ||
pingIntervalMs, | ||
this.startAllJobs.bind(this) | ||
this.startAllJobs.bind(this), | ||
); | ||
@@ -51,0 +51,0 @@ } |
@@ -20,3 +20,3 @@ import { sum } from 'lodash'; | ||
private readonly schedulesRepository: SchedulesRepository, | ||
private readonly jobRepository: JobRepository | ||
private readonly jobRepository: JobRepository, | ||
) { | ||
@@ -67,3 +67,3 @@ super(); | ||
{ name, maxRunning, ...schedule }, | ||
jobResult.error | ||
jobResult.error, | ||
); | ||
@@ -87,3 +87,3 @@ return false; | ||
this.schedulesRepository, | ||
this.jobRepository | ||
this.jobRepository, | ||
); | ||
@@ -123,4 +123,4 @@ | ||
this.logger, | ||
'Single execution failed' | ||
) | ||
'Single execution failed', | ||
), | ||
); | ||
@@ -127,0 +127,0 @@ } |
@@ -6,5 +6,11 @@ import { SchedulesRepository } from '../repository/SchedulesRepository'; | ||
enum StartJobsStatus { | ||
notStarted, | ||
inProgress, | ||
finished, | ||
} | ||
export class SchedulePing { | ||
private handle?: NodeJS.Timeout; | ||
private startedJobs: boolean = false; | ||
private startJobsStatus: StartJobsStatus = StartJobsStatus.notStarted; | ||
@@ -17,3 +23,3 @@ constructor( | ||
private readonly interval: number, | ||
private readonly startAllJobs: () => Promise<void> | ||
private readonly startAllJobs: () => Promise<void>, | ||
) {} | ||
@@ -39,6 +45,10 @@ | ||
await this.schedulesRepository.ping(this.scheduleId); | ||
if (!this.startedJobs) { | ||
this.logger.debug(`This schedule just turned active`); | ||
if (this.startJobsStatus === StartJobsStatus.notStarted) { | ||
this.startJobsStatus = StartJobsStatus.inProgress; | ||
this.logger.debug('This schedule just turned active'); | ||
await this.startAllJobs(); | ||
this.startedJobs = true; | ||
this.startJobsStatus = StartJobsStatus.finished; | ||
this.logger.debug('Finished starting scheduled jobs'); | ||
} | ||
@@ -45,0 +55,0 @@ } |
@@ -39,3 +39,3 @@ import { max } from 'lodash'; | ||
logger, | ||
errorMessage | ||
errorMessage, | ||
); | ||
@@ -42,0 +42,0 @@ |
@@ -30,5 +30,5 @@ import { DateTime } from 'luxon'; | ||
export function toExecutableSchedule( | ||
schedule: ParsedIntervalSchedule | CronSchedule | ||
schedule: ParsedIntervalSchedule | CronSchedule, | ||
): ExecutableIntervalSchedule | ExecutableCronSchedule { | ||
return isCronSchedule(schedule) ? new ExecutableCronSchedule(schedule) : new ExecutableIntervalSchedule(schedule); | ||
} |
@@ -27,3 +27,3 @@ import { min } from 'lodash'; | ||
private readonly jobRepository: JobRepository, | ||
private readonly logger: Logger | ||
private readonly logger: Logger, | ||
) {} | ||
@@ -36,3 +36,3 @@ | ||
schedulesRepository: SchedulesRepository, | ||
jobRepository: JobRepository | ||
jobRepository: JobRepository, | ||
): JobScheduler { | ||
@@ -58,3 +58,3 @@ const executor = new JobExecutor(job.handler, schedulesRepository, jobRepository, logger); | ||
{ name: this.jobName }, | ||
momoError.jobNotFound | ||
momoError.jobNotFound, | ||
); | ||
@@ -83,3 +83,3 @@ return; | ||
{ name: this.jobName }, | ||
momoError.jobNotFound | ||
momoError.jobNotFound, | ||
); | ||
@@ -122,3 +122,3 @@ return; | ||
{ name: this.jobName }, | ||
momoError.jobNotFound | ||
momoError.jobNotFound, | ||
); | ||
@@ -147,3 +147,3 @@ return { | ||
{ name: this.jobName }, | ||
momoError.jobNotFound | ||
momoError.jobNotFound, | ||
); | ||
@@ -177,5 +177,5 @@ return; | ||
{ name: this.jobName }, | ||
error | ||
error, | ||
); | ||
} | ||
} |
@@ -8,3 +8,3 @@ import { Logger } from '../logging/Logger'; | ||
logger: Logger, | ||
errorMessage: string | ||
errorMessage: string, | ||
): NodeJS.Timeout { | ||
@@ -24,3 +24,3 @@ return setTimeout(async () => { | ||
logger: Logger, | ||
errorMessage: string | ||
errorMessage: string, | ||
): NodeJS.Timeout { | ||
@@ -27,0 +27,0 @@ return setInterval(async () => { |
@@ -13,3 +13,3 @@ import { setSafeInterval, setSafeTimeout } from './safeTimeouts'; | ||
logger: Logger, | ||
errorMessage: string | ||
errorMessage: string, | ||
): TimeoutHandle { | ||
@@ -31,5 +31,5 @@ const intervalWithDelay = new IntervalWithDelay(callback, interval, delay, logger, errorMessage); | ||
logger, | ||
errorMessage | ||
errorMessage, | ||
); | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
242426
3619
0
+ Added@types/luxon@3.3.8(transitive)
+ Added@types/whatwg-url@11.0.5(transitive)
+ Addedbson@6.7.0(transitive)
+ Addedcron@2.4.3(transitive)
+ Addedcron-parser@4.9.0(transitive)
+ Addedluxon@3.4.3(transitive)
+ Addedmongodb@6.7.0(transitive)
+ Addedmongodb-connection-string-url@3.0.1(transitive)
+ Addedtr46@4.1.1(transitive)
+ Addeduuid@9.0.1(transitive)
+ Addedwhatwg-url@13.0.0(transitive)
- Removed@types/node@20.14.2(transitive)
- Removed@types/whatwg-url@8.2.2(transitive)
- Removedbson@5.5.1(transitive)
- Removedcron@2.3.1(transitive)
- Removedcron-parser@4.8.1(transitive)
- Removedip-address@9.0.5(transitive)
- Removedjsbn@1.1.0(transitive)
- Removedmongodb@5.9.2(transitive)
- Removedmongodb-connection-string-url@2.6.0(transitive)
- Removedsmart-buffer@4.2.0(transitive)
- Removedsocks@2.8.3(transitive)
- Removedsprintf-js@1.1.3(transitive)
- Removedtr46@3.0.0(transitive)
- Removedundici-types@5.26.5(transitive)
- Removeduuid@9.0.0(transitive)
- Removedwhatwg-url@11.0.0(transitive)
Updatedcron@2.4.3
Updatedcron-parser@4.9.0
Updatedluxon@3.4.3
Updateduuid@9.0.1