跳转到主要内容

目标身份与一致的健康检查响应

状态: 原始契约已随 v1.5.0 发布;2026-11-09 健康修订已纳入 v1.6.0。
决策/最后核验日期: 原始决策为 2026-10-23;健康修订与本地验证为 2026-11-09。
范围: v1.5.0,源码 1891254(修复基线 9b378cb),涵盖连接串与数据库身份、由元数据驱动的采集器选择、HTTP 健康状态快照、YAML 时间配置及指标路由校验。
交付: 源码已提交并推送,v1.5.0 标签指向 18912540fe5edce0ed0ee81fac0d989738ecaa87;CI 与正式发布工作流通过,GitHub Release、RPM/DEB/压缩包和 Docker 镜像已发布。未据此声称生产部署或生产负载验收完成。

目标身份

URL 语法或查询参数转义错误必须阻止启动,且不能打印原始连接串或含有连接串的解析错误。共享 URL 解析器还须拒绝解码后为空、包含 Unicode 空白或 = 的查询参数名,防止这些名称进入驱动解析或绕过密码遮盖。诊断使用固定文本,不包含连接串或错误参数名。静默丢弃错误参数可能连显式 TLS 设置一起丢掉。省略 sslmode 时仍默认使用 disable;有效的显式设置保持不变。

所支持 URL 形式中的数据库名交给现有 lib/pq 解析器确定,涵盖路径、显式查询参数和数据库别名。Exporter 自行模拟它们的优先级容易把 PgBouncer 当成 PostgreSQL,或错误识别自动发现的数据库。发现新库后,替换连接串时移除 dbname、database 两个查询参数,将库名写入唯一且正确转义的路径,保留其他参数及库名的原始字符。Service 文件中的目标解析,以及仅依赖连接默认值推导的 PgBouncer 身份,不在本次修复范围内。

选用 PG_EXPORTER_URL_FILE 时,文件内容为空或仅含空白必须阻止启动。此处检查的是保存连接 URL 的文件,不是 TLS 私钥文件。

采集器选择

元数据预检成功后,用户名、Schema 集合或扩展集合变化也应使查询计划失效,沿用版本、数据库名和恢复状态变化时的处理方式。比较集合而非返回数组顺序。下一轮采集通过既有计划流程重新选择采集器、重建缓存并重置查询统计;元数据不变则保留现有计划和缓存。安装扩展后不再需要手工重载,也不增加后台线程或额外元数据查询。

健康状态快照

只发布一个原子状态枚举:unknown、down、starting、primary、replica。Up() 和 Recovery() 从这个值推导。每个健康检查 HTTP 请求只读取一次,并同时推导状态码和响应正文。三个分别原子更新的字段并不是一份原子快照;重复读取也可能混合不同探测轮次的状态。

v1.5.0 沿用既有路由、HTTP 状态码、后台探测间隔和采集结果更新健康状态的方式。此决定保证单次响应内部一致,并不把缓存状态变成数据库实时可用性的同步证明。完整状态可存入一个原子整数,无需增加互斥锁。

健康探测独立性(v1.6.0,2026-11-09)

本次修订基于 main 的 9d9804f,解决 #112 与 #113,纳入 v1.6.0 的 6bee809,不属于 v1.5.0。源码已提交并推送;CI 与草稿制品是独立发布关卡,草稿不等于公开发布或生产部署。

v1.6.0 之前,主目标的采集锁会阻塞后台探测;子库采集完成后,旧的主目标观测又可能覆盖较新的探测结果。数据库句柄初始化改用独立的短锁,探测沿用现有连接池,不再等待采集锁。PostgreSQL 主目标池保持三个连接,发现连接保持一个;PgBouncer 主目标池由一个增为两个,容纳串行采集与串行探测各一条连接,避免仅因 exporter 自身占用连接而报告 down。代价是最多增加一个 PgBouncer 连接。探测仍传递连接超时 context,但握手等网络阶段需要合适的 DSN connect_timeout,不承诺硬终止时限。

构造阶段的初始状态之后,HTTP 健康状态只由串行的健康探测更新。采集继续更新 pg_up 与 pg_in_recovery,但不再写入 HTTP 健康快照。路由、状态码与一秒探测间隔不变。健康状态表示最近一次完成的连通性与角色探测;fatal 采集器失败通过采集指标反映,不再成为另一处健康状态写入来源。

这个方案避免引入两个写入来源之间的时间戳仲裁和第二个连接池,不新增健康参数、工作线程,也不在 HTTP 健康处理器中同步执行 SQL。回归测试须覆盖阻塞采集器期间的探测、新角色或失败状态不被子库完成事件覆盖,以及初始化、显式与后台探测并发、停止过程的 race 检查。

指标路由

启动校验必须确认对配置路径的请求选中了指标处理器本身。仅比较匹配的路由字符串并不充分:mux 的重定向也可能返回相同字符串,却没有进入指标处理器。校验时比较返回的处理器与注册的指标处理器,拒绝落入重定向或其他端点的路径,保留在当前 mux 下确实可达的转义路径,无须一概禁止百分号转义。

验证与边界

回归测试覆盖显式数据库身份、特殊字符和冲突参数、错误 URL、Schema/扩展/用户资格变化、元数据顺序不变、并发健康状态切换,以及两种 HTTP mux 下的可达转义路径和重定向。新增用例在修复前失败、修复后通过。

v1.5.0 发布前验收补充了以下验证:

  • 错误的 TLS 转义在连接前被拒绝;verify-full 对不提供 TLS 的服务器连接失败。日志不包含连接密码,空 URL 文件被拒绝。
  • 含冲突参数和特殊字符的自动发现连接返回正确数据库指标;增加与删除数据库、Schema、hstore 后,采集计划按元数据更新。稳定集合保留缓存。
  • 非法单文件重载保留旧定义和缓存;断连后健康检查恢复;物理备库晋升后角色与标签选择更新;并发抓取、健康与重载请求的状态码和正文保持一致。
  • Go race 测试、两种 HTTP mux、go vet、Staticcheck、模块与生成配置一致性、六平台构建、RPM/DEB 安装和 Docker 构建通过。针对有效 UTF-8 URL 身份及实际 HTTP URL 构造的短时模糊测试完成约 60.4 万次输入。
  • 以非超级用户重新验证 PostgreSQL 9.1–9.6、10–18、19 RC1 的默认主库采集,以及 PgBouncer 1.26.1。PG10–13 使用已记录的订阅 OID 授权。此范围不代表穷尽扩展、瞬态故障和生产负载。

证书链/主机名/双向 TLS 的全部组合、SQLSTATE 57P03 的真实启动瞬态和生产负载不在本轮完整验证范围内。Docker 的证书验证仍需按手册挂载 CA。目录加载可跳过错误文件;空 YAML 是合法的零采集器配置;SIGTERM 不等待活跃 HTTP 请求。上述既有行为没有改变。

配套配置校验在转换为 time.Duration 前拒绝 YAML 主查询 TTL、查询 timeout 和谓词 TTL 中非有限或正向溢出的秒数,保留零值、默认值及有限负超时的既有语义。本次不迁移驱动,不整合可选组件,不修改指标名称,也不做通用重构。

本次修订通过了确定性的 race 测试,覆盖阻塞采集器、较新的 primary/replica/down 观测、连接池耗尽、数据库初始化并发,以及采集期间停止健康循环。这些测试不代表真实主备切换或生产负载验收。

PgBouncer 1.26.1 的统计响应被人为延迟时,版本探测仍成功,未误报 down;服务停止与重启也被正确识别。这不代表 PostgreSQL 故障切换或生产负载验收。