fix(video): PR #492 - 合并统一轮询并保留本地出站安全策略

This commit is contained in:
ddCat
2026-09-14 19:45:56 +08:00
17 changed files with 1219 additions and 227 deletions

View File

@@ -26,26 +26,18 @@ description: Channel 2 视频任务创建、轮询与上游任务同步延迟处
宿主只在同时满足以下条件时把该响应视为“任务状态尚未同步”:
1. 渠道接口类型精确为 `newapi-channel-2`;
2. 已经保存非空 Provider Task ID;
3. HTTP 状态为 `400`;
4. JSON 的 `code` 或 `message` 精确等于 `task_not_exist`。
1. 已经保存非空 Provider Task ID;
2. HTTP 状态为 `400` 或 `404`;
3. JSON 的 `code` 或 `message` 精确等于 `task_not_exist`、`task_not_found` 或对应的空格拼写。
不符合条件的 HTTP 错误、非 JSON 正文或其他错误码仍立即按失败语义处理。
该判断属于宿主统一的视频轮询语义,不再按渠道名分支。不符合条件的 400/401/403/422 等参数、鉴权或权限错误仍立即按失败语义处理。
## 轮询与延期
新任务首次查询前等待 `2` 秒;同步错误在同一次执行中按 `2` 秒、`3` 秒、`5` 秒、`5` 秒退避,最多短期查询 5 次。短期窗口耗尽后,Worker 不发送新的创建请求,也不切换渠道或退款,而是:
新任务和恢复任务都在首次查询前等待 30 秒,后续统一每 30 秒查询。连续三次 `task_not_exist` 或 404 才结束;中间任何一次正常的 pending/running 响应都会清零连续计数。HTTP 408/409/425/429/5xx、临时网络错误、断流、渠道槽位和短时熔断继续查询原 Provider Task ID,不发送第二次创建请求。
- 保持本地任务状态为 `running`;
- 保留原 `provider_request_id` 和计费状态;
- 释放当前租约并把 `next_poll_at` 延后 15 秒;
- 下一轮从已保存的 Provider Task ID 恢复,只发送查询请求。
`task_not_exist` 的跨轮次同步等待从本地任务首次开始执行起最多持续 5 分钟。超过该上限仍未得到有效任务状态时,Worker 将任务置为失败并释放活跃任务与计费预留,避免无效 Provider Task ID 永久占用用户任务名额。该限制只针对持续的 `task_not_exist`;查询接口已经返回正常 pending/running 状态后的长视频生成仍按视频任务超时和延期策略恢复。
因此,上游生成耗时 500 至 600 秒时,只要查询接口返回有效的处理中状态,本地会持续以低频查询等待完成,不会把正常排队误报为“模型服务拒绝了请求”。视频真正返回失败状态时,仍按供应商失败处理。
默认视频执行窗口为 60 分钟;管理员已经保存的运行时策略仍按保存值执行。视频真正返回 failed/cancelled/expired 等终态时,仍按供应商失败处理。
## 兼容边界
该容错只适用于 `newapi-channel-2` 视频任务。其他 NewAPI 接口、其他 Provider、缺少 Provider Task ID 的任务,以及真实参数错误不进入延期路径。
该容错适用于所有异步视频声明式插件和宿主内置协议。缺少 Provider Task ID 的创建失败、真实参数错误、鉴权错误和非法插件映射不进入重试路径。

View File

@@ -78,6 +78,18 @@ description: 当前项目已实现功能的手工验证清单与未确认边界
- [ ] 登录真实画布验收入口、设置、详情、输入及窗口偏好;检查画布缩放和快捷键不干扰浮窗。
- [ ] 真实 Agent 运行、工具写入、媒体生成与 SSE 重连待联调;当前专项测试使用模拟模型和本地数据库,不替代真实上游验收。
## 异步视频统一轮询容错
- 声明式视频插件、遗留内置视频协议和 RunningHub 视频工作流统一在创建成功后等待 30 秒首次查询,后续每 30 秒查询;图片、音频和文本协议不受影响。
- 查询阶段的 HTTP 408/409/425/429/5xx、临时网络错误、断流、渠道槽位或熔断,以及最多连续两次非法 JSON 不再直接结束任务;HTTP 404 和结构化 `task_not_exist` 连续第三次才结束。
- 结果下载复用同一暂态错误分类,最多尝试 3 次且不重新创建或轮询上游任务。默认视频任务执行窗口由 30 分钟改为 60 分钟;已保存的管理员策略不会被覆盖。
- [x] `cd backend && go test ./internal/app ./internal/platform -count=1`:通过。
- [x] `cd backend && go test ./... -count=1`、`go vet ./...`:通过。
- [ ] 在测试渠道制造一次查询 502,确认任务保持运行、供应商任务 ID 不变,后续查询恢复后正常完成且不重复扣费。
- [ ] 检查任务日志只出现一次「查询暂时异常」与一次「查询已恢复」,请求明细仍保留每次上游调用。
- [ ] 让视频下载首次返回 502、第二次成功,确认宿主不重新查询或创建任务;连续三次下载失败时应返回包含供应商任务 ID 的错误。
- [ ] 将管理员视频超时仍设为旧值的升级环境启动一次,确认保存值保持不变;新安装环境默认显示 60 分钟。
## 画布图片资源缓存链路优化
消除首屏满屏转圈与"一直在下载":后端图片资源改 30 天磁盘强缓存(`Cache-Control: max-age=2592000`),前端内存防抖 IndexedDB 元数据写入、O(1) 淘汰预检、并发下载 4→16,视口内远程图片直链首帧上屏(走原生磁盘缓存秒开),Blob 缓存后台平滑升级。已编译通过,需浏览器最终确认:

View File

@@ -0,0 +1,109 @@
# 统一异步视频轮询容错设计
## 背景
当前异步视频渠道分别在声明式协议和多个内置协议中维护轮询循环。轮询间隔分散为 2.5 秒、3 秒、5 秒、10 秒或 20 秒;除少数协议外,一次 HTTP 502、429、网络超时或连接中断就会结束执行,使本地任务失败并退款,而上游任务可能仍在继续并最终成功。
本改动面向开源宿主整体修复,不识别任何供应商、域名或私有模型。
## 目标
- 所有异步视频协议统一每 30 秒轮询一次,声明式插件和内置协议行为一致。
- 创建成功后等待 30 秒再首次查询,避免刚创建即查询。
- 单次可恢复查询异常不得终止整个生成任务。
- 只在上游明确返回终态失败、遇到确定不可恢复错误或达到任务总超时时结束任务。
- 成品已生成后,临时下载异常不得立即丢失任务结果。
- 保留现有任务 ID、计费、审计、渠道并发、熔断和人工恢复边界。
## 非目标
- 不改变各供应商的请求字段、状态映射或结果结构。
- 不为单独渠道增加判断或插件私有配置。
- 不将轮询改造成释放 worker 的持久化后台状态机;该方向可作为后续独立重构。
- 不自动重试创建请求,避免在缺少供应商幂等保证时重复创建计费任务。
## 统一轮询执行器
在 `backend/internal/app` 增加宿主级视频轮询执行器。所有异步视频实现通过回调完成一次查询和供应商特有的状态解析,执行器负责等待、错误分类、重试、超时和通用日志。
执行流程:
1. 创建请求成功并取得供应商任务 ID。
2. 等待 30 秒。
3. 执行一次查询。
4. 查询返回处理中时,再等待 30 秒。
5. 查询遇到可恢复异常时记录重试信息,等待统一间隔或更长的 `Retry-After` 后继续。
6. 查询成功后清零连续异常计数。
7. 收到成功或明确失败终态时退出。
8. 上下文截止时按视频任务超时处理。
恢复一个已有供应商任务 ID 时也遵守同一轮询节奏,避免 worker 重启后形成突发查询。
## 错误分类
以下只适用于已经取得供应商任务 ID 后的查询或结果下载阶段:
### 可恢复
- HTTP 408、409、425、429、500、502、503、504 以及其他 5xx。
- DNS、连接重置、临时网络错误、请求超时和响应读取中断。
- 渠道熔断暂时开启或并发槽暂时不可用。
- 查询返回 200 但响应暂时不是合法 JSON:允许有限次数连续重试,避免网关临时返回 HTML;连续 3 次后视为协议错误。
HTTP 429 的 `Retry-After` 大于 30 秒时优先遵守,但等待时间不得越过任务截止时间。
### 有条件恢复
- HTTP 404:连续 3 次查询都不存在后才结束,兼容供应商创建后异步同步任务记录的窗口。
- 已有协议明确把某个业务错误定义为“尚不可查”时,统一纳入相同计数,不再维护供应商专属睡眠循环。
### 不可恢复
- HTTP 400、401、403、422 等明确的参数、鉴权或权限错误。
- 插件清单无法构造请求、响应映射本身非法或本地校验失败。
- 上游明确返回 `failed`、`cancelled`、`canceled`、`expired` 等终态。
- 用户主动取消或宿主关闭导致的上下文取消。
## 下载阶段
结果状态成功后,媒体下载复用同一可恢复网络错误分类。下载最多连续尝试 3 次,每次至少等待 30 秒并遵守 `Retry-After`;仍失败时返回带供应商任务 ID 的明确错误,人工恢复仍可只查询原任务并再次下载,不创建新任务。
## 超时与运行时策略
- 全局视频任务默认超时从 30 分钟调整为 60 分钟。
- 已保存的管理员运行时策略保持原值,升级不静默覆盖管理员配置;管理员可手动调整为 60 分钟。
- 所有等待和 HTTP 请求都服从任务上下文截止时间,不额外延长租约之外的生命周期。
## 日志与计费
- 每次上游 HTTP 失败仍保留 API 调用审计记录。
- 可恢复的轮询异常不得触发任务终态、退款或重新创建供应商任务。
- 首次进入重试和恢复正常时写任务日志;连续相同错误不按每次轮询刷屏。
- 最终成功沿用现有结算流程;明确失败或总超时沿用现有失败与退款流程。
## 兼容与迁移
- 不修改数据库结构。
- 声明式插件清单无需升级,已安装插件自动获得统一策略。
- 内置视频协议只迁移其查询循环,不改变创建和响应解析代码。
- 前端仍查询宿主任务状态,无需改变前端轮询间隔。
## 测试策略
先写失败测试,再实现:
- 统一间隔常量为 30 秒,首次查询前也等待该间隔。
- 声明式协议经历 `502 -> processing -> succeeded` 后成功,且只创建一次上游任务。
- 一个内置协议经历网络超时或 503 后继续并成功。
- 401、400 和明确失败状态立即结束。
- 404 前两次重试、第三次结束;正常响应会重置连续错误计数。
- `Retry-After` 生效且不越过上下文截止时间。
- 下载在临时 502 后重试成功,不重复创建任务。
- 上下文取消与总超时能够及时退出。
- 后端完整测试 `go test ./...` 通过。
## 风险与控制
- 统一 30 秒会降低原来高频协议的结果发现速度,但显著减少供应商压力和限流概率;这是本次明确接受的产品取舍。
- 持续故障会让任务保持运行更久,并占用 worker。通过现有并发上限、渠道熔断和 60 分钟总超时控制;后台状态机另行设计。
- 过宽重试可能掩盖配置错误,因此鉴权、参数、映射和明确终态不进入重试。