概念定义
99.9% 的 30 天 SLO 约给 43 分钟容许停机。告警要回答的不是“错误率高吗”,而是“预算烧得是否快到撑不满周期”。燃烧率回答这个。
工作机制
计算观测坏事件率对预算率的比值:14.4 倍燃烧会在两天烧光 30 天预算——呼页;1 倍燃烧恰好按计划消耗——正常。多窗口把短窗的敏感与长窗的确认配对:两者同时超阈才是持续快燃,仅短窗超阈只是毛刺。
使用场景
在定义了诚实 SLI/SLO 与真实错误预算之后采纳——在那之前,燃烧告警度量的是无人同意过的预算。配套政策:预算烧尽则冻结功能开发转向可靠性,否则数字只是装饰。
常见误区
燃烧率不是花哨的错误率:同样的 0.1%,在 99.99% 目标下是紧急事件,在 99% 下是噪声。单窗口要在误报与迟钝之间二选一,配对窗口才能验证持续燃烧。窗口算术必须匹配预算周期——阈值是推导出来的不是拍的;SLI 选择才是主导:度量服务器在线而用户在挨失败,烧的是一笔看不见的预算。分母要按请求归一而不是按分钟,否则低流量深夜会伪造燃烧。
代码示例
from dataclasses import dataclass, field
import random
@dataclass
class Span:
name: str
duration_ms: float
error: bool = False
@dataclass
class Policy:
slow_ms: float = 500.0
keep_errors: bool = True,
fallback_rate: float = 0.01
def decide(spans: list['Span'], policy: Policy, rng: random.Random | None = None) -> tuple[bool, str]:
rng = rng or random.Random()
if not spans:
return False, 'empty'
if policy.keep_errors and any(s.error for s in spans):
return True, 'error'
slowest = max(s.duration_ms for s in spans)
if slowest >= policy.slow_ms:
return True, 'slow'
if rng.random() < policy.fallback_rate:
return True, 'sampled'
return False, 'dropped'
package main
import (
"context"
"fmt"
"time"
)
type Step struct {
Name string
Cost time.Duration
}
func runSteps(ctx context.Context, steps []Step) error {
for _, step := range steps {
if err := ctx.Err(); err != nil {
return fmt.Errorf("before step %q: %w", step.Name, err)
}
if deadline, ok := ctx.Deadline(); ok {
remaining := time.Until(deadline)
if remaining < step.Cost {
return fmt.Errorf(
"step %q needs %s but only %s remains: %w",
step.Name,
step.Cost,
remaining.Round(time.Millisecond),
context.DeadlineExceeded,
)
}
}
timer := time.NewTimer(step.Cost)
select {
case <-ctx.Done():
timer.Stop()
return fmt.Errorf("step %q interrupted: %w", step.Name, ctx.Err())
case <-timer.C:
fmt.Printf("completed %q in %s\n", step.Name, step.Cost)
}
}
return nil
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 80*time.Millisecond)
defer cancel()
steps := []Step{
{Name: "load-config", Cost: 20 * time.Millisecond},
{Name: "fetch-profile", Cost: 25 * time.Millisecond},
{Name: "render-report", Cost: 60 * time.Millisecond},
}
if err := runSteps(ctx, steps); err != nil {
fmt.Println("stopped:", err)
}
}