gongxiangp2/转码恢复实现方案.md

7.2 KiB
Raw Blame History

转码任务恢复实现方案(已完成 ✅)

实现状态

✅ 已完成实现并部署到影子系统(2024-12-20)

实现的功能

  1. 自动恢复中断的转码任务

    • 懒加载方式:第一次访问转码API时自动触发
    • 查询所有 processing 状态的视频,清理残留文件并重置为 pending
    • 查询所有 pending 状态的视频,按时间顺序重新入队
  2. 清理残留文件

    • 清理S3上的预览视频(videoPreview540Url)
    • 清理S3上的预览片段(videoPreviewClipUrl)
    • 智能清理封面图:仅删除自动生成的封面(文件名包含 video-cover-)
  3. 转码队列管理

    • 支持并发转码(默认2个并发)
    • 自动重试机制(最多3次)
    • 进度回调和日志记录

实现文件

  • lib/video-transcode-queue.ts - 转码队列和恢复逻辑
  • app/api/materials/transcode/route.ts - 转码API(含懒加载恢复)
  • lib/video-processor.ts - 视频处理工具(FFmpeg转码)

您的方案分析

方案概述

  1. 部署后自动恢复:所有 processing 状态的视频重置为 pending
  2. 清理残留文件:删除这些视频的转码残留文件(S3 + 本地)
  3. 队列重建:按时间顺序查询所有 pending 状态的视频,重新入队

优点 ✅

  • 实现简单:不需要持久化队列,不需要 Redis
  • 自动恢复:部署后自动处理,无需手动干预
  • 清理彻底:避免残留文件占用存储空间
  • 保持顺序:按 createdAt 排序,保持原始队列顺序

需要注意的点 ⚠️

  1. 封面图判断

    • 问题:如何区分用户上传的封面和自动生成的封面?
    • 方案:如果 URL 包含 video-cover-,认为是自动生成的,可以删除
    • 否则认为是用户上传的,不删除
  2. 队列重建时机

    • 问题:什么时候重建队列?
    • 方案 A:应用启动时立即执行(可能影响启动速度)
    • 方案 B:懒加载,第一次访问转码 API 时执行(推荐)
    • 方案 C:创建独立的恢复 API,手动触发
  3. 性能考虑

    • 如果有大量 pending 任务,恢复时可能需要一些时间
    • 建议:清理文件可以异步执行,不阻塞恢复流程

实现细节

1. 残留文件识别

S3 上的文件(需要清理)

  • videoPreview540Url - 720P 预览视频(文件名通常包含 video-540p- 或 video-preview-)
  • videoPreviewClipUrl - 10 秒预览片段(文件名通常包含 video-clip-)
  • videoCoverUrl - 封面图(仅当文件名包含 video-cover- 时删除)

本地临时文件

  • 通常在 tmpdir() 目录下,文件名包含 materialId
  • 转码失败时可能残留,但通常会在 finally 中清理
  • 可选:扫描并清理超过一定时间的临时文件

2. 清理逻辑

// 清理策略
1. videoPreview540Url → 如果存在且是 S3 URL,删除
2. videoPreviewClipUrl → 如果存在且是 S3 URL,删除
3. videoCoverUrl → 如果存在且是 S3 URL 且文件名包含 "video-cover-",删除
   (避免误删用户上传的封面)

3. 队列重建

// 查询顺序
1. 查询所有 pending 状态的视频
2. 条件:
   - videoProcessingStatus = 'pending'
   - videoSourceUrl IS NOT NULL(必须有源视频)
   - deletedAt IS NULL(未删除)
3. 排序:按 createdAt ASC(最早上传的优先)
4. 重新入队:按顺序调用 transcodeQueue.add()

实现代码结构

文件结构

lib/video-transcode-queue.ts
  - 添加 recoverInterruptedTasks() 方法
  - 添加 cleanupTranscodeResidues() 辅助函数

app/api/materials/transcode/route.ts
  - 在第一次调用时触发恢复(懒加载)
  - 或创建独立的恢复 API

关键实现点

  1. 恢复函数:

    • 查询所有 processing 状态的视频
    • 清理残留文件
    • 重置状态为 pending
    • 查询所有 pending 状态的视频,按时间排序
    • 重新入队
  2. 清理函数:

    • 判断文件类型(预览视频、片段、封面)
    • 判断封面是否自动生成(文件名包含 video-cover-)
    • 批量删除 S3 文件
  3. 调用时机:

    • 懒加载:第一次访问转码 API 时检查并恢复
    • 或:应用启动时执行(需要异步初始化)

具体实现建议

方案 A:懒加载恢复(推荐)

优点:

  • 不影响应用启动速度
  • 只在需要时才执行恢复
  • 实现简单

实现:

  • 在转码 API 的 POST 方法中,第一次调用时检查是否需要恢复
  • 使用一个标志位 recoveryChecked 确保只执行一次

方案 B:启动时恢复

优点:

  • 部署后立即恢复,用户看到的状态更准确

缺点:

  • 可能影响启动速度(如果有大量任务)
  • 需要异步初始化机制

实现:

  • 在 lib/video-transcode-queue.ts 的初始化时调用
  • 或创建独立的初始化脚本

需要确认的问题

  1. 封面图判断:

    • 使用文件名判断(包含 video-cover- 就删除)?
    • 还是需要更精确的判断方式?
  2. 恢复时机:

    • 懒加载(第一次访问转码 API 时)?
    • 还是启动时立即执行?
  3. 本地临时文件:

    • 是否需要清理本地临时文件?
    • 还是只清理 S3 上的文件?
  4. 日志记录:

    • 是否需要记录哪些任务被恢复了?
    • 方便后续排查问题

我的建议

推荐实现方式:

  1. 恢复时机:懒加载(第一次访问转码 API 时)

    • 不影响启动速度
    • 实现简单
  2. 清理策略:

    • S3 文件:清理预览视频、片段、自动生成的封面
    • 本地临时文件:可选,通常已自动清理
  3. 封面判断:

    • 简单判断:URL 包含 video-cover- 就删除
    • 如果后续发现不够准确,再优化
  4. 日志记录:

    • 记录恢复的任务数量
    • 记录清理的文件数量
    • 方便排查问题

实现后的效果

部署前:

  • 队列:[1, 2, 3, 4, 5, 6...]
  • 正在转码:3(状态 processing)

部署后:

  • 自动检测到 3 的状态是 processing
  • 清理 3 的残留文件(S3 上的预览视频、片段等)
  • 重置 3 的状态为 pending
  • 查询所有 pending 状态的视频:[3, 4, 5, 6...](按时间排序)
  • 重新入队,3 会重新开始转码

用户体验:

  • 用户看到的状态会从"转码中"变为"待转码"
  • 然后自动开始重新转码
  • 不会"卡住"在转码中状态

总结

您的方案非常好,实现简单且实用。主要优势:

  1. ✅ 不需要持久化队列:利用数据库状态重建队列
  2. ✅ 自动恢复:部署后自动处理,无需手动干预
  3. ✅ 清理彻底:避免残留文件占用存储
  4. ✅ 保持顺序:按时间排序,保持原始队列顺序

建议的实现方式:

  • 懒加载恢复(第一次访问转码 API 时)
  • 简单判断封面图(文件名包含 video-cover-)
  • 只清理 S3 文件(本地临时文件通常已自动清理)

如果您同意这个方案,我可以开始实现。有什么需要调整的地方吗?