14.6 建立可复用扩展 ADR
ADR(Architecture Decision Record)不是会议纪要,也不是给既定选择补理由。 它要让未来的维护者回答:
当时解决什么问题?
在什么版本和假设下?
比较了哪些替代?
什么证据使结论成立?
哪些风险仍然存在?
何时必须复审或退出?本章提供 扩展 ADR 模板。模板不是为了 填满十个标题,而是强迫“价值—运行—退出”形成闭环。
14.6.1 问题、候选、假设与成功标准
标题写问题,不先写扩展
较差:
ADR-023: Adopt pgvector更好:
ADR-023: Semantic nearest-neighbor retrieval for product support corpus第二种标题允许结论是:
- 采用 pgvector;
- 采用另一个 PostgreSQL 扩展;
- 使用外部服务;
- 使用精确检索;
- 现在不做。
候选没有绑架问题。
决策元数据
最小字段:
id: ADR-023
status: proposed
owners:
product: ...
application: ...
database: ...
platform: ...
created_at: ...
review_at: ...
decision_scope:
environment: ...
postgresql: ...
pigsty: ...
os_arch: ...状态只用明确集合:
proposed -> pilot -> accepted
-> rejected
accepted/rejected -> superseded by ADR-N不要把 pilot 当没有期限的半批准。它必须有 traffic/data/environment 边界、
停止标准和截止复审日。
问题陈述
写:
current behavior
observed evidence
business/technical impact
target SLO/quality
in scope
out of scope
do-nothing consequence示例:
当前标题检索的零结果率为 X;
经标注样本确认 Y% 来自一个字符拼写误差;
目标只覆盖英文产品标题,返回上限 20,P95 < 50 ms;
中文分词、语义相关性和全站文档不在范围;
不改变时影响为 Z。每个数字链接到 query snapshot、dashboard 或数据集版本。没有证据的假设 单独列:
assumptions:
- typo distribution remains stable
- title updates are below ...
- one cluster can hold index within ...后续证据推翻假设时自动触发复审。
候选集合
至少包含:
- 不做;
- PostgreSQL 原生机制;
- 候选扩展;
- 外部服务/应用实现(若实际可行)。
对每个候选用同一维度:
| 维度 | 不做 | 原生 | 扩展 A | 外部服务 |
|---|---|---|---|---|
| 正确性/质量 | ||||
| P95/P99 | ||||
| 写入与资源成本 | ||||
| 一致性 | ||||
| HA/恢复 | ||||
| 升级/供应 | ||||
| 权限/安全 | ||||
| 退出成本 | ||||
| 团队技能/owner |
不要把“扩展一行 SQL”与“外部服务完整运维”比较;每格都是完整方案。
成功标准与停止标准成对出现
示例:
success:
relevance_at_20: ">= 0.82"
p95_ms: "<= 50"
p99_ms: "<= 100"
replica_lag_p95_s: "<= 2"
clean_restore: pass
upgrade_rehearsal: pass
stop:
crash_or_corruption: immediate
wrong_result: immediate
p99_ms: "> 200"
wal_multiplier: "> 3"
restore_rto: "> agreed budget"
no_portable_export: reject“比现在快”不是标准;“无明显问题”不是停止线。
分离硬门槛与权重
某些条件不可用加权分抵消:
hard gates:
license approved
target packages available on all nodes
no correctness regression
clean restore passes
exit artifact exists
security boundary accepted
weighted trade-offs:
latency
cost
operator effort
feature richness否则一个非常快但不能恢复的扩展,可能用性能分“赢”过恢复硬门槛。
决策声明
结论写成:
We accept X
for problem Y
in environment/version boundary Z
because evidence A/B/C passed.
We do not approve M/N.
Residual risks are R.
Before production, gates G must pass.
Review is triggered by T.这比“综合考虑后决定采用”更容易审计。
14.6.2 最小 PoC、风险清单与退出路径
PoC 的最小不是样本最少
最小 PoC 是覆盖决策最关键不确定性的最小实验。它不需要模拟所有生产流量, 但不能只跑 happy path。
扩展通用 PoC:
identity
server/package/control/library/object versions
install
intended role success
unauthorized role failure
preload/restart if required
behavior
correctness and representative query
indexes/plans
boundary and adverse data
lifecycle
update path
update before/after regression
physical standby/failover
logical/dump behavior
clean restore
major upgrade clone
exit
portable export
dependency inventory
removal without CASCADE本章本地 PoC 覆盖其中 install、behavior、object update、dump 与文本出口; 没有覆盖 Pigsty L1、备库、clean restore 和 major upgrade,所以 vector 只能是 pilot。
fixture 必须确定性
记录:
- schema/data version;
- 生成方式;
- 随机 seed;
- 数据规模与分布;
- query 参数;
- expected rows/order/error;
- baseline checksum。
本章不是比较浮点的无限精度,而固定六位小数与 top ID:
trigram scores:
0.620690,0.305556,0.205128
vector L2:
0.000000,0.141421,0.282843对于近似索引,大数据 PoC 应定义 recall tolerance,而不是错误要求每次物理 计划与结果顺序完全相同。
正向、负向、破坏性测试分层
read-only:
catalog, availability, plan, dependency
reversible DDL in isolated lab:
CREATE/ALTER/DROP extension
fault injection:
missing library, wrong preload, failover, crash
destructive lifecycle:
restore, major upgrade, exit conversion后两类必须在隔离 clone/L1 进行,有明确 target 与恢复路径。不要为了完成 ADR 在生产主库拔动态库。
本书 lab 的 destructive action 只接管:
pg36_shop/shop_ch14/pg_trgm+vector并要求 marker、token、target 与无活跃 worker。生产迁移另写,不复用 “删掉重建”脚本。
evidence 不是终端滚屏
每轮输出一个不可变目录:
manifest.txt
package-manifest.txt
available-versions.csv
extension-inventory-before/after.csv
member-catalog-before/after.csv
security-catalog.csv
behavior-before/after.csv
plans
failure stdout/stderr/exit
database-schema.sql
selected-schema.sql
portable-export.csv
verify.txt
review.txtmanifest 包含:
captured_at
target/service
server/tool versions
validation path
source file hashes
proposal checksum不要写密码、连接 URI secret 或生产个人数据。
风险清单有 owner 与触发器
| 风险 | 概率/影响 | 缓解 | 观测 | owner | trigger |
|---|---|---|---|---|---|
| package 在新 PG major 缺失 | 提前构建/替代 | release matrix | major roadmap | ||
| C library crash | canary/rollback | crash/restart | error budget | ||
| ANN recall 漂移 | golden corpus | quality job | model/data change | ||
| restore 缺旧脚本 | repo snapshot | restore drill | retention review | ||
| vendor/license 改变 | legal/exit | periodic review | new terms | ||
| node package drift | Pigsty convergence | parity probe | failover/new node |
没有 owner 的风险不是被管理,只是被记录。
退出路径从依赖图开始
SELECT
d.classid::regclass,
d.objid,
d.deptype
FROM pg_depend AS d
JOIN pg_extension AS e
ON e.oid = d.refobjid
WHERE d.refclassid = 'pg_extension'::regclass
AND e.extname = 'vector';还要查引用扩展成员的业务对象。退出步骤必须显式:
export/copy
-> verify
-> dual representation
-> switch reads
-> stop old writes
-> remove business dependencies
-> DROP EXTENSION RESTRICT
-> remove preload/restart
-> remove packages from nodes/repository only when safe最后一步不是第一步。包删除前要考虑历史备份与降级节点。
验证退出,而不是只验证导出
退出 PoC 成功条件:
- 导出行数/主键/checksum 匹配;
- 目标表示能承载单位、坐标系、模型与精度;
- 新查询结果和 SLO 在容差内;
- 旧应用与新 schema 的兼容窗口成立;
- 无残余 view/function/index/table 依赖;
DROP EXTENSION在不使用CASCADE时成功;- 包与 preload 清理后实例重启、备库和恢复通过。
14.6.3 结论的版本范围和复审触发器
ADR 是带范围的结论
错误:
pgvector is approved.可执行:
vector 0.8.4 is approved for a bounded pilot
on upstream PostgreSQL 18.4 / Ubuntu 24.04 amd64 / Pigsty 4.4,
using dimension D and model M,
for corpus C and query shape Q,
under package build B and SLO envelope E.范围外不是自动拒绝,但必须重新验证。
版本块
scope:
postgresql:
implementation: upstream
versions: ["18.4"]
pigsty: ["4.4"]
os_arch: ["ubuntu-24.04-amd64"]
extension:
sql_name: vector
object_version: "0.8.4"
package_build: "..."
topology:
primary: 1
physical_standby: 2
workload:
corpus_version: "..."
model: "..."
dimension: 1536
distance: cosine“支持 PG14–18”可以是项目宣称;ADR 的验证范围可能只完成 17/18。两者分列。
复审触发器
日历触发:
每 6/12 个月
扩展或 PostgreSQL EOL 前
license/support 合同续签前变更触发:
- PostgreSQL major/minor 或内核供应者变化;
- Pigsty release、OS、CPU architecture 变化;
- extension project/package/object version 变化;
- control 的
trusted/preload/requires/relocatable变化; - 数据规模、分布、语言、模型、维度、距离度量变化;
- 新建/替换 standby、灾备或恢复镜像;
- SLO、错误预算或容量越界;
- crash、错误结果、安全通告;
- 维护者、许可证、供应商或仓库变化;
- clean restore/upgrade drill 失败;
- 退出成本估算越过窗口。
不覆盖历史,使用 supersede
决策改变时:
ADR-014 accepted pg_trgm 1.6 in scope X
ADR-028 supersedes ADR-014 for scope Y保留旧 ADR:
- 能解释旧备份/旧服务为何依赖它;
- 能追踪当时证据;
- 能区分错误决策与条件变化;
- 能为事故和退出提供历史。
只在原文底部改“现在改用 Z”,会抹掉因果链。
把复审接入变更门禁
自动检查:
inventory package version changed
pg_extension extversion changed
control/library hash changed
server major changed
model/corpus identity changed若任一发生:
baseline no longer matches
-> block silent promotion
-> open review
-> run scoped test matrix
-> issue new proposal checksum不要让监控自动决定架构,但让它阻止“版本已经漂了,ADR 仍显示已批准”。
供第 15–17 章复用
后续三章沿用同一模板,但各自增加领域项:
第 15 章检索
language/tokenizer/dictionary
ranking and relevance corpus
query grammar and denial-of-service boundary
index pending-list/bloat/update cost第 16 章时空
SRID/coordinate order/unit
geometry validity
spatial selectivity
time zone and temporal range
GIS export format第 17 章分析与分布式
shard key/co-location
cross-shard transaction
rebalance/failure
columnar/OLAP consistency
capacity crossover point它们可以增加字段,不能删掉供应、恢复、权限和退出。
ADR 验收问题
评审者逐句问:
- 问题是否在没有候选扩展名时仍成立?
- 是否有“不做”和原生替代?
- 成功/停止标准是否能机器或人工复验?
- 是否写了 exact server/package/object 版本?
- 是否测过未授权失败?
- 是否覆盖备库、clean restore 和 major upgrade?
- 自定义数据能否导出,退出是否不用
CASCADE? - 残余风险是否有 owner?
- 哪个变化会让结论失效?
- evidence 能否由另一位工程师重跑?
任一回答“以后再补”,ADR 状态最多是 proposed/pilot。
本节结论
好的扩展 ADR 不是“为什么喜欢它”,而是一个可撤销承诺:
under these facts,
for this problem,
this option passes these gates,
with these residual risks,
until one of these triggers changes.它让采用扩展成为受控工程选择,而不是永久信仰。
上一节:用 Pigsty 管理扩展可用性 · 返回本章目录 · 下一节:实战:评审三个候选扩展 · 查看全书目录 · 查看索引中心