UNPKG

cnpmcore

Version:
192 lines 18.3 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TaskService = void 0; const tegg_1 = require("@eggjs/tegg"); const NFSAdapter_1 = require("../../common/adapter/NFSAdapter"); const Task_1 = require("../../common/enum/Task"); const AbstractService_1 = require("../../common/AbstractService"); const TaskRepository_1 = require("../../repository/TaskRepository"); const Task_2 = require("../entity/Task"); let TaskService = class TaskService extends AbstractService_1.AbstractService { async getTaskQueueLength(taskType) { return await this.queueAdapter.length(taskType); } async createTask(task, addTaskQueueOnExists) { const existsTask = await this.taskRepository.findTaskByTargetName(task.targetName, task.type); // 只在包同步场景下做任务合并,其余场景通过 bizId 来进行任务幂等 if (existsTask && Task_2.Task.needMergeWhenWaiting(task.type)) { // 在包同步场景,如果任务还未被触发,就不继续重复创建 // 如果任务正在执行,可能任务状态已更新,这种情况需要继续创建 if (existsTask.state === Task_1.TaskState.Waiting) { if (task.type === Task_1.TaskType.SyncPackage) { // 如果是specificVersions的任务则可能可以和存量任务进行合并 const specificVersions = task.data?.specificVersions; const existsTaskSpecificVersions = existsTask.data?.specificVersions; if (existsTaskSpecificVersions) { if (specificVersions) { // 存量的任务和新增任务都是同步指定版本的任务,合并两者版本至存量任务 await this.taskRepository.updateSpecificVersionsOfWaitingTask(existsTask, specificVersions); } else { // 新增任务是全量同步任务,移除存量任务中的指定版本使其成为全量同步任务 await this.taskRepository.updateSpecificVersionsOfWaitingTask(existsTask); } } // 存量任务是全量同步任务,直接提高任务优先级 } // 提高任务的优先级 if (addTaskQueueOnExists) { const queueLength = await this.getTaskQueueLength(task.type); if (queueLength < this.config.cnpmcore.taskQueueHighWaterSize) { // make sure waiting task in queue await this.queueAdapter.push(task.type, existsTask.taskId); this.logger.info('[TaskService.createTask:exists-to-queue] taskType: %s, targetName: %s, taskId: %s, queue size: %s', task.type, task.targetName, task.taskId, queueLength); } } } return existsTask; } await this.taskRepository.saveTask(task); await this.queueAdapter.push(task.type, task.taskId); const queueLength = await this.getTaskQueueLength(task.type); this.logger.info('[TaskService.createTask:new] taskType: %s, targetName: %s, taskId: %s, queue size: %s', task.type, task.targetName, task.taskId, queueLength); return task; } async retryTask(task, appendLog) { if (appendLog) { await this.appendLogToNFS(task, appendLog); } task.state = Task_1.TaskState.Waiting; await this.taskRepository.saveTask(task); await this.queueAdapter.push(task.type, task.taskId); const queueLength = await this.getTaskQueueLength(task.type); this.logger.info('[TaskService.retryTask:save] taskType: %s, targetName: %s, taskId: %s, queue size: %s', task.type, task.targetName, task.taskId, queueLength); } async findTask(taskId) { return await this.taskRepository.findTask(taskId); } async findTasks(taskIdList) { return await this.taskRepository.findTasks(taskIdList); } async findTaskLog(task) { return await this.nfsAdapter.getDownloadUrlOrStream(task.logPath); } async findExecuteTask(taskType) { let taskId = await this.queueAdapter.pop(taskType); let task; while (taskId) { task = await this.taskRepository.findTask(taskId); // 任务已删除或任务已执行 // 继续取下一个任务 if (task === null || task?.state !== Task_1.TaskState.Waiting) { taskId = await this.queueAdapter.pop(taskType); continue; } const condition = task.start(); const saveSucceed = await this.taskRepository.idempotentSaveTask(task, condition); if (!saveSucceed) { taskId = await this.queueAdapter.pop(taskType); continue; } return task; } return null; } async retryExecuteTimeoutTasks() { // try processing timeout tasks in 10 mins const tasks = await this.taskRepository.findTimeoutTasks(Task_1.TaskState.Processing, 60000 * 10); for (const task of tasks) { try { // ignore ChangesStream task, it won't timeout if (task.attempts >= 3 && task.type !== Task_1.TaskType.ChangesStream) { await this.finishTask(task, Task_1.TaskState.Timeout); this.logger.warn('[TaskService.retryExecuteTimeoutTasks:timeout] taskType: %s, targetName: %s, taskId: %s, attempts %s set to fail', task.type, task.targetName, task.taskId, task.attempts); continue; } if (task.attempts >= 1) { // reset logPath task.resetLogPath(); } await this.retryTask(task); this.logger.info('[TaskService.retryExecuteTimeoutTasks:retry] taskType: %s, targetName: %s, taskId: %s, attempts %s will retry again', task.type, task.targetName, task.taskId, task.attempts); } catch (e) { this.logger.error('[TaskService.retryExecuteTimeoutTasks:error] processing task, taskType: %s, targetName: %s, taskId: %s, attempts %s will retry again', task.type, task.targetName, task.taskId, task.attempts); } } // try waiting timeout tasks in 30 mins const waitingTasks = await this.taskRepository.findTimeoutTasks(Task_1.TaskState.Waiting, 60000 * 30); for (const task of waitingTasks) { try { await this.retryTask(task); this.logger.warn('[TaskService.retryExecuteTimeoutTasks:retryWaiting] taskType: %s, targetName: %s, taskId: %s waiting too long', task.type, task.targetName, task.taskId); } catch (e) { this.logger.error('[TaskService.retryExecuteTimeoutTasks:error] waiting task, taskType: %s, targetName: %s, taskId: %s, attempts %s will retry again', task.type, task.targetName, task.taskId, task.attempts); } } return { processing: tasks.length, waiting: waitingTasks.length, }; } async appendTaskLog(task, appendLog) { await this.appendLogToNFS(task, appendLog); await this.taskRepository.saveTask(task); } async finishTask(task, taskState, appendLog) { if (appendLog) { await this.appendLogToNFS(task, appendLog); } task.state = taskState; await this.taskRepository.saveTaskToHistory(task); } async appendLogToNFS(task, appendLog) { try { const nextPosition = await this.nfsAdapter.appendBytes(task.logPath, Buffer.from(appendLog + '\n'), task.logStorePosition, { 'Content-Type': 'text/plain; charset=utf-8', }); if (nextPosition) { task.logStorePosition = nextPosition; } } catch (err) { // [PositionNotEqualToLengthError]: Position is not equal to file length, status: 409 // [ObjectNotAppendableError]: The object is not appendable if (err.code === 'PositionNotEqualToLength' || err.code === 'ObjectNotAppendable') { // override exists log file await this.nfsAdapter.uploadBytes(task.logPath, Buffer.from(appendLog + '\n')); return; } throw err; } } }; exports.TaskService = TaskService; __decorate([ (0, tegg_1.Inject)(), __metadata("design:type", TaskRepository_1.TaskRepository) ], TaskService.prototype, "taskRepository", void 0); __decorate([ (0, tegg_1.Inject)(), __metadata("design:type", NFSAdapter_1.NFSAdapter) ], TaskService.prototype, "nfsAdapter", void 0); __decorate([ (0, tegg_1.Inject)(), __metadata("design:type", Object) ], TaskService.prototype, "queueAdapter", void 0); exports.TaskService = TaskService = __decorate([ (0, tegg_1.SingletonProto)({ accessLevel: tegg_1.AccessLevel.PUBLIC, }) ], TaskService); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVGFza1NlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9hcHAvY29yZS9zZXJ2aWNlL1Rhc2tTZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztBQUFBLHNDQUlxQjtBQUNyQixnRUFBNkQ7QUFDN0QsaURBQTZEO0FBQzdELGtFQUErRDtBQUMvRCxvRUFBaUU7QUFDakUseUNBQWlFO0FBTTFELElBQU0sV0FBVyxHQUFqQixNQUFNLFdBQVksU0FBUSxpQ0FBZTtJQVF2QyxLQUFLLENBQUMsa0JBQWtCLENBQUMsUUFBa0I7UUFDaEQsT0FBTyxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFFTSxLQUFLLENBQUMsVUFBVSxDQUFDLElBQVUsRUFBRSxvQkFBNkI7UUFDL0QsTUFBTSxVQUFVLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLG9CQUFvQixDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRTlGLHFDQUFxQztRQUNyQyxJQUFJLFVBQVUsSUFBSSxXQUFJLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQ3RELDRCQUE0QjtZQUM1QixnQ0FBZ0M7WUFDaEMsSUFBSSxVQUFVLENBQUMsS0FBSyxLQUFLLGdCQUFTLENBQUMsT0FBTyxFQUFFO2dCQUMxQyxJQUFJLElBQUksQ0FBQyxJQUFJLEtBQUssZUFBUSxDQUFDLFdBQVcsRUFBRTtvQkFDdEMsdUNBQXVDO29CQUN2QyxNQUFNLGdCQUFnQixHQUFJLElBQXdDLENBQUMsSUFBSSxFQUFFLGdCQUFnQixDQUFDO29CQUMxRixNQUFNLDBCQUEwQixHQUFJLFVBQThDLENBQUMsSUFBSSxFQUFFLGdCQUFnQixDQUFDO29CQUMxRyxJQUFJLDBCQUEwQixFQUFFO3dCQUM5QixJQUFJLGdCQUFnQixF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