mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
implementing task scheduling at backend
This commit is contained in:
parent
fd3a59ef3d
commit
d04a17bf69
@ -25,4 +25,13 @@ export const winstonSettings = {
|
|||||||
exitOnError: false
|
exitOnError: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Logger = new (<any>winston).Logger(winstonSettings);
|
type logFN = (...args: (string | number)[]) => {};
|
||||||
|
|
||||||
|
export const Logger: {
|
||||||
|
error: logFN,
|
||||||
|
warn: logFN,
|
||||||
|
info: logFN,
|
||||||
|
verbose: logFN,
|
||||||
|
debug: logFN,
|
||||||
|
silly: logFN
|
||||||
|
} = new (<any>winston).Logger(winstonSettings);
|
||||||
|
@ -457,6 +457,7 @@ export class AdminMWs {
|
|||||||
original.save();
|
original.save();
|
||||||
|
|
||||||
await ConfigDiagnostics.runDiagnostics();
|
await ConfigDiagnostics.runDiagnostics();
|
||||||
|
ObjectManagers.getInstance().TaskManager.runSchedules();
|
||||||
Logger.info(LOG_TAG, 'new config:');
|
Logger.info(LOG_TAG, 'new config:');
|
||||||
Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t'));
|
Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t'));
|
||||||
return next();
|
return next();
|
||||||
|
@ -105,10 +105,19 @@ export class ObjectManagers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async reset() {
|
public static async reset() {
|
||||||
|
if (ObjectManagers.getInstance().TaskManager) {
|
||||||
|
ObjectManagers.getInstance().TaskManager.stopSchedules();
|
||||||
|
}
|
||||||
await SQLConnection.close();
|
await SQLConnection.close();
|
||||||
this._instance = null;
|
this._instance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async InitCommonManagers() {
|
||||||
|
const TaskManager = require('./tasks/TaskManager').TaskManager;
|
||||||
|
ObjectManagers.getInstance().TaskManager = new TaskManager();
|
||||||
|
}
|
||||||
|
|
||||||
public static async InitMemoryManagers() {
|
public static async InitMemoryManagers() {
|
||||||
await ObjectManagers.reset();
|
await ObjectManagers.reset();
|
||||||
const GalleryManager = require('./memory/GalleryManager').GalleryManager;
|
const GalleryManager = require('./memory/GalleryManager').GalleryManager;
|
||||||
@ -119,7 +128,6 @@ export class ObjectManagers {
|
|||||||
const IndexingManager = require('./memory/IndexingManager').IndexingManager;
|
const IndexingManager = require('./memory/IndexingManager').IndexingManager;
|
||||||
const PersonManager = require('./memory/PersonManager').PersonManager;
|
const PersonManager = require('./memory/PersonManager').PersonManager;
|
||||||
const VersionManager = require('./memory/VersionManager').VersionManager;
|
const VersionManager = require('./memory/VersionManager').VersionManager;
|
||||||
const TaskManager = require('./tasks/TaskManager').TaskManager;
|
|
||||||
ObjectManagers.getInstance().GalleryManager = new GalleryManager();
|
ObjectManagers.getInstance().GalleryManager = new GalleryManager();
|
||||||
ObjectManagers.getInstance().UserManager = new UserManager();
|
ObjectManagers.getInstance().UserManager = new UserManager();
|
||||||
ObjectManagers.getInstance().SearchManager = new SearchManager();
|
ObjectManagers.getInstance().SearchManager = new SearchManager();
|
||||||
@ -128,7 +136,7 @@ export class ObjectManagers {
|
|||||||
ObjectManagers.getInstance().IndexingManager = new IndexingManager();
|
ObjectManagers.getInstance().IndexingManager = new IndexingManager();
|
||||||
ObjectManagers.getInstance().PersonManager = new PersonManager();
|
ObjectManagers.getInstance().PersonManager = new PersonManager();
|
||||||
ObjectManagers.getInstance().VersionManager = new VersionManager();
|
ObjectManagers.getInstance().VersionManager = new VersionManager();
|
||||||
ObjectManagers.getInstance().TaskManager = new TaskManager();
|
this.InitCommonManagers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async InitSQLManagers() {
|
public static async InitSQLManagers() {
|
||||||
@ -142,7 +150,6 @@ export class ObjectManagers {
|
|||||||
const IndexingManager = require('./sql/IndexingManager').IndexingManager;
|
const IndexingManager = require('./sql/IndexingManager').IndexingManager;
|
||||||
const PersonManager = require('./sql/PersonManager').PersonManager;
|
const PersonManager = require('./sql/PersonManager').PersonManager;
|
||||||
const VersionManager = require('./sql/VersionManager').VersionManager;
|
const VersionManager = require('./sql/VersionManager').VersionManager;
|
||||||
const TaskManager = require('./tasks/TaskManager').TaskManager;
|
|
||||||
ObjectManagers.getInstance().GalleryManager = new GalleryManager();
|
ObjectManagers.getInstance().GalleryManager = new GalleryManager();
|
||||||
ObjectManagers.getInstance().UserManager = new UserManager();
|
ObjectManagers.getInstance().UserManager = new UserManager();
|
||||||
ObjectManagers.getInstance().SearchManager = new SearchManager();
|
ObjectManagers.getInstance().SearchManager = new SearchManager();
|
||||||
@ -151,7 +158,7 @@ export class ObjectManagers {
|
|||||||
ObjectManagers.getInstance().IndexingManager = new IndexingManager();
|
ObjectManagers.getInstance().IndexingManager = new IndexingManager();
|
||||||
ObjectManagers.getInstance().PersonManager = new PersonManager();
|
ObjectManagers.getInstance().PersonManager = new PersonManager();
|
||||||
ObjectManagers.getInstance().VersionManager = new VersionManager();
|
ObjectManagers.getInstance().VersionManager = new VersionManager();
|
||||||
ObjectManagers.getInstance().TaskManager = new TaskManager();
|
this.InitCommonManagers();
|
||||||
Logger.debug('SQL DB inited');
|
Logger.debug('SQL DB inited');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import {TaskProgressDTO} from '../../../common/entities/settings/TaskProgressDTO';
|
import {TaskProgressDTO} from '../../../common/entities/settings/TaskProgressDTO';
|
||||||
import {TaskScheduleDTO} from '../../../common/entities/task/TaskScheduleDTO';
|
|
||||||
import {TaskDTO} from '../../../common/entities/task/TaskDTO';
|
import {TaskDTO} from '../../../common/entities/task/TaskDTO';
|
||||||
|
|
||||||
export interface ITaskManager {
|
export interface ITaskManager {
|
||||||
@ -12,4 +11,8 @@ export interface ITaskManager {
|
|||||||
|
|
||||||
|
|
||||||
getAvailableTasks(): TaskDTO[];
|
getAvailableTasks(): TaskDTO[];
|
||||||
|
|
||||||
|
stopSchedules(): void;
|
||||||
|
|
||||||
|
runSchedules(): void;
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,18 @@ import {TaskProgressDTO} from '../../../common/entities/settings/TaskProgressDTO
|
|||||||
import {ITask} from './ITask';
|
import {ITask} from './ITask';
|
||||||
import {TaskRepository} from './TaskRepository';
|
import {TaskRepository} from './TaskRepository';
|
||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../common/config/private/Config';
|
||||||
import {TaskTriggerType} from '../../../common/entities/task/TaskScheduleDTO';
|
import {TaskScheduleDTO, TaskTriggerType} from '../../../common/entities/task/TaskScheduleDTO';
|
||||||
|
import {Logger} from '../../Logger';
|
||||||
|
|
||||||
|
const LOG_TAG = '[TaskManager]';
|
||||||
|
|
||||||
export class TaskManager implements ITaskManager {
|
export class TaskManager implements ITaskManager {
|
||||||
|
|
||||||
|
protected timers: NodeJS.Timeout[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.runSchedules();
|
||||||
|
}
|
||||||
|
|
||||||
getProgresses(): { [id: string]: TaskProgressDTO } {
|
getProgresses(): { [id: string]: TaskProgressDTO } {
|
||||||
const m: { [id: string]: TaskProgressDTO } = {};
|
const m: { [id: string]: TaskProgressDTO } = {};
|
||||||
@ -14,10 +22,12 @@ export class TaskManager implements ITaskManager {
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
start(taskName: string, config: any): void {
|
start<T>(taskName: string, config: T): void {
|
||||||
const t = this.findTask(taskName);
|
const t = this.findTask(taskName);
|
||||||
if (t) {
|
if (t) {
|
||||||
t.start(config);
|
t.start(config);
|
||||||
|
} else {
|
||||||
|
Logger.warn(LOG_TAG, 'cannot find task to start:' + taskName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +35,8 @@ export class TaskManager implements ITaskManager {
|
|||||||
const t = this.findTask(taskName);
|
const t = this.findTask(taskName);
|
||||||
if (t) {
|
if (t) {
|
||||||
t.stop();
|
t.stop();
|
||||||
|
} else {
|
||||||
|
Logger.warn(LOG_TAG, 'cannot find task to stop:' + taskName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,38 +44,79 @@ export class TaskManager implements ITaskManager {
|
|||||||
return TaskRepository.Instance.getAvailableTasks();
|
return TaskRepository.Instance.getAvailableTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public stopSchedules(): void {
|
||||||
|
this.timers.forEach(timer => clearTimeout(timer));
|
||||||
|
this.timers = [];
|
||||||
|
}
|
||||||
|
|
||||||
public runSchedules(): void {
|
public runSchedules(): void {
|
||||||
|
this.stopSchedules();
|
||||||
|
Logger.info(LOG_TAG, 'Running task schedules');
|
||||||
Config.Server.tasks.scheduled.forEach(schedule => {
|
Config.Server.tasks.scheduled.forEach(schedule => {
|
||||||
let nextRun = null;
|
const nextDate = this.getDateFromSchedule(new Date(), schedule);
|
||||||
switch (schedule.trigger.type) {
|
if (nextDate && nextDate.getTime() > Date.now()) {
|
||||||
case TaskTriggerType.scheduled:
|
Logger.debug(LOG_TAG, 'running schedule: [' + schedule.id + '] ' + schedule.name +
|
||||||
nextRun = Date.now() - schedule.trigger.time;
|
' at ' + nextDate.toLocaleString(undefined, {hour12: false}));
|
||||||
break;
|
|
||||||
/*case TaskTriggerType.periodic:
|
|
||||||
|
|
||||||
//TODo finish it
|
const timer: NodeJS.Timeout = setTimeout(() => {
|
||||||
const getNextDayOfTheWeek = (dayOfWeek: number) => {
|
|
||||||
const refDate = new Date();
|
|
||||||
refDate.setHours(0, 0, 0, 0);
|
|
||||||
refDate.setDate(refDate.getDate() + (dayOfWeek + 7 - refDate.getDay()) % 7);
|
|
||||||
return refDate;
|
|
||||||
};
|
|
||||||
|
|
||||||
nextRun = Date.now() - schedule.trigger.periodicity;
|
|
||||||
break;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextRun != null) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.start(schedule.taskName, schedule.config);
|
this.start(schedule.taskName, schedule.config);
|
||||||
}, nextRun);
|
this.timers = this.timers.filter(t => t !== timer);
|
||||||
|
}, nextDate.getTime() - Date.now());
|
||||||
|
this.timers.push(timer);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Logger.debug(LOG_TAG, 'skipping schedule: [' + schedule.id + '] ' + schedule.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected findTask(taskName: string): ITask<any> {
|
protected getDateFromSchedule(refDate: Date, schedule: TaskScheduleDTO): Date {
|
||||||
return this.getAvailableTasks().find(t => t.Name === taskName);
|
switch (schedule.trigger.type) {
|
||||||
|
case TaskTriggerType.scheduled:
|
||||||
|
return new Date(schedule.trigger.time);
|
||||||
|
|
||||||
|
case TaskTriggerType.periodic:
|
||||||
|
const nextValidHM = (date: Date, h: number, m: number, dayDiff: number): Date => {
|
||||||
|
|
||||||
|
if (date.getHours() < h || (date.getHours() === h && date.getMinutes() <= m)) {
|
||||||
|
date.setHours(h);
|
||||||
|
date.setMinutes(m);
|
||||||
|
} else {
|
||||||
|
date.setTime(date.getTime() + dayDiff);
|
||||||
|
date.setHours(h);
|
||||||
|
date.setMinutes(m);
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNextDayOfTheWeek = (dayOfWeek: number, h: number, m: number): Date => {
|
||||||
|
const date = new Date(refDate);
|
||||||
|
date.setDate(refDate.getDate() + (dayOfWeek + 1 + 7 - refDate.getDay()) % 7);
|
||||||
|
if (date.getDay() === refDate.getDay()) {
|
||||||
|
return new Date(refDate);
|
||||||
|
}
|
||||||
|
date.setHours(0, 0, 0, 0);
|
||||||
|
return date;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const hour = Math.floor(schedule.trigger.atTime / 1000 / (60 * 60));
|
||||||
|
const minute = (schedule.trigger.atTime / 1000 / 60) % 60;
|
||||||
|
|
||||||
|
if (schedule.trigger.periodicity <= 6) { // Between Monday and Sunday
|
||||||
|
const nextRunDate = getNextDayOfTheWeek(schedule.trigger.periodicity, hour, minute);
|
||||||
|
return nextValidHM(nextRunDate, hour, minute, 7 * 24 * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// every day
|
||||||
|
return nextValidHM(new Date(refDate), hour, minute, 24 * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected findTask<T = any>(taskName: string): ITask<T> {
|
||||||
|
return this.getAvailableTasks().find(t => t.Name === taskName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ export class Server {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (!(process.env.NODE_ENV === 'production')) {
|
if (!(process.env.NODE_ENV === 'production')) {
|
||||||
Logger.debug(LOG_TAG, 'Running in DEBUG mode, set env variable NODE_ENV=production to disable ');
|
Logger.info(LOG_TAG, 'Running in DEBUG mode, set env variable NODE_ENV=production to disable ');
|
||||||
}
|
}
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
@ -43,10 +43,10 @@ export class Server {
|
|||||||
async init() {
|
async init() {
|
||||||
Logger.info(LOG_TAG, 'running diagnostics...');
|
Logger.info(LOG_TAG, 'running diagnostics...');
|
||||||
await ConfigDiagnostics.runDiagnostics();
|
await ConfigDiagnostics.runDiagnostics();
|
||||||
Logger.info(LOG_TAG, 'using config:');
|
Logger.verbose(LOG_TAG, 'using config:');
|
||||||
const appVer = require('../package.json').version;
|
const appVer = require('../package.json').version;
|
||||||
Config.Client.appVersion = appVer;
|
Config.Client.appVersion = appVer;
|
||||||
Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t'));
|
Logger.verbose(LOG_TAG, JSON.stringify(Config, null, '\t'));
|
||||||
|
|
||||||
this.app = _express();
|
this.app = _express();
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ export enum DatabaseType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum LogLevel {
|
export enum LogLevel {
|
||||||
error = 1, warn = 2, info = 3, debug = 4, verbose = 5, silly = 6
|
error = 1, warn = 2, info = 3, verbose = 4, debug = 5, silly = 6
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SQLLogLevel {
|
export enum SQLLogLevel {
|
||||||
|
@ -17,7 +17,7 @@ export interface ScheduledTaskTrigger extends TaskTrigger {
|
|||||||
|
|
||||||
export interface PeriodicTaskTrigger extends TaskTrigger {
|
export interface PeriodicTaskTrigger extends TaskTrigger {
|
||||||
type: TaskTriggerType.periodic;
|
type: TaskTriggerType.periodic;
|
||||||
periodicity: number; // 1-7: week days 8+ every x days
|
periodicity: number; // 0-6: week days 7 every day
|
||||||
atTime: number; // day time
|
atTime: number; // day time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
77
test/backend/unit/model/tasks/TaskManager.spec.ts
Normal file
77
test/backend/unit/model/tasks/TaskManager.spec.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import {expect} from 'chai';
|
||||||
|
import {TaskManager} from '../../../../../backend/model/tasks/TaskManager';
|
||||||
|
import {TaskScheduleDTO, TaskTriggerType} from '../../../../../common/entities/task/TaskScheduleDTO';
|
||||||
|
|
||||||
|
class TaskManagerSpec extends TaskManager {
|
||||||
|
|
||||||
|
public getDateFromSchedule(refDate: Date, schedule: TaskScheduleDTO): Date {
|
||||||
|
return super.getDateFromSchedule(refDate, schedule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('TaskManager', () => {
|
||||||
|
|
||||||
|
it('should get date from schedule', async () => {
|
||||||
|
const tm = new TaskManagerSpec();
|
||||||
|
|
||||||
|
const refDate = new Date(2019, 7, 18, 5, 10); // its a sunday
|
||||||
|
|
||||||
|
|
||||||
|
expect(tm.getDateFromSchedule(refDate, <any>{
|
||||||
|
trigger: {
|
||||||
|
type: TaskTriggerType.scheduled,
|
||||||
|
time: (new Date(2019, 7, 18, 5, 10)).getTime()
|
||||||
|
}
|
||||||
|
})).to.be.deep.equal((new Date(2019, 7, 18, 5, 10)));
|
||||||
|
|
||||||
|
|
||||||
|
for (let dayOfWeek = 0; dayOfWeek < 7; ++dayOfWeek) {
|
||||||
|
let nextDay = dayOfWeek < 6 ? (18 + dayOfWeek + 1) : 18;
|
||||||
|
|
||||||
|
let h = 10;
|
||||||
|
let m = 5;
|
||||||
|
expect(tm.getDateFromSchedule(refDate, <any>{
|
||||||
|
trigger: {
|
||||||
|
type: TaskTriggerType.periodic,
|
||||||
|
atTime: (h * 60 + m) * 60 * 1000,
|
||||||
|
periodicity: dayOfWeek
|
||||||
|
}
|
||||||
|
})).to.be.deep.equal((new Date(2019, 7, nextDay, h, m)), 'for day: ' + dayOfWeek);
|
||||||
|
|
||||||
|
h = 2;
|
||||||
|
m = 5;
|
||||||
|
nextDay = 18 + dayOfWeek + 1;
|
||||||
|
expect(tm.getDateFromSchedule(refDate, <any>{
|
||||||
|
trigger: {
|
||||||
|
type: TaskTriggerType.periodic,
|
||||||
|
atTime: (h * 60 + m) * 60 * 1000,
|
||||||
|
periodicity: dayOfWeek
|
||||||
|
}
|
||||||
|
})).to.be.deep.equal((new Date(2019, 7, nextDay, h, m)), 'for day: ' + dayOfWeek);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const h = 10;
|
||||||
|
const m = 5;
|
||||||
|
expect(tm.getDateFromSchedule(refDate, <any>{
|
||||||
|
trigger: {
|
||||||
|
type: TaskTriggerType.periodic,
|
||||||
|
atTime: (h * 60 + m) * 60 * 1000,
|
||||||
|
periodicity: 7
|
||||||
|
}
|
||||||
|
})).to.be.deep.equal((new Date(2019, 7, 18, h, m)));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const h = 2;
|
||||||
|
const m = 5;
|
||||||
|
expect(tm.getDateFromSchedule(refDate, <any>{
|
||||||
|
trigger: {
|
||||||
|
type: TaskTriggerType.periodic,
|
||||||
|
atTime: (h * 60 + m) * 60 * 1000,
|
||||||
|
periodicity: 7
|
||||||
|
}
|
||||||
|
})).to.be.deep.equal((new Date(2019, 7, 19, h, m)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user