fix(snapshot): onModuleInit 增加扫描临时目录清理孤儿文件

SQLite 可能因重建丢失任务记录,导致 onModuleInit 仅靠查数据库
无法清理遗留的临时目录。新增逻辑:启动时扫描临时目录所有子目录,
若数据库中不存在对应任务则直接删除。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-24 01:45:24 -08:00
parent 669a8a7248
commit 7b7bfcac93
2 changed files with 19 additions and 0 deletions

View File

@ -36,12 +36,23 @@ export class SnapshotOrchestratorService implements OnModuleInit {
} }
async onModuleInit(): Promise<void> { async onModuleInit(): Promise<void> {
// 1. 标记数据库中遗留的 RUNNING 任务为 FAILED
const stale = await this.repo.findByStatus(SnapshotStatus.RUNNING); const stale = await this.repo.findByStatus(SnapshotStatus.RUNNING);
for (const task of stale) { for (const task of stale) {
await this.repo.updateTaskStatus(task.id, SnapshotStatus.FAILED, '服务重启,任务中断'); await this.repo.updateTaskStatus(task.id, SnapshotStatus.FAILED, '服务重启,任务中断');
this.localStorage.deleteTask(task.id); this.localStorage.deleteTask(task.id);
this.logger.warn(`遗留任务已标记失败: ${task.id}`); this.logger.warn(`遗留任务已标记失败: ${task.id}`);
} }
// 2. 扫描临时目录,清理数据库中不存在的孤儿目录
const dirIds = this.localStorage.listTaskDirs();
for (const dirId of dirIds) {
const task = await this.repo.findById(dirId);
if (!task) {
this.localStorage.deleteTask(dirId);
this.logger.warn(`清理孤儿临时目录: ${dirId}`);
}
}
} }
getAvailableTargets(): BackupTarget[] { getAvailableTargets(): BackupTarget[] {

View File

@ -36,6 +36,14 @@ export class LocalStorageAdapter {
} }
} }
/** 列出临时目录中所有任务子目录的 ID */
listTaskDirs(): string[] {
if (!fs.existsSync(this.tempDir)) return [];
return fs.readdirSync(this.tempDir, { withFileTypes: true })
.filter((e) => e.isDirectory())
.map((e) => e.name);
}
cleanupExpired(retentionHours: number): string[] { cleanupExpired(retentionHours: number): string[] {
const threshold = Date.now() - retentionHours * 60 * 60 * 1000; const threshold = Date.now() - retentionHours * 60 * 60 * 1000;
const deletedIds: string[] = []; const deletedIds: string[] = [];