فهرست منبع

fix: tryAutoMerge 加章节级内存锁,杜绝重复合并风暴(磁盘撑满/CPU打满根因)

现象: 服务器 10+ 个 ffmpeg 持续运行停不下来,同一章节(150/154/.../170)
被重复合并多次,撑满 CPU 和磁盘临时文件(曾导致 /data 盘满→MySQL 挂→服务502).

根因: AudioScanner 每30s对每个叶节点调一次 tryAutoMerge. 合并是异步(ffmpeg
数十秒),期间父章节 audioUrl 未写入, step5 幂等检查失效, 下一轮扫描又触发,
同一章节被并发重复合并, ffmpeg 进程越积越多.

修复: 加 mergingChapters Set 内存锁, 章节正在合并中则跳过, 合并完成(finally)
释放. 保证同一章节同时只有一个合并任务.
MyFramework User 2 ماه پیش
والد
کامیت
8fcb517893
1فایلهای تغییر یافته به همراه21 افزوده شده و 6 حذف شده
  1. 21 6
      server/src/modules/book-generator/book-generator.store.ts

+ 21 - 6
server/src/modules/book-generator/book-generator.store.ts

@@ -153,6 +153,12 @@ function computeContentHash(content: string): string {
  * 自动检测:当某个叶节点音频完成后,检查其所属1级章节下所有叶节点
  * 是否都已就绪,若是则自动触发音频合并到该章节。
  */
+// 正在合并中的章节集合(内存锁)。
+// 防止 AudioScanner 每 30s 对每个叶节点重复调用 tryAutoMerge 时,
+// 同一章节在上一次合并(异步 ffmpeg 数十秒)未完成前被并发重复触发,
+// 造成 ffmpeg 进程堆积、CPU 打满、临时文件撑爆磁盘。
+const mergingChapters = new Set<number>();
+
 export async function tryAutoMerge(
   completedLeafId: number,
   bookId: number | null,
@@ -266,16 +272,25 @@ export async function tryAutoMerge(
       }
     }
 
-    // 6. 触发合并
+    // 6. 触发合并(加内存锁,防止同一章节并发重复合并)
+    if (mergingChapters.has(chapterId)) {
+      // 该章节已有合并任务在进行中,跳过本次触发
+      return;
+    }
+    mergingChapters.add(chapterId);
     console.log(
       `[AutoMerge] 🎵 章节${chapterId}下所有${leafNodesUnderChapter.length}个叶节点音频已就绪,开始自动合并...`,
     );
 
-    const mergedUrl = await mergeChapterAudios(chapterId);
-    if (mergedUrl) {
-      console.log(`[AutoMerge] ✅ 章节${chapterId}音频自动合并完成: ${mergedUrl}`);
-    } else {
-      console.warn(`[AutoMerge] ⚠️ 章节${chapterId}合并返回空结果`);
+    try {
+      const mergedUrl = await mergeChapterAudios(chapterId);
+      if (mergedUrl) {
+        console.log(`[AutoMerge] ✅ 章节${chapterId}音频自动合并完成: ${mergedUrl}`);
+      } else {
+        console.warn(`[AutoMerge] ⚠️ 章节${chapterId}合并返回空结果`);
+      }
+    } finally {
+      mergingChapters.delete(chapterId);
     }
   } catch (err) {
     console.error(`[AutoMerge] 自动合并检测失败:`, err);