第15章 故障、重试与异步执行不能悄悄扩大权限
授权依赖故障是业务状态
身份平台不可用、JWKS 刷新超时、授权状态缓存失效、审批服务延迟,都可能发生在一次工具调用中。工程团队需要预先定义这些故障如何影响不同动作。对于敏感写入,无法确定当前授权通常应拒绝;对于低风险读取,可以在明确的短缓存窗口内继续,但不能临时把整个系统变成永久离线验签。
本书使用两秒 HTTP 超时,服务端设置头部读取、请求读取、写入和空闲超时,并限制头大小。依赖不可用时不放行。这样的策略适合教学观察,真实容量规划还需考虑连接池、并发上限、超时传播和熔断。超时不应简单层层相加,否则代理等待身份服务,再等待业务服务,会超过上游任务预算。
错误分类决定 Agent 的恢复方式。输入格式错误需要修正输入,权限不足需要获取真实授权,审批失效需要重新批准,依赖故障可以在安全条件下稍后重试,业务状态冲突需要查询当前对象。把所有错误包装成“工具暂时失败,请重试”会让模型不断重放已执行的副作用。
队列中的授权会变旧
异步任务若保存一个长寿命管理员 bearer,排队越久,暴露越大。更合理的是保存任务引用、责任主体、租户、规范化参数与授权记录引用。执行器领取任务后重新确认当前身份状态,获取短期目标凭据,并在提交前检查审批和业务对象。任务完成审计应保留原始责任主体与当前执行者。
失败案例是员工离职前提交一批批量退款,任务排队两小时。执行器只在入队时检查权限,实际处理时全部放行。用户退出、撤权和审批撤回都无法影响队列。修复需要把授权检查放到副作用之前,而非仅放在创建任务时。若业务允许已经批准的任务离职后继续,也必须通过独立任务授权明确表达,而不是无意保留旧令牌。
失败关闭也有产品成本
身份服务故障导致全部写入暂停,会影响客服工作。解决方式可以是提高身份依赖可用性、把授权版本与业务事务适当共置、为不同风险设计有限缓存,而不是在事故中临时绕过。任何应急特权都需要明确的操作人、范围、期限和审计,不能由 Agent 自行选择“紧急模式”。
生产中的健康检查也要分层。进程能响应 200 不表示它能访问授权状态;签名验证成功不表示撤权链路工作;新令牌正常不表示旧键已经退休。运行看板应跟踪各类拒绝、依赖延迟、缓存年龄和审批消费异常,避免把安全边界的失效藏在总成功率里。
实验与演练
运行 go test -race -run TestKeyRotationAndDependencyOutage -v ./lab。测试真正关闭身份 HTTP 服务,再提交一个刚刚有效的令牌,要求被拒绝。它没有通过模拟布尔值跳过网络。阅读业务授权函数,观察业务服务也需要当前状态,而不是只相信代理之前查过。
练习:制定一次十五分钟身份服务故障演练。写出哪些读写停止、用户看到什么、Agent 会不会重试、任务是否入队、恢复后旧审批怎样处理。再模拟业务成功但审计输送失败,决定是事务回滚、outbox 重送还是独立告警。本书没有实现分布式恢复,所以这些是生产迁移必须补齐的检查。
重试预算应由整条调用链共享
如果 Agent 重试三次,代理重试三次,业务客户端又重试三次,一次用户动作可能产生二十七次下游尝试。即使审批防止重复副作用,这些尝试仍会放大负载和告警。应使用明确的整体时间预算、有限退避和稳定业务引用,让每一层知道前面已经尝试过什么。
认证失败、权限不足和审批绑定错误通常不应自动重试,因为相同输入不会恢复。依赖超时可能重试,但对写入先查询幂等结果。流量达到上限时,应让 Agent 得到清晰的暂缓状态,避免它误以为参数有问题而生成另一笔操作。
故障恢复也要防止惊群。身份服务恢复后,大量执行器同时刷新 JWKS、重新签发凭据并领取旧任务,可能让系统再次失败。缓存刷新合并、任务速率限制与高风险人工复核可以帮助平稳恢复。它们属于运营设计,不能靠延长令牌寿命一次性解决。