三个接口一起 gather,其中一个返回 500,结果列表里那一项是异常对象,代码照常往下跑,最后写库的是空值,日志里一条错误都没有。第一次遇到会往复杂处想,其实漏掉的多是个小前提。

res = await asyncio.gather(*tasks, return_exceptions=True)
for i, r in enumerate(res):
    if isinstance(r, BaseException):
        log.error('task %s failed: %r', i, r)

return_exceptions 只是把异常当成返回值收进结果列表,不会抛出也不会打日志,调用方不逐项判断类型,异常就被当成正常数据继续往下流转。这一点先确认清楚,后面就不用反复试。

「gather 异常吞没」这类改动最怕改一半,回滚脚本先准备好,再动生产。新版本先放一台机器上跑一天再全量,问题一般在这段时间暴露。

备份不是备了就行,恢复一次才算数,很多备份文件打开才发现是空的。

具体做法是,拿到结果后逐项判断是否为异常实例,别直接取字段;把异常项挑出来单独记录并计数,超过阈值就中止整批任务;需要整体失败就用 gather 默认行为,让第一个异常直接抛出。

改完照着核一遍,结果列表里不再混入异常对象,失败任务在日志中有明确的一条记录可以追溯。

作者 9823189