backend v4 half
This commit is contained in:
1 parent
aba7a506ad
commit
725516ad6c
4183 files changed
+217684
-75056
No files matched your search
+42
@@ -0,0 +1,42 @@
|
||||
import { Execution } from "../tasks/scheduled-task";
|
||||
import { TimeMatcher } from "../time/time-matcher";
|
||||
type OnFn = (date: Date) => void | Promise<void>;
|
||||
type OnErrorHookFn = (date: Date, error: Error, execution: Execution) => void | Promise<void>;
|
||||
type OnHookFn = (date: Date, execution: Execution) => boolean | Promise<boolean>;
|
||||
type OnMatch = (date: Date, execution: Execution) => any | Promise<any>;
|
||||
export type RunnerOptions = {
|
||||
noOverlap?: boolean;
|
||||
timezone?: string;
|
||||
maxExecutions?: number;
|
||||
maxRandomDelay?: number;
|
||||
onMissedExecution?: OnFn;
|
||||
onOverlap?: OnFn;
|
||||
onError?: OnErrorHookFn;
|
||||
onFinished?: OnHookFn;
|
||||
beforeRun?: OnHookFn;
|
||||
onMaxExecutions?: OnFn;
|
||||
};
|
||||
export declare class Runner {
|
||||
timeMatcher: TimeMatcher;
|
||||
onMatch: OnMatch;
|
||||
noOverlap: boolean;
|
||||
maxExecutions?: number;
|
||||
maxRandomDelay: number;
|
||||
runCount: number;
|
||||
running: boolean;
|
||||
heartBeatTimeout?: NodeJS.Timeout;
|
||||
onMissedExecution: OnFn;
|
||||
onOverlap: OnFn;
|
||||
onError: OnErrorHookFn;
|
||||
beforeRun: OnHookFn;
|
||||
onFinished: OnHookFn;
|
||||
onMaxExecutions: OnFn;
|
||||
constructor(timeMatcher: TimeMatcher, onMatch: OnMatch, options?: RunnerOptions);
|
||||
start(): void;
|
||||
nextRun(): Date;
|
||||
stop(): void;
|
||||
isStarted(): boolean;
|
||||
isStopped(): boolean;
|
||||
execute(): Promise<void>;
|
||||
}
|
||||
export {};
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Runner = void 0;
|
||||
const create_id_1 = require("../create-id");
|
||||
const logger_1 = __importDefault(require("../logger"));
|
||||
const tracked_promise_1 = require("../promise/tracked-promise");
|
||||
function emptyOnFn() { }
|
||||
;
|
||||
function emptyHookFn() { return true; }
|
||||
;
|
||||
function defaultOnError(date, error) {
|
||||
logger_1.default.error('Task failed with error!', error);
|
||||
}
|
||||
class Runner {
|
||||
timeMatcher;
|
||||
onMatch;
|
||||
noOverlap;
|
||||
maxExecutions;
|
||||
maxRandomDelay;
|
||||
runCount;
|
||||
running;
|
||||
heartBeatTimeout;
|
||||
onMissedExecution;
|
||||
onOverlap;
|
||||
onError;
|
||||
beforeRun;
|
||||
onFinished;
|
||||
onMaxExecutions;
|
||||
constructor(timeMatcher, onMatch, options) {
|
||||
this.timeMatcher = timeMatcher;
|
||||
this.onMatch = onMatch;
|
||||
this.noOverlap = options == undefined || options.noOverlap === undefined ? false : options.noOverlap;
|
||||
this.maxExecutions = options?.maxExecutions;
|
||||
this.maxRandomDelay = options?.maxRandomDelay || 0;
|
||||
this.onMissedExecution = options?.onMissedExecution || emptyOnFn;
|
||||
this.onOverlap = options?.onOverlap || emptyOnFn;
|
||||
this.onError = options?.onError || defaultOnError;
|
||||
this.onFinished = options?.onFinished || emptyHookFn;
|
||||
this.beforeRun = options?.beforeRun || emptyHookFn;
|
||||
this.onMaxExecutions = options?.onMaxExecutions || emptyOnFn;
|
||||
this.runCount = 0;
|
||||
this.running = false;
|
||||
}
|
||||
start() {
|
||||
this.running = true;
|
||||
let lastExecution;
|
||||
let expectedNextExecution;
|
||||
const scheduleNextHeartBeat = (currentDate) => {
|
||||
if (this.running) {
|
||||
clearTimeout(this.heartBeatTimeout);
|
||||
this.heartBeatTimeout = setTimeout(heartBeat, getDelay(this.timeMatcher, currentDate));
|
||||
}
|
||||
};
|
||||
const runTask = (date) => {
|
||||
return new Promise(async (resolve) => {
|
||||
const execution = {
|
||||
id: (0, create_id_1.createID)('exec'),
|
||||
reason: 'scheduled'
|
||||
};
|
||||
const shouldExecute = await this.beforeRun(date, execution);
|
||||
const randomDelay = Math.floor(Math.random() * this.maxRandomDelay);
|
||||
if (shouldExecute) {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
this.runCount++;
|
||||
execution.startedAt = new Date();
|
||||
const result = await this.onMatch(date, execution);
|
||||
execution.finishedAt = new Date();
|
||||
execution.result = result;
|
||||
this.onFinished(date, execution);
|
||||
if (this.maxExecutions && this.runCount >= this.maxExecutions) {
|
||||
this.onMaxExecutions(date);
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
execution.finishedAt = new Date();
|
||||
execution.error = error;
|
||||
this.onError(date, error, execution);
|
||||
}
|
||||
resolve(true);
|
||||
}, randomDelay);
|
||||
}
|
||||
});
|
||||
};
|
||||
const checkAndRun = (date) => {
|
||||
return new tracked_promise_1.TrackedPromise(async (resolve, reject) => {
|
||||
try {
|
||||
if (this.timeMatcher.match(date)) {
|
||||
await runTask(date);
|
||||
}
|
||||
resolve(true);
|
||||
}
|
||||
catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
const heartBeat = async () => {
|
||||
const currentDate = nowWithoutMs();
|
||||
if (expectedNextExecution && expectedNextExecution.getTime() < currentDate.getTime()) {
|
||||
while (expectedNextExecution.getTime() < currentDate.getTime()) {
|
||||
logger_1.default.warn(`missed execution at ${expectedNextExecution}! Possible blocking IO or high CPU user at the same process used by node-cron.`);
|
||||
expectedNextExecution = this.timeMatcher.getNextMatch(expectedNextExecution);
|
||||
runAsync(this.onMissedExecution, expectedNextExecution, defaultOnError);
|
||||
}
|
||||
}
|
||||
if (lastExecution && lastExecution.getState() === 'pending') {
|
||||
runAsync(this.onOverlap, currentDate, defaultOnError);
|
||||
if (this.noOverlap) {
|
||||
logger_1.default.warn('task still running, new execution blocked by overlap prevention!');
|
||||
expectedNextExecution = this.timeMatcher.getNextMatch(currentDate);
|
||||
scheduleNextHeartBeat(currentDate);
|
||||
return;
|
||||
}
|
||||
}
|
||||
lastExecution = checkAndRun(currentDate);
|
||||
expectedNextExecution = this.timeMatcher.getNextMatch(currentDate);
|
||||
scheduleNextHeartBeat(currentDate);
|
||||
};
|
||||
this.heartBeatTimeout = setTimeout(() => {
|
||||
heartBeat();
|
||||
}, getDelay(this.timeMatcher, nowWithoutMs()));
|
||||
}
|
||||
nextRun() {
|
||||
return this.timeMatcher.getNextMatch(new Date());
|
||||
}
|
||||
stop() {
|
||||
this.running = false;
|
||||
if (this.heartBeatTimeout) {
|
||||
clearTimeout(this.heartBeatTimeout);
|
||||
this.heartBeatTimeout = undefined;
|
||||
}
|
||||
}
|
||||
isStarted() {
|
||||
return !!this.heartBeatTimeout && this.running;
|
||||
}
|
||||
isStopped() {
|
||||
return !this.isStarted();
|
||||
}
|
||||
async execute() {
|
||||
const date = new Date();
|
||||
const execution = {
|
||||
id: (0, create_id_1.createID)('exec'),
|
||||
reason: 'invoked'
|
||||
};
|
||||
try {
|
||||
const shouldExecute = await this.beforeRun(date, execution);
|
||||
if (shouldExecute) {
|
||||
this.runCount++;
|
||||
execution.startedAt = new Date();
|
||||
const result = await this.onMatch(date, execution);
|
||||
execution.finishedAt = new Date();
|
||||
execution.result = result;
|
||||
this.onFinished(date, execution);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
execution.finishedAt = new Date();
|
||||
execution.error = error;
|
||||
this.onError(date, error, execution);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.Runner = Runner;
|
||||
async function runAsync(fn, date, onError) {
|
||||
try {
|
||||
await fn(date);
|
||||
}
|
||||
catch (error) {
|
||||
onError(date, error);
|
||||
}
|
||||
}
|
||||
function getDelay(timeMatcher, currentDate) {
|
||||
const maxDelay = 86400000;
|
||||
const nextRun = timeMatcher.getNextMatch(currentDate);
|
||||
const now = new Date();
|
||||
const delay = nextRun.getTime() - now.getTime();
|
||||
if (delay > maxDelay) {
|
||||
return maxDelay;
|
||||
}
|
||||
return Math.max(0, delay);
|
||||
}
|
||||
function nowWithoutMs() {
|
||||
const date = new Date();
|
||||
date.setMilliseconds(0);
|
||||
return date;
|
||||
}
|
||||
//# sourceMappingURL=runner.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/scheduler/runner.ts"],"names":[],"mappings":"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 truncated
|
||||
Reference in new issue
Block a user