fix: 按实测数据放宽图片生成超时并修复租约不足 #62

Closed
opened 2026-08-25 21:24:25 +08:00 by ila · 4 comments
Owner

基本信息

  • 类型:缺陷(运维配置)
  • 所属 Epic:#3
  • 所属 MVP / 版本:#35 / MVP-2
  • 阶段:已完成

依赖与并行

  • 前置工单:#58(已把三层超时调到 300 秒并完成一次受控复验)
  • 是否允许与前置工单并行:否
  • 原因:本工单是 #58 的直接后继。#58 的结论"不继续把超时提高到 300 秒以上"已被实测证据推翻,需在其配置基线上继续调整同一组值。

子项目影响

  • 仅影响的子项目 / 交付单元:portal(运行配置)、管理端中的 ProviderModel 配置数据
  • 是否跨子项目:否
  • 是否修改共享接口或契约:否;唯一事实来源:portal/config/settings.yml 与 provider_models 表
  • 各子项目需要执行的验证:Portal 重启后可访问;一次真实图片生成形成可审计终态

原始需求

  • 来源:用户对话
  • 提出时间:2026-08-25
  • 关键原话或脱敏摘要:用户报告"最新代码,portal 端 test 用户登录后文生图失败",要求分析具体原因。用户指出"同样的参数,cmhub 正常文生图",并授权对上游做受控对照请求以定位差异。

要解决什么

实测证据(2026-08-25,经用户授权的受控请求)

对上游 POST /v1/images/generations 发送与 chorus 当前完全一致的请求体,不设客户端上限,跑到底:

http_code=200  ttfb=161.97s  total=385.10s  size=4,877,772 B  avg=12.7 KB/s

传输曲线(每 30 秒累计字节):

时刻 累计 区间速率
0–162s 0 无任何数据
180s 688 KB —
210s 1.18 MB 17.3 KB/s
240s 1.84 MB 22.4 KB/s
270s 2.43 MB 20.2 KB/s
300s 3.24 MB 25.0 KB/s
330s 3.65 MB 16.5 KB/s
360s 4.17 MB 17.7 KB/s
385s 4.65 MB(完成) ~21 KB/s

一次完整生成分两段:生成阶段 162 秒无任何响应字节,随后传输阶段 223 秒以 17~25 KB/s 稳定吐出 4.65 MB base64,全程无停顿。

根因

chorus 的 http.Client.Timeout 是从发起请求到读完 body 的绝对总时长(internal/platform/http/safehttp.go:94),body 由 Do() 用 io.ReadAll 一次读完,整个读取过程都在该上限内。当前配置 300 秒,在收到 3.24 MB(完整响应的 66%)时被切断,只差 145 秒、1.4 MB。

上游没有故障,也不是"对复杂提示词无响应"。ResponseHeaderTimeout 从未起作用。

被证伪的两个假设

  • "上游对该提示词不返回":错。上游 200 返回并完整出图,只是响应体大且传输慢。此前"耗时精确等于超时值"的现象来自本地切断,不是上游超时。
  • "文生图缺少 size 参数导致图过大":错。对照请求显示,加与不加 size:"1024x1024",上游返回的都是 1536×1024,该中转站直接忽略 size。补 extra_body 无效。

做什么 / 不做什么

  • 做:把三层超时调整到能覆盖实测 385 秒并留足余量的值。
  • 不做:
    • 不修改超时语义(把绝对总时长改为空闲间隔超时属治本方案,另建工单,见"遗留")。
    • 不修改 Provider 协议实现、retryable 判定、SSRF 校验、端口白名单或数据库结构。
    • 不修改 provider_models 中除 timeout_ms 以外的字段,不再尝试补 size。
    • 不反复提交任务试错;一次验证后无论成败都停止并回写证据。
    • 不把 API Key、上游响应正文、生成图片或真实用户数据写入工单、Wiki、Git 或日志。

已确认方案

三处必须联动,缺一不可:

位置 现值 改为 说明
portal/config/settings.yml → provider.http_timeout_seconds 300 900 全局 HTTP 客户端硬上限;per-model timeout_ms 不可能超过它
provider_models.id=4(gpt-image-2)的 timeout_ms,管理端修改 300000 900000 worker 的 context.WithTimeout
portal/config/settings.yml → worker.lease_seconds 360 960 受启动校验约束

900 秒对实测 385 秒有 2.3 倍余量。

启动校验会拒绝不一致的组合:

// internal/config/config.go:220
if cfg.WorkerLeaseDuration <= cfg.ProviderHTTPTimeout+5*time.Second { /* 拒绝启动 */ }

internal/config/file.go:84 对 YAML 有同样约束。只改其中一到两处,结果要么被静默截断,要么进程起不来。

同时修掉的租约不足问题

现在 lease_seconds=360 小于实测的 385 秒。即使放开 HTTP 超时,租约也会在请求完成前到期,任务可能被另一个 worker 重新取走,造成对上游的重复调用。

现有校验 lease > http_timeout + 5 只比对配置值,管不到真实耗时,因此这个洞不会被它发现。本次把 lease 提到 960 一并覆盖。

已知代价

极端情况下单个任务会占用一个 worker 槽位最长 900 秒。当前为单机单人使用,可接受。后续若提高并发,需另立工单讨论 worker 并发度。

预计修改文件:

  • portal/config/settings.yml
  • 管理端 ProviderModel gpt-image-2(provider_models.id=4)的 timeout_ms,属配置数据不属代码

需求变化记录

日期 变化内容 原因 用户确认
2026-08-25 推翻 #58 的"不继续把超时提高到 300 秒以上" 300 秒下同一提示词连续多次失败 是
2026-08-25 删除原验收标准中"先确认 cmhub provider 默认超时秒数"一项 已由实测数据直接给出所需值,无需再依赖 cmhub 参照 是
2026-08-25 目标值由"建议 900、待 cmhub 值确认"改为确定的 900/900000/960 实测总耗时 385 秒,900 秒余量充足 是
2026-08-25 增加 worker.lease_seconds 不足的修复 实测 385 秒已超过现值 360 秒,存在重复调用上游的风险 是

设计与原型门禁

  • 修改类型:非 UI
  • 所需设计证据:架构、API、数据、状态或流程设计
  • 可编辑设计源链接、版本或事实来源:本工单"要解决什么"中的实测数据;internal/platform/http/safehttp.go:94、internal/config/config.go:220
  • 本地 HTML 审核快照路径和版本(不适用时说明原因):不适用,本工单不改变任何界面结构或交互
  • 本地浏览方式和资源完整性检查:不适用
  • 版本、revision 或确认日期:2026-08-25
  • 状态:已确认
  • 确认人、确认时间和覆盖范围:ila,2026-08-25,覆盖三层超时值调整与一次真实验证
  • 无需 UI 原型或无需任何原型的原因:只调整运行配置数值,界面无变化

文档影响

  • 更新常见修改或故障排查

"三层超时必须联动、否则被截断或起不来",以及"上游可能在生成阶段长时间不返回字节、随后慢速流式传输",属长期排错知识,需写入 Troubleshooting。

交付文档影响

  • 无交付文档影响,原因:本机运维配置调整,无外部受众。

验收标准

  • 三处值分别为 900 / 900000 / 960,且满足 lease_seconds > http_timeout_seconds + 5。
  • Portal 重启后可正常访问和登录,worker 正常启动。
  • 仅创建一个新的真实 image generation,使用与 generation 13 相同的提示词,形成可审计终态。
  • 该 generation status=succeeded,有受保护输出与缩略图,rendered_prompt 非空。
  • attempts 中记录的耗时与实测量级(约 400 秒)相符,且未出现第二次 provider 尝试(证明租约未提前到期导致重复调用)。
  • 没有凭据、上游响应正文、生成图片或真实用户数据进入工单、Wiki、Git 和日志。

验证方式

# 1. 确认配置
Get-Content portal/config/settings.yml

# 2. 重启 Portal 并确认进程与端口
./scripts/status-all.bat

# 3. 浏览器登录 test 账号,提交与 generation 13 相同的提示词,仅提交一次

# 4. 核对终态
# SELECT id,status,error_code,provider_attempt_count,
#        JSON_EXTRACT(attempts,'$[1].latency_ms') lat_ms
#   FROM generations ORDER BY id DESC LIMIT 3;

完成证据

最终差异

配置变更(不进版本库)

位置 原值 现值
portal/config/settings.yml → provider.http_timeout_seconds 300 900
portal/config/settings.yml → worker.lease_seconds 360 960
provider_models.id=4 → timeout_ms 300000 900000

portal/config/settings.yml 被 .gitignore:49 排除,timeout_ms 是数据库配置数据,两者均无提交记录。portal/config/settings.example.yml(已跟踪)按计划保持 45 / 60 未改动。

timeout_ms 通过 SQL 修改(本会话无法驱动管理端页面),影响行数 1:

UPDATE provider_models SET timeout_ms=900000
 WHERE id=4 AND model_id='gpt-image-2' AND api_type='images';

已确认 config_audit_logs 表在当前库中不存在,此举未绕过任何已实现的审计机制。

代码变更

  • 378a95e docs: 补充上游慢速流式传输与三层超时判读 (#62) —— 仅 docs/06-troubleshooting.md 一个文件,是 Wiki 镜像的导出结果。

服务重启

D:\supervisor\supervisord.exe ctl /c D:\supervisor\supervisord.conf restart chorus-user
chorus-user: restarted

新进程 PID 67652,启动 22:52:01,日志末行 chorus portal listening on 127.0.0.1:8085,无配置错误,即 internal/config/config.go:220 的 lease > timeout + 5 校验通过。

测试结果

真实生成验证 —— generation 14,人工在浏览器用 test 账号提交一次,提示词与 generation 13 相同:

项 结果
status succeeded
总耗时 465 秒(22:54:44 → 23:02:30)
provider_attempt_count 1
error_code NULL
attempt 记录 retryable: false,无 error_code,latency_ms: 465462
输出 image/png,3,448,452 字节,已落盘
缩略图 91,648 字节,已生成

按旧配置该任务会在 22:59:44(300 秒)被切断;实际撑到 23:02:30 完成,确认 900 秒上限生效。provider_attempt_count=1 确认 960 秒租约未提前到期,本工单要修的租约不足问题已消除。

耗时与实测基线对照:受控请求 385 秒 / 4.65 MB,本次 465 秒 / 3.45 MB,同一量级。465 秒占 900 秒预算的 52%。

流程验证

python dev_scripts/harness.py sync --check   → Wiki 镜像检查通过(15 个页面全部一致)
python dev_scripts/harness.py check --strict → DevHarness 检查通过
python -m unittest discover -s tests         → OK

未运行 go build/vet/test:本工单不含 Go 代码改动。

未验证部分

  • 900 秒余量是否长期够用:仅有 385 秒与 465 秒两个样本,波动区间未知。若后续再出现超时,应先比对 attempts.latency_ms 与基线,而非直接上调超时。
  • timeout_ms 经管理端页面修改的路径未验证:本次走 SQL,管理端表单对该字段的校验与生效行为未覆盖。
  • 其他 ProviderModel 未调整:id=3(文本,60000)、id=5(图片编辑,300000)保持原值,其超时是否充足未验证。
  • 并发场景未验证:单任务最长占用一个 worker 槽位 900 秒的影响,在单人单机下未构成压力,多并发行为未测。

提交哈希

  • 378a95e(Wiki 镜像)

配置类改动无提交哈希,证据以本节记录的配置内容、SQL 语句和运行结果为准。

长期 Wiki 页面与 revision

  • Troubleshooting → 22f9502779d019edbf433d632905eddcb41f648b

修改内容:把"attempt latency 接近超时值"的判读从"先检查配置"扩展为明确说明不能据此判断上游无响应,补充 http.Client.Timeout 覆盖整个 body 读取的语义、三层确认顺序、"不要靠反复上调超时试错"的约束,以及本次实测基线(生成阶段约 160 秒无数据,17–25 KB/s 传输,总时长 385–465 秒,响应体 3.4–4.9 MB)。

实施中发现、未纳入本工单的问题

  • generation_outputs.width 与 height 对 generation 14 均为 NULL,但该图实际为 1536×1024,输出尺寸未落库。不影响本次验收,未处理。
  • openai_provider_model.txt(仓库根目录,.git/info/exclude 排除)中的 api key 已失效,且为明文。建议删除。

风险和回退

  • 一次真实请求会产生上游费用,并最长占用一个 worker 槽位 900 秒。
  • 本方案是治标:绝对总时长上限仍然存在,只是抬高了。若将来出现更大的响应体,同样的问题会再次出现。治本方案见"遗留"。
  • 回退:settings.yml 恢复 300/360,ProviderModel 恢复 timeout_ms=300000,重启 Portal;不删除已产生的 generation 与 attempt 审计记录。

遗留(需另建工单)

把 http.Client.Timeout 的绝对总时长语义改为响应头超时 + body 读取空闲间隔超时 + 宽松绝对兜底,与 cmhub 行为一致。

依据:实测显示生成阶段有 162 秒完全无数据,传输阶段速率稳定且无停顿,因此空闲阈值需大于 162 秒(建议 240~300 秒),这样无论响应体多大都不会误杀,也不必反复上调绝对值。

涉及 internal/platform/http/safehttp.go 与配置校验,改动面大,不与本工单合并。

## 基本信息 - 类型:缺陷(运维配置) - 所属 Epic:#3 - 所属 MVP / 版本:#35 / MVP-2 - 阶段:已完成 ## 依赖与并行 - 前置工单:#58(已把三层超时调到 300 秒并完成一次受控复验) - 是否允许与前置工单并行:否 - 原因:本工单是 #58 的直接后继。#58 的结论"不继续把超时提高到 300 秒以上"已被实测证据推翻,需在其配置基线上继续调整同一组值。 ## 子项目影响 - 仅影响的子项目 / 交付单元:`portal`(运行配置)、管理端中的 ProviderModel 配置数据 - 是否跨子项目:否 - 是否修改共享接口或契约:否;唯一事实来源:`portal/config/settings.yml` 与 `provider_models` 表 - 各子项目需要执行的验证:Portal 重启后可访问;一次真实图片生成形成可审计终态 ## 原始需求 - 来源:用户对话 - 提出时间:2026-08-25 - 关键原话或脱敏摘要:用户报告"最新代码,portal 端 test 用户登录后文生图失败",要求分析具体原因。用户指出"同样的参数,cmhub 正常文生图",并授权对上游做受控对照请求以定位差异。 ## 要解决什么 ### 实测证据(2026-08-25,经用户授权的受控请求) 对上游 `POST /v1/images/generations` 发送与 chorus 当前完全一致的请求体,不设客户端上限,跑到底: ``` http_code=200 ttfb=161.97s total=385.10s size=4,877,772 B avg=12.7 KB/s ``` 传输曲线(每 30 秒累计字节): | 时刻 | 累计 | 区间速率 | |---|---|---| | 0–162s | 0 | 无任何数据 | | 180s | 688 KB | — | | 210s | 1.18 MB | 17.3 KB/s | | 240s | 1.84 MB | 22.4 KB/s | | 270s | 2.43 MB | 20.2 KB/s | | **300s** | **3.24 MB** | 25.0 KB/s | | 330s | 3.65 MB | 16.5 KB/s | | 360s | 4.17 MB | 17.7 KB/s | | 385s | 4.65 MB(完成) | ~21 KB/s | 一次完整生成分两段:**生成阶段 162 秒无任何响应字节**,随后**传输阶段 223 秒**以 17~25 KB/s 稳定吐出 4.65 MB base64,全程无停顿。 ### 根因 chorus 的 `http.Client.Timeout` 是**从发起请求到读完 body 的绝对总时长**(`internal/platform/http/safehttp.go:94`),body 由 `Do()` 用 `io.ReadAll` 一次读完,整个读取过程都在该上限内。当前配置 300 秒,**在收到 3.24 MB(完整响应的 66%)时被切断**,只差 145 秒、1.4 MB。 上游没有故障,也不是"对复杂提示词无响应"。`ResponseHeaderTimeout` 从未起作用。 ### 被证伪的两个假设 - **"上游对该提示词不返回"**:错。上游 200 返回并完整出图,只是响应体大且传输慢。此前"耗时精确等于超时值"的现象来自本地切断,不是上游超时。 - **"文生图缺少 `size` 参数导致图过大"**:错。对照请求显示,加与不加 `size:"1024x1024"`,上游返回的都是 1536×1024,**该中转站直接忽略 `size`**。补 `extra_body` 无效。 ## 做什么 / 不做什么 - 做:把三层超时调整到能覆盖实测 385 秒并留足余量的值。 - 不做: - 不修改超时语义(把绝对总时长改为空闲间隔超时属治本方案,另建工单,见"遗留")。 - 不修改 Provider 协议实现、`retryable` 判定、SSRF 校验、端口白名单或数据库结构。 - 不修改 `provider_models` 中除 `timeout_ms` 以外的字段,不再尝试补 `size`。 - 不反复提交任务试错;一次验证后无论成败都停止并回写证据。 - 不把 API Key、上游响应正文、生成图片或真实用户数据写入工单、Wiki、Git 或日志。 ## 已确认方案 三处必须联动,缺一不可: | 位置 | 现值 | 改为 | 说明 | |---|---|---|---| | `portal/config/settings.yml` → `provider.http_timeout_seconds` | 300 | **900** | 全局 HTTP 客户端硬上限;per-model `timeout_ms` 不可能超过它 | | `provider_models.id=4`(`gpt-image-2`)的 `timeout_ms`,管理端修改 | 300000 | **900000** | worker 的 `context.WithTimeout` | | `portal/config/settings.yml` → `worker.lease_seconds` | 360 | **960** | 受启动校验约束 | 900 秒对实测 385 秒有 2.3 倍余量。 启动校验会拒绝不一致的组合: ```go // internal/config/config.go:220 if cfg.WorkerLeaseDuration <= cfg.ProviderHTTPTimeout+5*time.Second { /* 拒绝启动 */ } ``` `internal/config/file.go:84` 对 YAML 有同样约束。只改其中一到两处,结果要么被静默截断,要么进程起不来。 ### 同时修掉的租约不足问题 现在 `lease_seconds=360` **小于实测的 385 秒**。即使放开 HTTP 超时,租约也会在请求完成前到期,任务可能被另一个 worker 重新取走,造成对上游的重复调用。 现有校验 `lease > http_timeout + 5` 只比对配置值,管不到真实耗时,因此这个洞不会被它发现。本次把 lease 提到 960 一并覆盖。 ### 已知代价 极端情况下单个任务会占用一个 worker 槽位最长 900 秒。当前为单机单人使用,可接受。后续若提高并发,需另立工单讨论 worker 并发度。 预计修改文件: - `portal/config/settings.yml` - 管理端 ProviderModel `gpt-image-2`(`provider_models.id=4`)的 `timeout_ms`,属配置数据不属代码 ## 需求变化记录 | 日期 | 变化内容 | 原因 | 用户确认 | |---|---|---|---| | 2026-08-25 | 推翻 #58 的"不继续把超时提高到 300 秒以上" | 300 秒下同一提示词连续多次失败 | 是 | | 2026-08-25 | 删除原验收标准中"先确认 cmhub provider 默认超时秒数"一项 | 已由实测数据直接给出所需值,无需再依赖 cmhub 参照 | 是 | | 2026-08-25 | 目标值由"建议 900、待 cmhub 值确认"改为确定的 900/900000/960 | 实测总耗时 385 秒,900 秒余量充足 | 是 | | 2026-08-25 | 增加 `worker.lease_seconds` 不足的修复 | 实测 385 秒已超过现值 360 秒,存在重复调用上游的风险 | 是 | ## 设计与原型门禁 - 修改类型:非 UI - 所需设计证据:架构、API、数据、状态或流程设计 - 可编辑设计源链接、版本或事实来源:本工单"要解决什么"中的实测数据;`internal/platform/http/safehttp.go:94`、`internal/config/config.go:220` - 本地 HTML 审核快照路径和版本(不适用时说明原因):不适用,本工单不改变任何界面结构或交互 - 本地浏览方式和资源完整性检查:不适用 - 版本、revision 或确认日期:2026-08-25 - 状态:已确认 - 确认人、确认时间和覆盖范围:ila,2026-08-25,覆盖三层超时值调整与一次真实验证 - 无需 UI 原型或无需任何原型的原因:只调整运行配置数值,界面无变化 ## 文档影响 - [x] 更新常见修改或故障排查 "三层超时必须联动、否则被截断或起不来",以及"上游可能在生成阶段长时间不返回字节、随后慢速流式传输",属长期排错知识,需写入 Troubleshooting。 ## 交付文档影响 - [x] 无交付文档影响,原因:本机运维配置调整,无外部受众。 ## 验收标准 - [x] 三处值分别为 900 / 900000 / 960,且满足 `lease_seconds > http_timeout_seconds + 5`。 - [x] Portal 重启后可正常访问和登录,worker 正常启动。 - [x] 仅创建一个新的真实 image generation,使用与 generation 13 相同的提示词,形成可审计终态。 - [x] 该 generation `status=succeeded`,有受保护输出与缩略图,`rendered_prompt` 非空。 - [x] `attempts` 中记录的耗时与实测量级(约 400 秒)相符,且未出现第二次 provider 尝试(证明租约未提前到期导致重复调用)。 - [x] 没有凭据、上游响应正文、生成图片或真实用户数据进入工单、Wiki、Git 和日志。 ## 验证方式 ```powershell # 1. 确认配置 Get-Content portal/config/settings.yml # 2. 重启 Portal 并确认进程与端口 ./scripts/status-all.bat # 3. 浏览器登录 test 账号,提交与 generation 13 相同的提示词,仅提交一次 # 4. 核对终态 # SELECT id,status,error_code,provider_attempt_count, # JSON_EXTRACT(attempts,'$[1].latency_ms') lat_ms # FROM generations ORDER BY id DESC LIMIT 3; ``` ## 完成证据 ### 最终差异 **配置变更(不进版本库)** | 位置 | 原值 | 现值 | |---|---|---| | `portal/config/settings.yml` → `provider.http_timeout_seconds` | 300 | 900 | | `portal/config/settings.yml` → `worker.lease_seconds` | 360 | 960 | | `provider_models.id=4` → `timeout_ms` | 300000 | 900000 | `portal/config/settings.yml` 被 `.gitignore:49` 排除,`timeout_ms` 是数据库配置数据,两者均无提交记录。`portal/config/settings.example.yml`(已跟踪)按计划保持 45 / 60 未改动。 `timeout_ms` 通过 SQL 修改(本会话无法驱动管理端页面),影响行数 1: ```sql UPDATE provider_models SET timeout_ms=900000 WHERE id=4 AND model_id='gpt-image-2' AND api_type='images'; ``` 已确认 `config_audit_logs` 表在当前库中不存在,此举未绕过任何已实现的审计机制。 **代码变更** - `378a95e docs: 补充上游慢速流式传输与三层超时判读 (#62)` —— 仅 `docs/06-troubleshooting.md` 一个文件,是 Wiki 镜像的导出结果。 **服务重启** ``` D:\supervisor\supervisord.exe ctl /c D:\supervisor\supervisord.conf restart chorus-user chorus-user: restarted ``` 新进程 PID 67652,启动 22:52:01,日志末行 `chorus portal listening on 127.0.0.1:8085`,无配置错误,即 `internal/config/config.go:220` 的 `lease > timeout + 5` 校验通过。 ### 测试结果 **真实生成验证** —— generation 14,人工在浏览器用 `test` 账号提交一次,提示词与 generation 13 相同: | 项 | 结果 | |---|---| | status | **succeeded** | | 总耗时 | **465 秒**(22:54:44 → 23:02:30) | | `provider_attempt_count` | **1** | | `error_code` | NULL | | attempt 记录 | `retryable: false`,无 error_code,`latency_ms: 465462` | | 输出 | `image/png`,3,448,452 字节,已落盘 | | 缩略图 | 91,648 字节,已生成 | 按旧配置该任务会在 22:59:44(300 秒)被切断;实际撑到 23:02:30 完成,确认 900 秒上限生效。`provider_attempt_count=1` 确认 960 秒租约未提前到期,本工单要修的租约不足问题已消除。 耗时与实测基线对照:受控请求 385 秒 / 4.65 MB,本次 465 秒 / 3.45 MB,同一量级。465 秒占 900 秒预算的 52%。 **流程验证** ``` python dev_scripts/harness.py sync --check → Wiki 镜像检查通过(15 个页面全部一致) python dev_scripts/harness.py check --strict → DevHarness 检查通过 python -m unittest discover -s tests → OK ``` 未运行 `go build/vet/test`:本工单不含 Go 代码改动。 ### 未验证部分 - **900 秒余量是否长期够用**:仅有 385 秒与 465 秒两个样本,波动区间未知。若后续再出现超时,应先比对 `attempts.latency_ms` 与基线,而非直接上调超时。 - **`timeout_ms` 经管理端页面修改的路径未验证**:本次走 SQL,管理端表单对该字段的校验与生效行为未覆盖。 - **其他 ProviderModel 未调整**:`id=3`(文本,60000)、`id=5`(图片编辑,300000)保持原值,其超时是否充足未验证。 - **并发场景未验证**:单任务最长占用一个 worker 槽位 900 秒的影响,在单人单机下未构成压力,多并发行为未测。 ### 提交哈希 - `378a95e`(Wiki 镜像) 配置类改动无提交哈希,证据以本节记录的配置内容、SQL 语句和运行结果为准。 ### 长期 Wiki 页面与 revision - `Troubleshooting` → `22f9502779d019edbf433d632905eddcb41f648b` 修改内容:把"attempt latency 接近超时值"的判读从"先检查配置"扩展为明确说明**不能据此判断上游无响应**,补充 `http.Client.Timeout` 覆盖整个 body 读取的语义、三层确认顺序、"不要靠反复上调超时试错"的约束,以及本次实测基线(生成阶段约 160 秒无数据,17–25 KB/s 传输,总时长 385–465 秒,响应体 3.4–4.9 MB)。 ### 实施中发现、未纳入本工单的问题 - `generation_outputs.width` 与 `height` 对 generation 14 均为 NULL,但该图实际为 1536×1024,输出尺寸未落库。不影响本次验收,未处理。 - `openai_provider_model.txt`(仓库根目录,`.git/info/exclude` 排除)中的 api key 已失效,且为明文。建议删除。 ## 风险和回退 - 一次真实请求会产生上游费用,并最长占用一个 worker 槽位 900 秒。 - 本方案是治标:绝对总时长上限仍然存在,只是抬高了。若将来出现更大的响应体,同样的问题会再次出现。治本方案见"遗留"。 - 回退:`settings.yml` 恢复 300/360,ProviderModel 恢复 `timeout_ms=300000`,重启 Portal;不删除已产生的 generation 与 attempt 审计记录。 ## 遗留(需另建工单) 把 `http.Client.Timeout` 的**绝对总时长**语义改为**响应头超时 + body 读取空闲间隔超时 + 宽松绝对兜底**,与 cmhub 行为一致。 依据:实测显示生成阶段有 162 秒完全无数据,传输阶段速率稳定且无停顿,因此空闲阈值需大于 162 秒(建议 240~300 秒),这样无论响应体多大都不会误杀,也不必反复上调绝对值。 涉及 `internal/platform/http/safehttp.go` 与配置校验,改动面大,不与本工单合并。
Author
Owner

待确认项澄清(2026-08-25)

验收标准第一项要查的是 "请求超时"(等待上游返回),不是连接超时:

  • 截图中"连接超时秒数 = 30"已是明确值,对应 chorus 的 net.Dialer.Timeout = 10s(internal/platform/http/safehttp.go:64),两边都已单独隔离,不是本工单的调整对象。
  • 截图中"请求超时秒数 = 0,0 表示使用 provider 默认值"——真正生效的秒数在 cmhub 的 provider 级配置里,这才是要查的值,对应 chorus 的 provider.http_timeout_seconds。

实施时需要额外核对的语义差异

只比数字可能不够。若 cmhub 用 Python requests,其 timeout=(connect, read) 中的 read 是两次数据到达之间的最大间隔,而 chorus 的 http.Client.Timeout 是整个请求的绝对总时长。两者数值相同,行为也不同:上游持续缓慢返回数据时 cmhub 不会超时,chorus 到点必砍。

因此确认 cmhub 默认值时,一并确认它是间隔超时还是总时长超时。如果是间隔超时,则不能直接把该秒数抄成 chorus 的 http_timeout_seconds,需要按"该模型实际最长生成时长"另行取值。

本条为待核实假设,未读过 cmhub 源码(本机无该仓库)。

## 待确认项澄清(2026-08-25) 验收标准第一项要查的是 **"请求超时"(等待上游返回)**,不是连接超时: - 截图中"连接超时秒数 = 30"已是明确值,对应 chorus 的 `net.Dialer.Timeout = 10s`(`internal/platform/http/safehttp.go:64`),两边都已单独隔离,不是本工单的调整对象。 - 截图中"请求超时秒数 = 0,0 表示使用 provider 默认值"——真正生效的秒数在 cmhub 的 provider 级配置里,这才是要查的值,对应 chorus 的 `provider.http_timeout_seconds`。 ### 实施时需要额外核对的语义差异 只比数字可能不够。若 cmhub 用 Python `requests`,其 `timeout=(connect, read)` 中的 read 是**两次数据到达之间的最大间隔**,而 chorus 的 `http.Client.Timeout` 是**整个请求的绝对总时长**。两者数值相同,行为也不同:上游持续缓慢返回数据时 cmhub 不会超时,chorus 到点必砍。 因此确认 cmhub 默认值时,一并确认它是间隔超时还是总时长超时。如果是间隔超时,则不能直接把该秒数抄成 chorus 的 `http_timeout_seconds`,需要按"该模型实际最长生成时长"另行取值。 本条为待核实假设,未读过 cmhub 源码(本机无该仓库)。
ila changed title from fix: 放宽图片生成整体超时并联动三层配置 to fix: 按实测数据放宽图片生成超时并修复租约不足 2026-08-25 22:48:29 +08:00
Author
Owner

开始实施(2026-08-25)

阶段:待实施 → 进行中。

前置 #58 检查:其配置基线(300 / 300000 / 360)已部署并在运行中,本工单在此基线上继续调整同一组值,真实依赖已满足。#58 本身仍为待验收,不受本工单影响。

工作区检查:config/local-services.yml 有既存改动、admin/config/settings.yml 与一张截图未跟踪,均为用户已有内容,与本任务无关,保持原样不提交。

实施中发现的偏差(需在验收时确认)

  1. 本工单不产生代码提交。 portal/config/settings.yml 被 .gitignore:49 排除,是本机运行配置;provider_models.timeout_ms 是数据库配置数据。两者都不进版本库,因此"提交哈希"一栏将填"无代码提交",证据以配置内容和运行结果为准。
  2. timeout_ms 通过 SQL 直接修改,而非管理端页面(本会话无法驱动浏览器)。已确认 config_audit_logs 表在当前库中不存在,因此此举没有绕过任何已实现的审计机制。执行的语句会原文记录在完成证据中。
  3. portal/config/settings.example.yml(已跟踪)不修改,保持 45 / 60 的保守默认值。它是新装模板,不应被本机实测值覆盖。
## 开始实施(2026-08-25) 阶段:待实施 → 进行中。 前置 #58 检查:其配置基线(300 / 300000 / 360)已部署并在运行中,本工单在此基线上继续调整同一组值,真实依赖已满足。#58 本身仍为待验收,不受本工单影响。 工作区检查:`config/local-services.yml` 有既存改动、`admin/config/settings.yml` 与一张截图未跟踪,均为用户已有内容,与本任务无关,保持原样不提交。 ### 实施中发现的偏差(需在验收时确认) 1. **本工单不产生代码提交。** `portal/config/settings.yml` 被 `.gitignore:49` 排除,是本机运行配置;`provider_models.timeout_ms` 是数据库配置数据。两者都不进版本库,因此"提交哈希"一栏将填"无代码提交",证据以配置内容和运行结果为准。 2. **`timeout_ms` 通过 SQL 直接修改**,而非管理端页面(本会话无法驱动浏览器)。已确认 `config_audit_logs` 表在当前库中不存在,因此此举没有绕过任何已实现的审计机制。执行的语句会原文记录在完成证据中。 3. **`portal/config/settings.example.yml`(已跟踪)不修改**,保持 45 / 60 的保守默认值。它是新装模板,不应被本机实测值覆盖。
Author
Owner

配置已生效,等待人工完成验证提交(2026-08-25)

已完成

项 原值 现值 方式
portal/config/settings.yml → provider.http_timeout_seconds 300 900 直接编辑(该文件被 .gitignore:49 排除,不进版本库)
portal/config/settings.yml → worker.lease_seconds 360 960 同上
provider_models.id=4 的 timeout_ms 300000 900000 SQL,见下

执行的 SQL(影响行数 1):

UPDATE provider_models SET timeout_ms=900000
 WHERE id=4 AND model_id='gpt-image-2' AND api_type='images';

Portal 已通过 Supervisor 重启:

D:\supervisor\supervisord.exe ctl /c D:\supervisor\supervisord.conf restart chorus-user
chorus-user: restarted

新进程 PID 67652,启动时间 22:52:01,日志末行 chorus portal listening on 127.0.0.1:8085,无配置错误——说明 lease_seconds > http_timeout_seconds + 5 的启动校验(internal/config/config.go:220)已通过。

portal/config/settings.example.yml(已跟踪)按计划保持 45 / 60 不变。

阻塞:最后一步需要人工操作

验收标准要求"提交一次真实生成并核对终态",但本会话无法完成提交:

  • 无法驱动浏览器登录 test 账号;
  • OpenAPI 路径需要 API Key 明文。库中 api_keys 只有 1 条且只存哈希;chorus-runtime.env.ps1 中并没有 CHORUS_API_KEY 变量(此前误读,那两行实际是 CHORUS_API_KEY_RATE_LIMIT_*)。

因此请人工在浏览器中用 test 账号提交一次与 generation 13 相同的提示词,其余核对与证据回写由 Agent 完成。在此之前本工单保持进行中,不标记待验收。

待验证的预期

  • 请求总耗时约 400 秒(实测基线 385 秒),不再在 300 秒被切断;
  • provider_attempt_count 应为 1,attempts 中只有一次 provider 尝试——若出现第二次,说明租约仍然不足,需要重新评估 960 秒。
## 配置已生效,等待人工完成验证提交(2026-08-25) ### 已完成 | 项 | 原值 | 现值 | 方式 | |---|---|---|---| | `portal/config/settings.yml` → `provider.http_timeout_seconds` | 300 | **900** | 直接编辑(该文件被 `.gitignore:49` 排除,不进版本库) | | `portal/config/settings.yml` → `worker.lease_seconds` | 360 | **960** | 同上 | | `provider_models.id=4` 的 `timeout_ms` | 300000 | **900000** | SQL,见下 | 执行的 SQL(影响行数 1): ```sql UPDATE provider_models SET timeout_ms=900000 WHERE id=4 AND model_id='gpt-image-2' AND api_type='images'; ``` Portal 已通过 Supervisor 重启: ``` D:\supervisor\supervisord.exe ctl /c D:\supervisor\supervisord.conf restart chorus-user chorus-user: restarted ``` 新进程 PID 67652,启动时间 22:52:01,日志末行 `chorus portal listening on 127.0.0.1:8085`,无配置错误——说明 `lease_seconds > http_timeout_seconds + 5` 的启动校验(`internal/config/config.go:220`)已通过。 `portal/config/settings.example.yml`(已跟踪)按计划保持 45 / 60 不变。 ### 阻塞:最后一步需要人工操作 验收标准要求"提交一次真实生成并核对终态",但本会话无法完成提交: - 无法驱动浏览器登录 `test` 账号; - OpenAPI 路径需要 API Key 明文。库中 `api_keys` 只有 1 条且只存哈希;`chorus-runtime.env.ps1` 中并没有 `CHORUS_API_KEY` 变量(此前误读,那两行实际是 `CHORUS_API_KEY_RATE_LIMIT_*`)。 因此请人工在浏览器中用 `test` 账号提交**一次**与 generation 13 相同的提示词,其余核对与证据回写由 Agent 完成。在此之前本工单保持进行中,不标记待验收。 ### 待验证的预期 - 请求总耗时约 400 秒(实测基线 385 秒),不再在 300 秒被切断; - `provider_attempt_count` 应为 1,`attempts` 中只有一次 provider 尝试——若出现第二次,说明租约仍然不足,需要重新评估 960 秒。
Author
Owner

验收通过(2026-08-26)

用户于 2026-08-26 明确验收通过。

验收依据:generation 14 成功(465 秒、provider_attempt_count=1、输出 3,448,452 字节 PNG 与缩略图齐全),配置三处值为 900 / 900000 / 960,sync --check、check --strict 与单元测试均通过。详见工单正文"完成证据"。

提交 378a95e 已推送至 origin/main。

本工单关闭。工单正文"未验证部分"记录的四项(900 秒余量的长期充分性、管理端页面修改 timeout_ms 的路径、其他 ProviderModel 的超时、并发场景)仍然有效,不因验收而消除。

正文"遗留"一节提出的空闲间隔超时治本方案尚未建单,需要时另行提出。

## 验收通过(2026-08-26) 用户于 2026-08-26 明确验收通过。 验收依据:generation 14 成功(465 秒、`provider_attempt_count=1`、输出 3,448,452 字节 PNG 与缩略图齐全),配置三处值为 900 / 900000 / 960,`sync --check`、`check --strict` 与单元测试均通过。详见工单正文"完成证据"。 提交 `378a95e` 已推送至 `origin/main`。 本工单关闭。工单正文"未验证部分"记录的四项(900 秒余量的长期充分性、管理端页面修改 `timeout_ms` 的路径、其他 ProviderModel 的超时、并发场景)仍然有效,不因验收而消除。 正文"遗留"一节提出的空闲间隔超时治本方案尚未建单,需要时另行提出。
ila closed this issue 2026-08-26 09:00:37 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/chorus#62