cnpmcore
Version:
Private NPM Registry for Enterprise
191 lines • 18.8 kB
JavaScript
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);
};
import { debuglog } from 'node:util';
import { AccessLevel, Inject, SingletonProto } from 'egg';
import { AbstractService } from "../../common/AbstractService.js";
import { TaskState, TaskType } from "../../common/enum/Task.js";
const debug = debuglog('cnpmcore/app/core/service/TaskService');
let TaskService = class TaskService extends 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.needMergeWhenWaiting()) {
// 在包同步场景,如果任务还未被触发,就不继续重复创建
// 如果任务正在执行,可能任务状态已更新,这种情况需要继续创建
if (existsTask.state === TaskState.Waiting) {
if (task.type === 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 = 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 !== 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(TaskState.Processing, 60_000 * 10);
for (const task of tasks) {
try {
// ignore ChangesStream task, it won't timeout
if (task.attempts >= 3 && task.type !== TaskType.ChangesStream) {
await this.finishTask(task, 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);
this.logger.error(e);
}
}
// try waiting timeout tasks in 30 mins
const waitingTasks = await this.taskRepository.findTimeoutTasks(TaskState.Waiting, 60_000 * 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);
this.logger.error(e);
}
}
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) {
debug('appendLogToNFS: %s\n%s', task.logPath, 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;
}
}
};
__decorate([
Inject(),
__metadata("design:type", Function)
], TaskService.prototype, "taskRepository", void 0);
__decorate([
Inject(),
__metadata("design:type", Function)
], TaskService.prototype, "nfsAdapter", void 0);
__decorate([
Inject(),
__metadata("design:type", Object)
], TaskService.prototype, "queueAdapter", void 0);
TaskService = __decorate([
SingletonProto({
accessLevel: AccessLevel.PUBLIC,
})
], TaskService);
export { TaskService };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVGFza1NlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9hcHAvY29yZS9zZXJ2aWNlL1Rhc2tTZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFFckMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBRTFELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUVsRSxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBS2hFLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyx1Q0FBdUMsQ0FBQyxDQUFDO0FBS3pELElBQU0sV0FBVyxHQUFqQixNQUFNLFdBQVksU0FBUSxlQUFlO0lBUXZDLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxRQUFrQjtRQUNoRCxPQUFPLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUVNLEtBQUssQ0FBQyxVQUFVLENBQUMsSUFBVSxFQUFFLG9CQUE2QjtRQUMvRCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFOUYscUNBQXFDO1FBQ3JDLElBQUksVUFBVSxJQUFJLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxFQUFFLENBQUM7WUFDOUMsNEJBQTRCO1lBQzVCLGdDQUFnQztZQUNoQyxJQUFJLFVBQVUsQ0FBQyxLQUFLLEtBQUssU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDO2dCQUMzQyxJQUFJLElBQUksQ0FBQyxJQUFJLEtBQUssUUFBUSxDQUFDLFdBQVcsRUFBRSxDQUFDO29CQUN2Qyx1Q0FBdUM7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