fix(soak): 环境客户端把传输层失败也翻成 AppWorldError,「连不上」那一档原本没实现

execute 的 docstring 一直写着「只有环境自己坏了——连不上、HTTP 非 2xx、返回体不是约定
形状——才抛 AppWorldError」,但实现里只处理了后两档。连不上、读超时、连接中途断掉时,
httpx 抛的是它自己的异常类型,而上层的动作执行接缝只认 AppWorldError,于是容器一挂整次
运行会以一个未捕获的第三方异常炸出去,正确的行为是记成环境故障、由库合成一段观察、以
env_error 收尾。

这个缺口是压测里真把容器 docker kill 掉之后才暴露的——在那之前它只写在 docstring 里。
顺带覆盖了另一个一直没验过的路径:客户端读超时同属这一类。

CancelledError 继承 BaseException,不在 httpx.HTTPError 的范围内,取消照旧原样穿过去。

修完重跑那一类,七条判据仍然全过。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-11 11:16:08 -04:00
parent 67f0d355d0
commit e590bea70e
+14 -1
View File
@@ -796,8 +796,21 @@ async def _post_json(
path: str,
body: Mapping[str, Any],
) -> Any:
"""打一个 POST,检查状态码,剥掉 environment server 统一的 output 包装。"""
"""打一个 POST,检查状态码,剥掉 environment server 统一的 output 包装。
**传输层的失败也要翻成 `AppWorldError`。** 连不上、读超时、连接中途断掉,httpx 抛的是
它自己的异常类型,而上层的动作执行接缝只认 `AppWorldError`——不翻的话,容器挂掉会让整
次运行以一个未捕获的第三方异常炸出去,而正确的行为是记成环境故障、由库合成一段观察、
以 `env_error` 收尾。这个缺口是压测里真把容器 `docker kill` 掉之后才暴露的:在那之前
「连不上」这一档只写在上面那个 docstring 里,没有实现。
`asyncio.CancelledError` 不在 `httpx.HTTPError` 的范围内(它继承 `BaseException`),
所以取消照旧原样穿过去。
"""
try:
response = await client.post(f"{base_url}{path}", json=dict(body))
except httpx.HTTPError as exc:
raise AppWorldError(f"{path} 的请求没能完成:{type(exc).__name__}: {exc}") from exc
if response.is_error:
raise AppWorldError(f"{path} 返回 HTTP {response.status_code}{response.text[:2000]}")
payload = response.json()