Commit 0a45add1f9823974c0b9dd7e83a08e8eca1e118a

Authored by yibo
1 parent 817951ea

raylight watchdog.sh: 修复卡死误判——采样跑完后的VAE存盘阶段也套用了10分钟阈值

watchdog.sh按step字段判断denoise是否卡死,但没考虑到step到达total_steps(采样跑完)后
还有VAE解码+存盘这段没有独立进度信号的尾巴,step会停在最终值不变。这段被并入denoise
阶段的10分钟宽限,实测至少一个1344×768长帧数案例这个阶段本身就超过608秒,被误判卡死、
自动整机重启,打断了h3-showcase批量重跑中5个正在跑/排队的任务。修复:给这个阶段单独
一档30分钟宽限(STALL_LIMIT_POSTPROC)。已部署重启watchdog,被打断的5个任务已重新提交
(新任务ID同步进h3-showcase-cases.md)。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CHANGELOG.md
1 1 # 变更记录
2 2  
  3 +## 2026-08-25(凌晨)— 修复 watchdog.sh 误杀:把"采样跑完/VAE存盘中"当成卡死重启,打断了一批正在跑的任务
  4 +
  5 +**背景**:批量重跑 16 个 h3-showcase 案例期间,队列跑到一半突然又出现"进程重启导致任务被取消/
  6 +执行状态丢失"——5 个任务(1 个 failed + 4 个 cancelled)一次性被打断,一开始怀疑又是人为操作
  7 +(跟8月24日PM2那次很像),查了才发现这次是机器自己的`watchdog.sh`干的。
  8 +
  9 +**根因**:`watchdog.sh`(2026-08-23新增,按`step`字段判断任务卡死,不看总耗时)只区分了
  10 +"FSDP初始化中(step=0)"和"denoise采样中(step>0)"两个阶段,没考虑到`step`到达`total_steps`
  11 +(采样跑完)之后还有VAE视频/音频解码+存盘这段没有独立进度信号的尾巴——`step`会一直停在最终值
  12 +不变。这段被并入了denoise阶段的10分钟宽限,实测至少一个1344×768长帧数案例这个阶段本身就超过
  13 +608秒,被误判"卡死"、自动`bash start.sh`整机重启,把当时排队中的一批任务连带打断。
  14 +
  15 +**修复**:`watchdog.sh`给"`step>=total_steps`"这个采样完/存盘中阶段单独加一档30分钟宽限
  16 +(`STALL_LIMIT_POSTPROC=1800`),不再跟denoise阶段共用10分钟的紧阈值。已部署并重启watchdog
  17 +进程(新PID替换旧的),被打断的5个任务已用存量`req_json`重新提交。详见
  18 +`raylight/README.md`已知限制第10条。
  19 +
  20 +**教训记录**:这台机器上至少有两套独立的自动重启机制(这个watchdog + 8月24日排查过的PM2托管
  21 +服务),以后见到"进程重启导致任务被取消"这类错误,先查`watchdog.log`有没有"卡死判定触发",
  22 +比先怀疑人为操作更快定位。
  23 +
3 24 ## 2026-08-24(晚,续3)— 修复 raylight `/health` 骗人报健康的盲区,ComfyUI 崩了近20分钟没人发现
4 25  
5 26 **背景**:续2记录的那次 ref2va OOM(`b659b60c6cbc`,FSDP初始化阶段GPU2/GPU3 OOM)实际上
... ...
docs/todo/h3-showcase-cases.md
... ... @@ -53,11 +53,11 @@
53 53  
54 54 | # | 标题 | 模式 | 参考素材 | 状态 | 任务ID | 备注 |
55 55 |---|---|---|---|---|---|---|
56   -| 1 | 望远镜寻 MINIMAX 装置 | ref2va | 图1-4连续关键帧,已上传 | 已提交 | a59cae01ec39 | |
  56 +| 1 | 望远镜寻 MINIMAX 装置 | ref2va | 图1-4连续关键帧,已上传 | 已完成 | a59cae01ec39 | |
57 57 | 2 | 史诗级太空歌剧院线短预告 | 纯文生(未确认) | 无/待确认 | 已完成 | 6639b07acc7b | |
58   -| 3 | 科幻悬疑电影短预告 | ref2va | 图1氛围/图2人物,已上传 | 已提交 | 97846542ab09 | |
59   -| 4 | 高级时尚品牌视频(荒漠公路) | ref2va | 图1场景/图2人物/图3包袋/图4logo,已上传 | 已提交 | c1237b599bf4 | |
60   -| 5 | 火场胶片时尚短片 | ref2va | 图1质感/图2人物,已上传 | 已提交 | 67ababbea78f | |
  58 +| 3 | 科幻悬疑电影短预告 | ref2va | 图1氛围/图2人物,已上传 | 已完成 | 97846542ab09 | |
  59 +| 4 | 高级时尚品牌视频(荒漠公路) | ref2va | 图1场景/图2人物/图3包袋/图4logo,已上传 | 已完成 | c1237b599bf4 | |
  60 +| 5 | 火场胶片时尚短片 | ref2va | 图1质感/图2人物,已上传 | 已完成 | 67ababbea78f | |
61 61  
62 62 <details><summary>提示词原文</summary>
63 63  
... ... @@ -85,7 +85,7 @@
85 85  
86 86 | # | 标题 | 模式 | 参考素材 | 状态 | 任务ID | 备注 |
87 87 |---|---|---|---|---|---|---|
88   -| 1 | 悬疑犯罪电影片头 | ref2va | 多张风格参考图(7张),已上传 | 已提交 | 81711242178d | |
  88 +| 1 | 悬疑犯罪电影片头 | ref2va | 多张风格参考图(7张),已上传 | 已完成 | 81711242178d | |
89 89 | 2 | 傍晚厨房手绘发光动画 | 纯文生 | 无 | 已完成 | c0e23beb4132 | |
90 90 | 3 | 深夜自助洗衣店手绘发光动画 | 纯文生 | 无 | 已完成 | 0fe0f2101c44 | |
91 91 | 4 | 手绘2.0,海边小集市指物变手绘 | 纯文生 | 无 | 已完成 | eaf2a1433c41 | |
... ... @@ -113,7 +113,7 @@
113 113  
114 114 | # | 标题 | 模式 | 参考素材 | 状态 | 任务ID | 备注 |
115 115 |---|---|---|---|---|---|---|
116   -| 1 | MiniMax H3 品牌开场 MG,波形 Logo | ref2va | 图1开场/图2波形logo,已上传 | 已提交 | 7ded7db676d3 | |
  116 +| 1 | MiniMax H3 品牌开场 MG,波形 Logo | ref2va | 图1开场/图2波形logo,已上传 | 已完成 | 7ded7db676d3 | |
117 117 | 2 | MiniMax H3 品牌 MG「One becomes everyone」 | 纯文生 | 无 | 已完成 | 194e9bd77da6 | |
118 118 | 3 | 全中文文字动效实验影像 | 纯文生 | 无 | 已完成 | 125751cac1c8 | |
119 119 | 4 | Touch Designer 动效追踪 | ref2va(视频编辑) | @视频1基础素材 | 不支持 | | raylight 后端 GenReq 无"以视频为基础编辑"字段,无法提交 |
... ... @@ -167,7 +167,7 @@
167 167 |---|---|---|---|---|---|---|
168 168 | 1 | 古装武侠悬疑短剧,竹林 | 纯文生(未确认) | 无/待确认 | 已完成 | 57169e6751d0 | |
169 169 | 2 | 室内家庭争吵短剧(竖屏) | 纯文生 | 无 | 已完成 | 99e771bbb50d | 含具体人物「秦皓瑄」台词/语气要求 |
170   -| 3 | 海外吸血鬼爱情短剧预告(竖屏) | ref2va | 图1男女主/图2场景,已上传 | 已提交 | 71a4d093aebc | 720×1280竖屏提交 |
  170 +| 3 | 海外吸血鬼爱情短剧预告(竖屏) | ref2va | 图1男女主/图2场景,已上传 | 已完成 | 71a4d093aebc | 720×1280竖屏提交 |
171 171  
172 172 <details><summary>提示词原文</summary>
173 173  
... ... @@ -189,8 +189,8 @@
189 189  
190 190 | # | 标题 | 模式 | 参考素材 | 状态 | 任务ID | 备注 |
191 191 |---|---|---|---|---|---|---|
192   -| 1 | 高级时尚眼镜广告,白棚双模特 | ref2va | 图1模特/图2外貌/图3眼镜,已上传 | 已提交 | b0d258352c3c | 720×1280竖屏提交 |
193   -| 2 | Herman Miller 人体工学椅 360 展示 | ref2va | 图1产品详情/图2产品图,已上传(仅1张) | 已提交 | fe749aa533a0 | 文档里图1图2视觉上是同一素材,只上传了1张 |
  192 +| 1 | 高级时尚眼镜广告,白棚双模特 | ref2va | 图1模特/图2外貌/图3眼镜,已上传 | 已完成 | b0d258352c3c | 720×1280竖屏提交 |
  193 +| 2 | Herman Miller 人体工学椅 360 展示 | ref2va | 图1产品详情/图2产品图,已上传(仅1张) | 已完成 | fe749aa533a0 | 文档里图1图2视觉上是同一素材,只上传了1张 |
194 194 | 3 | 北京烤鸭美食产品宣传 | 纯文生 | 无 | 已完成 | 4067993ff824 | |
195 195  
196 196 <details><summary>提示词原文</summary>
... ... @@ -213,9 +213,9 @@
213 213  
214 214 | # | 标题 | 模式 | 参考素材 | 状态 | 任务ID | 备注 |
215 215 |---|---|---|---|---|---|---|
216   -| 1 | 赛博朋克角色装备切换+UI+场景加载 | ref2va | 图1角色/图2 UI风格,已上传 | 已提交 | 2e18bf09b46a | 精确到秒的分镜时间轴,15s |
217   -| 2 | 产品官网落地页 UI/UX 滚动演示 | ref2va | 图1产品图,已上传 | 已提交 | 234a2037e064 | |
218   -| 3 | 网站 UI 标题滑动+汽车灯变色 | 按纯文生提交 | 无(提示词未引用@图片编号) | 已提交 | 88e3501a3fb9 | |
  216 +| 1 | 赛博朋克角色装备切换+UI+场景加载 | ref2va | 图1角色/图2 UI风格,已上传 | 已完成 | 2e18bf09b46a | 精确到秒的分镜时间轴,15s |
  217 +| 2 | 产品官网落地页 UI/UX 滚动演示 | ref2va | 图1产品图,已上传 | 已完成 | 234a2037e064 | |
  218 +| 3 | 网站 UI 标题滑动+汽车灯变色 | 按纯文生提交 | 无(提示词未引用@图片编号) | 已提交 | db125dc6a831 | |
219 219 | 4 | 2D 手绘比格犬反应游戏界面 | 纯文生 | 无 | 已完成 | e1fd99e4ab97 | 原案例10s/4:3画幅,本次统一用1344×768/12.5s提交,用于观察分辨率适配效果 |
220 220  
221 221 <details><summary>提示词原文</summary>
... ... @@ -240,7 +240,7 @@
240 240  
241 241 | # | 标题 | 模式 | 参考素材 | 状态 | 任务ID | 备注 |
242 242 |---|---|---|---|---|---|---|
243   -| 1 | 机械臂抓取红色方块 | 纯文生 | 无 | 已提交 | 0a5ca897d076 | |
  243 +| 1 | 机械臂抓取红色方块 | 纯文生 | 无 | 已提交 | 282281777d1b | |
244 244 | 2 | 精准替换视频背景为办公室场景 | 视频编辑 | 基于案例1输出 | 不支持 | | raylight 后端无视频编辑输入方式,且依赖案例1先跑完 |
245 245  
246 246 <details><summary>提示词原文</summary>
... ... @@ -260,9 +260,9 @@
260 260  
261 261 | # | 标题 | 模式 | 参考素材 | 状态 | 任务ID | 备注 |
262 262 |---|---|---|---|---|---|---|
263   -| 1 | 黏土动画狐狸跃崖 | 纯文生 | 无 | 已提交 | 760588dc9f82 | |
264   -| 2 | 国风仙侠 3D 角色 PV | ref2va | 图1分镜/图2人物,已上传 | 已提交 | 28244b583d2d | |
265   -| 3 | 乙游男主角色 PV | ref2va | 图2角色身份,已上传(合并图) | 已提交 | 329ed81249cd | 截图时未拆分图1/图2,用了含所需人物信息的合并图 |
  263 +| 1 | 黏土动画狐狸跃崖 | 纯文生 | 无 | 已提交 | 1a398591663e | |
  264 +| 2 | 国风仙侠 3D 角色 PV | ref2va | 图1分镜/图2人物,已上传 | 已提交 | 423a20afba5a | |
  265 +| 3 | 乙游男主角色 PV | ref2va | 图2角色身份,已上传(合并图) | 已提交 | d040eec16a88 | 截图时未拆分图1/图2,用了含所需人物信息的合并图 |
266 266  
267 267 <details><summary>提示词原文</summary>
268 268  
... ...
raylight/README.md
... ... @@ -139,6 +139,19 @@ ssh admin@192.168.110.67 &#39;cd /data/models/minimax-h3 &amp;&amp; export H3_OFFLOAD=disk &amp;
139 139 探测一次8188,探测失败就把`ready`降级成0、`model`字段报`"comfyui_unreachable"`,不再
140 140 单纯相信内存里那个只写一次的标志位。**这条不能顶替第8条**——探测准了只是能更快发现
141 141 崩溃,8188死了以后还是要手动`bash start.sh`重启,自愈本身仍然没有。
  142 +10. **【2026-08-25修复】`watchdog.sh`的卡死判定漏了"采样跑完、VAE解码存盘中"这个阶段,
  143 + 把正常任务当卡死误杀重启**——`watchdog.sh`(2026-08-23新增,按`step`字段判断任务是否
  144 + 卡死,见该脚本文件头注释)只区分"FSDP初始化中(step=0,20分钟宽限)"和"denoise采样中
  145 + (step>0,10分钟宽限)"两个阶段,没考虑到`step`到达`total_steps`后(采样已经跑完)还有
  146 + VAE视频/音频解码+存盘这段没有独立进度信号的尾巴,`step`会一直停在最终值不变。这段被
  147 + 并入了10分钟宽限那档,实测至少一个1344×768长帧数案例这个阶段本身就超过608秒——
  148 + watchdog判定"卡死"、自动执行`bash start.sh`整机重启,把当时排队中的一整批任务
  149 + (h3-showcase-cases批量测试,5个)连带打断,跟"服务被别人手动重启"表现完全一样,
  150 + 一开始容易误判成人为操作。**已修**:给`step>=total_steps`这个"采样完/存盘中"阶段单独
  151 + 一档`STALL_LIMIT_POSTPROC=1800`(30分钟宽限),不再跟denoise阶段共用10分钟的紧阈值。
  152 + **排查经验**:见到"进程重启导致任务被取消"这类错误,先查`watchdog.log`有没有
  153 + "卡死判定触发"字样,比先怀疑人为操作或`start.sh`被谁手动跑了更快定位——这台机器上
  154 + 至少有两套独立的自动重启机制(这个watchdog + 8月24日排查过的PM2),别再默认是人。
142 155  
143 156 ## 多参考图生视频支持参考视频/独立音频参考(2026-08-24 新增)
144 157  
... ...
raylight/watchdog.sh 0 → 100644
  1 +#!/usr/bin/env bash
  2 +# 基于进度的卡死检测,取代之前"固定600秒墙钟时间"的误杀判定(2026-08-23)。
  3 +# 判定标准:denoise 的 step 数字长时间不变才算卡死,不看总耗时——大分辨率/长帧数任务本来
  4 +# 就该跑更久,不该被时间线误杀。
  5 +#
  6 +# 【2026-08-25修复】漏了一个阶段:采样跑完(step达到total_steps)之后还有VAE视频/音频解码+
  7 +# 存盘,这段没有独立进度信号,step会一直停在最终值不变——之前这段直接被并入
  8 +# STALL_LIMIT_RUNNING(10分钟)判定,实测至少一个1344×768长帧数案例这个阶段本身就超过608秒,
  9 +# 被误判"卡死"重启,把一批正常在跑/排队的任务(showcase案例批次)集体打断。单独给这个阶段
  10 +# 一个更宽的阈值,跟INIT阶段同样的思路——不同阶段没有可比的"正常耗时",不能共用一个数字。
  11 +set -uo pipefail
  12 +BASE="http://127.0.0.1:8100"
  13 +STATE_FILE="/data/models/raylight/watchdog.state"
  14 +LOG_FILE="/data/models/raylight/watchdog.log"
  15 +STALL_LIMIT_RUNNING=600 # denoise 采样中(0<step<total_steps),step 10分钟不变才算卡死
  16 +STALL_LIMIT_INIT=1200 # 还在FSDP初始化(step仍是0)阶段,给20分钟宽限
  17 +STALL_LIMIT_POSTPROC=1800 # 采样已跑完(step>=total_steps),VAE解码/存盘阶段,给30分钟宽限
  18 +POLL_INTERVAL=20
  19 +
  20 +log() { echo "$(date '+%F %T') $*" >> "$LOG_FILE"; }
  21 +
  22 +# 状态文件格式:job_id step last_change_epoch
  23 +read_state() { cat "$STATE_FILE" 2>/dev/null || echo "none 0 0"; }
  24 +write_state() { echo "$1 $2 $3" > "$STATE_FILE"; }
  25 +
  26 +log "watchdog started"
  27 +
  28 +while true; do
  29 + sleep "$POLL_INTERVAL"
  30 +
  31 + HEALTH=$(curl -s --max-time 10 "$BASE/health" 2>/dev/null)
  32 + RUNNING=$(echo "$HEALTH" | python3 -c "import sys,json; print(json.load(sys.stdin).get('running',0))" 2>/dev/null)
  33 + if [ "$RUNNING" != "1" ]; then
  34 + write_state none 0 0
  35 + continue
  36 + fi
  37 +
  38 + JOBS=$(curl -s --max-time 10 "$BASE/v1/videos?limit=5" 2>/dev/null)
  39 + CUR=$(echo "$JOBS" | python3 -c "
  40 +import sys, json
  41 +try:
  42 + jobs = json.load(sys.stdin)
  43 + for j in jobs:
  44 + if j.get('status') == 'running':
  45 + print(j['id']); break
  46 +except Exception:
  47 + pass
  48 +" 2>/dev/null)
  49 + [ -z "$CUR" ] && continue
  50 +
  51 + DETAIL=$(curl -s --max-time 10 "$BASE/v1/videos/$CUR" 2>/dev/null)
  52 + STEP=$(echo "$DETAIL" | python3 -c "import sys,json; print(json.load(sys.stdin).get('step',0))" 2>/dev/null)
  53 + [ -z "$STEP" ] && STEP=0
  54 + TOTAL_STEPS=$(echo "$DETAIL" | python3 -c "import sys,json; print(json.load(sys.stdin).get('total_steps',0))" 2>/dev/null)
  55 + [ -z "$TOTAL_STEPS" ] && TOTAL_STEPS=0
  56 +
  57 + read -r PREV_ID PREV_STEP PREV_TS < <(read_state)
  58 + NOW=$(date +%s)
  59 +
  60 + if [ "$CUR" != "$PREV_ID" ] || [ "$STEP" != "$PREV_STEP" ]; then
  61 + write_state "$CUR" "$STEP" "$NOW"
  62 + log "job=$CUR step=$STEP (progress, reset stall timer)"
  63 + continue
  64 + fi
  65 +
  66 + STALLED_FOR=$((NOW - PREV_TS))
  67 + if [ "$STEP" = "0" ]; then
  68 + LIMIT=$STALL_LIMIT_INIT
  69 + elif [ "$TOTAL_STEPS" != "0" ] && [ "$STEP" -ge "$TOTAL_STEPS" ] 2>/dev/null; then
  70 + LIMIT=$STALL_LIMIT_POSTPROC
  71 + else
  72 + LIMIT=$STALL_LIMIT_RUNNING
  73 + fi
  74 +
  75 + if [ "$STALLED_FOR" -ge "$LIMIT" ]; then
  76 + log "job=$CUR step=$STEP 卡死判定触发:$STALLED_FOR 秒无进展(阈值 $LIMIT),执行重启"
  77 + bash /data/models/raylight/start.sh >> "$LOG_FILE" 2>&1
  78 + write_state none 0 0
  79 + log "重启完成"
  80 + fi
  81 +done
... ...