错误预算把可靠性约定变成能够参与排期的依据。产品与运维先确定目标,再度量失败是否超出允许范围,并提前约定预算耗尽后如何调整工作。这个数字支持共同决策,却不能预测明天的部署会不会引发事故。发布会引入风险,真正消耗已度量预算的是观察到的不良事件;不能把每次发布机械地扣成固定额度,也不能把余额充足当成变更必然安全的证明。
先定义这本账如何计算
对于基于请求的目标,允许失败数等于符合统计条件的请求总数乘以一减目标值。例如,三百万次请求的目标为百分之九十九点九,便允许三千次不良请求。接入发布规则之前,需要写清请求范围、成功条件、测量位置和观察窗口。按流量加权的失败额度,不会自动变成一个固定的宕机分钟数;高峰和低谷的同样一分钟可能影响完全不同数量的请求。
滚动三十天的统计会持续移除旧记录并加入新记录。流量变化时,分母与允许失败数都可能变化,即使目标百分比没有改动。自然月统计则在约定的日历边界重新开始,两种窗口回答的问题不同。事故处理中临时换窗口,可能让看似精确的余额失去可追溯含义。因此应将计算定义与策略一起版本化,并在余额旁显示实际窗口、请求范围和统计截止时间。
在争论之前约定如何响应
一种示例策略是:预算超支时暂停非必要功能发布,把资源转向可靠性工作,同时允许紧急安全修复和事故修复经过审核后执行。需要明确决策负责人、例外所需材料、记录方式以及恢复普通发布审查的条件。谷歌公开的预算策略针对书中的示例服务,不能不加判断地把所有阈值和升级处理规则照搬到另一项业务。
预算余额也不能独立决定是否具备发布条件。事故仍未结束、监控覆盖不足、回归失败或缺少可靠回退方案,都可能继续阻止变更。反过来,修复当前事故的必要变更,不应只因为该事故已经花光预算而被机械阻挡。把判断及依据写下来,才能让规则服务于用户影响和工程取舍,而不是让仪表盘变红的团队承担未经分析的责任。
坏掉的样子
一个大事故离开滚动窗口后,预算可能突然恢复。计算本身没有错,但这不能说明根因已经消除。悄悄更改排除条件,把无法采集的样本填成成功,或者直接平均不同流量服务的成功百分比,同样会制造虚假的盈余。看到余额改善时,应先检查测量覆盖与定义是否变化,再判断服务状态是否确实改善,尤其不能把未知数据解释为零失败。
短窗口燃烧率提供另一种观察角度:把实际不良事件比例除以目标允许的不良比例,可以看出近期消耗速度是否异常。高燃烧率能够在长窗口预算耗尽前发出警告,但由它推算剩余时间,需要假设流量与错误行为继续保持可比,不能当成确定的倒计时。应结合长窗口余额、短窗口变化和低流量条件设置可执行的告警,而不是每出现一次失败就叫醒值班人员。
快速验证
把下面代码保存为 budget-policy.py,使用 Python 3 执行。数据是人为构造的每日请求统计,用来核对滚动窗口及一个刻意保持简单的策略。程序不会连接发布系统,也没有对任何真实变更的事故概率作出估计;运行输出只证明给定输入下的计算和判断分支。
from fractions import Fraction
target = Fraction(999, 1000)
# Every synthetic daily bin has 100,000 eligible requests.
days = [(100000, 1300)] + [(100000, 70)] * 29
def account(window):
eligible = sum(total for total, _ in window)
bad = sum(errors for _, errors in window)
allowed = eligible * (1 - target)
return bad, allowed, allowed - bad
def feature_policy(remaining, incident_open):
return "hold" if remaining < 0 or incident_open else "eligible for review"
before = account(days)
after = account(days[1:] + [(100000, 50)])
assert before == (3330, 3000, -330)
assert after == (2080, 3000, 920)
assert feature_policy(before[2], False) == "hold"
assert feature_policy(after[2], True) == "hold"
assert feature_policy(after[2], False) == "eligible for review"
for label, (bad, allowed, remaining) in [("before", before), ("after", after)]:
print(f"{label}: bad={bad}, allowed={float(allowed):.0f}, remaining={float(remaining):.0f}, spent={float(bad / allowed):.2%}")
print("after rotation, incident still open:", feature_policy(after[2], True))
print("after rotation, incident closed:", feature_policy(after[2], False))
第一个窗口包含三千三百三十次不良请求,允许三千次,预算使用率为百分之一百一十一,余额为负三百三十。移除最旧一天、加入新一天后,不良请求降为二千零八十次,使用率约百分之六十九点三三,剩余九百二十次。示例特意让两个窗口都包含三百万次符合条件的请求,以便单独观察旧事故离开窗口的影响;真实系统的分母不一定保持不变。
即使新余额已经为正,只要事故仍处于打开状态,策略仍输出 hold。最后一个场景才变成 eligible for review,表示可以进入发布审查,并没有直接批准部署。这样可以把可验证的算术与使用它的运维判断分开:在采取行动前,同时检查统计定义、覆盖程度、事故证据和变更验证,避免只凭一个转绿的数字宣布恢复正常。
延伸阅读
来源
- Google SRE Workbook: Example Error Budget Policy,发布限制、例外与责任约定,已核验 2026-09-13。
- Google SRE: Service Level Objectives,度量边界与可靠性目标,已核验 2026-09-13。
- Google SRE Workbook: Alerting on SLOs,燃烧率与告警窗口,已核验 2026-09-13。