周期执行的记录规则主题封面,展示观察、判断与行动三个步骤。

记录规则保存按周期计算的查询结果

记录规则在指定评估时刻执行 PromQL 表达式,把结果作为一个新指标保存。反复刷新的面板可以读取这个指标,减少重复计算。但简单把它称为缓存,会遮住一个重要区别:记录序列有自己的样本历史、写入节奏和标签集合,并不是任意查询请求的透明缓存。使用者必须知道读到的是哪次预计算,以及这个结果保留了哪些信息。 三个时钟共同影响页面上的值 抓取决定原始观测何时进入存储,规则评估决定表达式何时生成记录样本,面板则决定何时以及以什么步长查询两者。把页面刷新得更快,不会让记录规则提前计算。因此,在两次规则评估之间读取记录指标,可能得到比同一时刻直接执行原表达式更旧的值。这是不同采样节奏的正常结果,不能仅凭两条曲线不同便断定计算错误。 规则组可以指定周期;未单独指定时,采用全局规则评估周期。同组规则按顺序执行,并使用相同的评估时间戳,所以后面的规则可以使用前面规则刚生成的结果。独立规则组即使写了相同的周期,也不能因此推导存在同样的先后顺序。依赖关系需要明确设计,不能依靠某次运行中碰巧出现的执行顺序。 预计算也改变了保留的数据 聚合表达式决定输出保留哪些维度。如果规则把 instance 求和消掉,面板之后就无法仅从这条记录序列恢复实例分布。仍需实例级排查时,应保留原始指标,并围绕已知消费者选择记录维度。每个剩余标签组合又是一条需要存储的新序列,因此把所有可能的聚合都做成规则,可能显著增加写入量和存储量,并不总是节省资源。 修改表达式会影响后续评估。正常重载规则不会用新定义重新计算全部旧样本,也不会自动填补规则创建之前的历史。在相同指标名下改变含义,可能让同一张图同时包含两套不兼容口径。应记录定义和时间窗口,在消费者要求历史可比时安排明确迁移,不能把正常重载解释为历史数据已经整体更新。 坏掉的样子 面板看起来停住,但原始样本仍在进入数据库时,应对照记录序列最近样本、规则组最近评估、执行耗时与错误。如果上一轮还没结束,下一次计划评估到来时会被跳过,直到当前轮完成或超时。把昂贵查询变成记录规则,只是把计算移入调度器,没有让计算成本消失。执行耗时长期接近周期时,新鲜度会比配置值更差。 查询变快也可能是因为聚合后回答的问题更窄。庆祝速度提升前,应确认被移除的维度确实不再需要。简单表达式通常不值得只为性能增加记录规则。应先测量重复查询成本和复用频率,再把新增序列、周期执行以及新鲜度延迟一起纳入判断。面板数量很多,也不等于每个表达式都必须预先保存。 快速验证 保存程序为 recording-cadence.py,执行 python3 recording-cadence.py。环境需要 Docker 与公开的 Prometheus 3.5.1 镜像,镜像缺失时可以由 Docker 获取。真实 promtool 引擎接收每分钟一份的队列值,而单元测试评估步长和规则组周期均设为两分钟,明确把原始观测与预计算节奏分开。测试数据时间不等于实际等待两分钟。 import subprocess import uuid # Requires Docker and the public Prometheus 3.5.1 image. tests = r"""evaluation_interval: 2m rule_files: - /tmp/rules.yml tests: - interval: 1m input_series: - series: "queue_depth{queue=\"orders\"}" values: "1 4 9 16" promql_expr_test: - expr: "sum(queue_depth)" eval_time: 1m exp_samples: - labels: "{}" value: 4 - expr: "queue:depth:sum" eval_time: 1m exp_samples: - labels: "{__name__=\"queue:depth:sum\"}" value: 1 - expr: "queue:depth:sum" eval_time: 2m exp_samples: - labels: "{__name__=\"queue:depth:sum\"}" value: 9 """ rules = r"""groups: - name: slow-recording interval: 2m rules: - record: queue:depth:sum expr: sum(queue_depth) """ script = "set -eu\ncat > /tmp/tests.yml <<'TEST_DATA'\n" + tests + "\nTEST_DATA\n" if rules: script += "cat > /tmp/rules.yml <<'RULE_DATA'\n" + rules + "\nRULE_DATA\n" script += "exec /bin/promtool test rules /tmp/tests.yml\n" name = "xbcatm-prom-unit-" + uuid.uuid4().hex[:12] try: result = subprocess.run( ["docker", "run", "--rm", "--name", name, "--network", "none", "-i", "--entrypoint", "/bin/sh", "prom/prometheus:v3.5.1", "-s"], input=script, text=True, capture_output=True, timeout=45, ) finally: subprocess.run(["docker", "rm", "-f", name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) print(result.stdout, end="") if result.returncode: print(result.stderr, end="") raise SystemExit(result.returncode) 预期输出为 SUCCESS。第一分钟原始求和结果是四,但记录值仍为第零分钟的一;第二分钟记录序列更新为九。这里故意选择很便宜的表达式,以隔离周期差异,不是在建议为每个队列仪表值建立记录规则,也没有测量真实调度器在负载下的性能。输入时间线和断言直接说明为何同一时刻两个查询可以返回不同结果。 ...

一月 26, 2028 · 1 分钟 · XB Field Notes