轮询只认 succeeded 漏掉 completed:一次状态机误判导致的 10 分钟假超时
一次 Telegram 机器人视频任务的“超时”,事后复盘发现根本不是后端慢,而是轮询客户端的状态机写窄了:它只认 succeeded 这一种成功状态,而后端 muse2api 返回的成功状态实际是 completed。任务早已出片成功,却被客户端一路等到 10 分钟上限,误报超时。这类问题不只浪费一次等待,更危险的是它会把一次成功伪装成失败,进而触发重试、重复出片甚至误告警。
一、现象:明明出片了,却报超时
流程很简单:机器人收到用户请求后提交视频任务,拿到任务 ID,随后每隔几秒查询一次任务状态,直到拿到成品地址或超时。某次任务中,用户迟迟没有收到视频,日志最后一行却是“任务超时”。第一反应是后端排队或生成太慢,但去后端核查时发现,该任务其实早就完成了,成品文件也已经生成。也就是说,超时是客户端自己“等出来”的,而不是后端真的没有完成。
二、排查:先看原始状态值,而不是先看结论
排查这类问题,最容易走的弯路是只看封装后的结论,例如日志里只有“超时”两个字,却没有当时的原始状态。正确的做法是把每一次轮询的原始返回原样记下来:时间、任务 ID、HTTP 状态、返回体里的状态字段。重新翻查原始记录后,链条立刻清晰了:任务状态经历了从处理中到 completed 的变化,客户端却始终没有把它当作成功。客户端的判定逻辑里,成功集合只有一个值 succeeded,于是 completed 被当成“还没成功”,继续轮询,直到 10 分钟上限触发超时分支。
三、根因:把一种成功写成了唯一的成功
根因不是网络抖动,也不是超时时间太短,而是对状态语义做了过度假设。不同后端、不同版本对“成功”的命名并不统一:有的叫 succeeded,有的叫 completed,有的还会区分 finished、done 等写法。把其中一种写进代码并当作唯一标准,本质上是把文档示例当成了协议本身。更深一层的问题是,代码没有对未知终态做分流:它只区分“成功”和“未成功”,没有第三类“已结束但我不认识”。于是 completed 这种已经结束的状态,被错误地放进了“继续等”的桶里。
四、修复:三处改动,一次性收口
第一,把 completed 纳入终态集合。成功集合不再是单个值,而是明确列出 succeeded 和 completed;失败集合同样要显式列出,例如 failed、cancelled 等;其余状态默认视为处理中,但要留出未知终态的出口。
第二,未知终态先记录原始状态值再判定。遇到不在已知集合里的状态时,不要直接把它当作处理中无限等待,也不要直接判失败。先把原始状态值、任务 ID、时间戳完整记入日志,再根据是否属于“已结束语义”做一次显式判定。这一条日志在后续排查中价值极大:它能立刻区分“后端一直没结束”和“后端结束了但客户端不认识”。
第三,超时提示区分“仍在处理”与“状态未知”。原来的超时提示只有一句话,用户和运维都无法判断下一步该做什么。修复后分两种文案:一种是轮询期间状态始终是处理中,到达上限后提示“任务仍在处理,请稍后重试或联系支持并提供任务 ID”;另一种是遇到未知终态时提示“任务状态未知,已记录原始状态,请勿重复提交”。前者避免用户误以为任务失败而重复提交,后者避免把未知状态悄悄吞掉。
修复后的判定逻辑可以概括为:先判断状态是否在成功集合里,是则取成品返回;再判断是否在失败集合里,是则返回失败原因;再判断是否属于已知的处理中状态,是则继续轮询;否则记录原始状态值并走未知终态分支,而不是继续等待。配合 10 分钟上限和轮询间隔,这个结构既不会把成功误判为超时,也不会把真正的失败无限拖延。
五、可以复用的检查清单
第一,对接任何异步任务 API 时,先向后端确认全部终态枚举,而不是只抄示例里的一个成功值;如果文档没有写全,就在测试中实际跑通成功、失败、取消等路径,把真实返回值记录下来。第二,轮询代码里永远保留一个“未知状态”分支,并把原始值写进日志;日志里只有结论、没有原始值,等于把最重要的证据扔掉了。第三,超时不是万能兜底:超时只能说明“在限定时间内没有等到已知成功”,不能直接等价于“任务失败”。文案、重试策略和告警级别都要按这个语义来区分。第四,上线前做一次状态矩阵测试:成功、失败、处理中、未知状态各跑一遍,确认每一种都走到预期的分支。
这次故障的修复成本其实很小,改动不过几行,但它暴露的习惯值得警惕:把一种成功当成全部成功,把超时当成失败的同义词。状态机每窄一分,误判的空间就大一分。对异步任务来说,最稳妥的做法不是把超时设得更长,而是把状态集合写全、把未知状态显式化、把提示文案区分开。这样,下一次后端换一个成功命名时,系统至少能先说一句“我不认识这个状态”,而不是默默等满 10 分钟,再给用户一个错误的超时结论。
- 点赞
- 收藏
- 关注作者
评论(0)