UNPKG

cnpmcore

Version:

Private NPM Registry for Enterprise

238 lines 20.9 kB
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 os from 'node:os'; import { setTimeout } from 'node:timers/promises'; import { AccessLevel, Inject, SingletonProto } from 'egg'; import { E500 } from 'egg/errors'; import { AbstractService } from "../../common/AbstractService.js"; import { AbstractChangeStream } from "../../common/adapter/changesStream/AbstractChangesStream.js"; import { GLOBAL_WORKER } from "../../common/constants.js"; import { TaskState, TaskType } from "../../common/enum/Task.js"; import { isTimeoutError } from "../../common/ErrorUtil.js"; import { getScopeAndName } from "../../common/PackageUtil.js"; import { HOST_NAME, Task } from "../entity/Task.js"; import { RegistryNotMatchError } from "./PackageSyncerService.js"; let ChangesStreamService = class ChangesStreamService extends AbstractService { // 出于向下兼容考虑, changes_stream 类型 Task 分为 // GLOBAL_WORKER: 默认的同步源 // `{registryName}_WORKER`: 自定义 scope 的同步源 async findExecuteTask() { const targetName = GLOBAL_WORKER; const globalRegistryTask = await this.taskRepository.findTaskByTargetName(targetName, TaskType.ChangesStream); // 如果没有配置默认同步源,先进行初始化 if (!globalRegistryTask) { await this.taskService.createTask(Task.createChangesStream(targetName), false); } // 自定义 scope 由 admin 手动创建 // 根据 TaskType.ChangesStream 从队列中获取 return (await this.taskService.findExecuteTask(TaskType.ChangesStream)); } async suspendSync(exit = false) { this.logger.info('[ChangesStreamService.suspendSync:start]'); if (this.config.cnpmcore.enableChangesStream) { // 防止继续获取新的任务 if (exit) { this.config.cnpmcore.enableChangesStream = false; } const authorIp = os.hostname(); // 暂停当前机器所有的 changesStream 任务 const tasks = await this.taskRepository.findTaskByAuthorIpAndType(authorIp, TaskType.ChangesStream); for (const task of tasks) { if (task.state === TaskState.Processing) { this.logger.info('[ChangesStreamService.suspendSync:suspend] taskId: %s', task.taskId); // 1. 更新任务状态为 waiting // 2. 重新推入任务队列供其他机器执行 await this.taskService.retryTask(task); } } } this.logger.info('[ChangesStreamService.suspendSync:finish]'); } async executeTask(task) { task.authorIp = os.hostname(); task.authorId = `pid_${process.pid}`; await this.taskRepository.saveTask(task); // 初始化 changeStream 任务 // since 默认从 1 开始 try { let since = task.data.since; if (!since) { since = await this.getInitialSince(task); } // allow disable changesStream dynamic while (since && this.config.cnpmcore.enableChangesStream) { const { lastSince, taskCount } = await this.executeSync(since, task); this.logger.info('[ChangesStreamService.executeTask:changes] since: %s => %s, %d new tasks, taskId: %s, updatedAt: %j', since, lastSince, taskCount, task.taskId, task.updatedAt); since = lastSince; if (taskCount === 0 && this.config.env === 'unittest') { break; } await setTimeout(this.config.cnpmcore.checkChangesStreamInterval); } } catch (err) { this.logger.warn('[ChangesStreamService.executeTask:error] %s, exit now', err.message); if (isTimeoutError(err)) { this.logger.warn(err); } else { this.logger.error(err); } task.error = `${err}`; await this.taskRepository.saveTask(task); await this.suspendSync(); } } // 优先从 registryId 获取,如果没有的话再返回默认的 registry async prepareRegistry(task) { const { registryId } = task.data || {}; // 如果已有 registryId, 查询 DB 直接获取 if (registryId) { const registry = await this.registryManagerService.findByRegistryId(registryId); if (!registry) { this.logger.error('[ChangesStreamService.getRegistry:error] registryId %s not found', registryId); throw new E500(`invalid change stream registry: ${registryId}`); } return registry; } const registry = await this.registryManagerService.ensureDefaultRegistry(); task.data = { ...task.data, registryId: registry.registryId, }; await this.taskRepository.saveTask(task); return registry; } // 根据 regsitry 判断是否需要添加同步任务 // 1. 如果该包已经指定了 registryId 则以 registryId 为准 // 1. 该包的 scope 在当前 registry 下 // 2. 如果 registry 下没有配置 scope (认为是通用 registry 地址) ,且该包的 scope 不在其他 registry 下 async needSync(registry, fullname) { const [scopeName, name] = getScopeAndName(fullname); const packageEntity = await this.packageRepository.findPackage(scopeName, name); // 如果包不存在,且处在 exist 模式下,则不同步 if (this.config.cnpmcore.syncMode === 'exist' && !packageEntity) { return false; } if (packageEntity?.registryId) { return registry.registryId === packageEntity.registryId; } const scope = await this.scopeManagerService.findByName(scopeName); const inCurrentRegistry = scope && scope?.registryId === registry.registryId; if (inCurrentRegistry) { return true; } const registryScopeCount = await this.scopeManagerService.countByRegistryId(registry.registryId); // 当前包没有 scope 信息,且当前 registry 下没有 scope,是通用 registry,需要同步 return !scope && !registryScopeCount; } async getInitialSince(task) { const registry = await this.prepareRegistry(task); const changesStreamAdapter = (await this.eggObjectFactory.getEggObject(AbstractChangeStream, registry.type)); const since = await changesStreamAdapter.getInitialSince(registry); return since; } // 从 changesStream 获取需要同步的数据 // 更新任务的 since 和 taskCount 相关字段 async executeSync(since, task) { const registry = await this.prepareRegistry(task); const changesStreamAdapter = (await this.eggObjectFactory.getEggObject(AbstractChangeStream, registry.type)); let taskCount = 0; let lastSince = since; // 获取需要同步的数据 // 需要根据 scope 和包信息进行过滤 const stream = changesStreamAdapter.fetchChanges(registry, since); let lastPackage; // 创建同步任务 for await (const change of stream) { const { fullname, seq } = change; lastPackage = fullname; lastSince = seq; const valid = await this.needSync(registry, fullname); if (valid) { taskCount++; const tips = `Sync cause by changes_stream(${registry.changeStream}) update seq: ${seq}`; try { const task = await this.packageSyncerService.createTask(fullname, { authorIp: HOST_NAME, authorId: 'ChangesStreamService', registryId: registry.registryId, skipDependencies: true, tips, }); this.logger.info('[ChangesStreamService.createTask:success] fullname: %s, task: %s, tips: %s', fullname, task.id, tips); } catch (err) { if (err instanceof RegistryNotMatchError) { this.logger.warn('[ChangesStreamService.executeSync:skip] fullname: %s, error: %s, tips: %s', fullname, err, tips); continue; } // only log error, make sure changes still reading this.logger.error('[ChangesStreamService.executeSync:error] fullname: %s, error: %s, tips: %s', fullname, err, tips); this.logger.error(err); continue; } } // 实时更新 task 信息 // 即使不需要同步,防止任务处理累积耗时超过 10min task.updateSyncData({ lastSince, lastPackage, taskCount, }); await this.taskRepository.saveTask(task); } // 如果 taskCount 为 0 更新一下任务信息 if (taskCount === 0) { task.updateSyncData({ lastSince, lastPackage, taskCount, }); await this.taskRepository.saveTask(task); } return { lastSince, taskCount }; } }; __decorate([ Inject(), __metadata("design:type", Function) ], ChangesStreamService.prototype, "taskRepository", void 0); __decorate([ Inject(), __metadata("design:type", Function) ], ChangesStreamService.prototype, "packageSyncerService", void 0); __decorate([ Inject(), __metadata("design:type", Function) ], ChangesStreamService.prototype, "taskService", void 0); __decorate([ Inject(), __metadata("design:type", Function) ], ChangesStreamService.prototype, "registryManagerService", void 0); __decorate([ Inject(), __metadata("design:type", Function) ], ChangesStreamService.prototype, "scopeManagerService", void 0); __decorate([ Inject(), __metadata("design:type", Object) ], ChangesStreamService.prototype, "eggObjectFactory", void 0); __decorate([ Inject(), __metadata("design:type", Function) ], ChangesStreamService.prototype, "packageRepository", void 0); ChangesStreamService = __decorate([ SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) ], ChangesStreamService); export { ChangesStreamService }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2hhbmdlc1N0cmVhbVNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9hcHAvY29yZS9zZXJ2aWNlL0NoYW5nZXNTdHJlYW1TZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBLE9BQU8sRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUN6QixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFFbEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUF5QixNQUFNLEtBQUssQ0FBQztBQUNqRixPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRWxDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNsRSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSw2REFBNkQsQ0FBQztBQUNuRyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDMUQsT0FBTyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDM0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBSTlELE9BQU8sRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUEwQixNQUFNLG1CQUFtQixDQUFDO0FBQzVFLE9BQU8sRUFBRSxxQkFBcUIsRUFBNkIsTUFBTSwyQkFBMkIsQ0FBQztBQVF0RixJQUFNLG9CQUFvQixHQUExQixNQUFNLG9CQUFxQixTQUFRLGVBQWU7SUFnQnZELHNDQUFzQztJQUN0Qyx3QkFBd0I7SUFDeEIsMENBQTBDO0lBQ25DLEtBQUssQ0FBQyxlQUFlO1FBQzFCLE1BQU0sVUFBVSxHQUFHLGFBQWEsQ0FBQztRQUNqQyxNQUFNLGtCQUFrQixHQUFHLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxvQkFBb0Is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