14.7 实战:评审三个候选扩展
本节把前六节压成一个 release proposal:
three problems
-> three decisions
-> package/control evidence
-> privilege boundaries
-> member/index/query evidence
-> one object upgrade
-> dump and portable exit
-> exact reset and rebuild它不是扩展性能评测,也不是生产安装脚本。实验的价值是证明评审结构能够 运行、失败、复位和重复。
环境与破坏边界
正式 evidence 来自 Homebrew PostgreSQL 18.4 直连服务,未在 Pigsty L1 运行。
task.sh all会精确删除并重建带本章 marker 的shop_ch14、pg_trgm和vector,只适合本地/开发数据库。生产变更不得运行这一 “删后重建”入口。
14.7.1 一个接受、一个试点、一个拒绝
问题 A:有界单字段拼写容错
候选 pg_trgm。
问题边界:
field: one title text column
query: typo-tolerant lookup
fixture typo: "PostgreSQL extenson"
result limit: 3
not in scope: language segmentation, semantic ranking, document search原生替代:
- 精确 B-tree;
- 规范化前缀搜索;
- PostgreSQL FTS;
- 应用侧拼写纠正。
采用理由:
- PostgreSQL contrib 扩展;
- 当前 control 为 trusted/relocatable;
- 不改变
title text类型; - GIN
gin_trgm_ops可由目录与计划验证; - 可以先切回精确/FTS,再删 GIN 和扩展;
- 1.3 → 1.6 更新路径与行为回归可重复。
结论:
accept pg_trgm
only for bounded fuzzy matching不是批准它替代第 15 章的全部检索设计。
问题 B:语义近邻检索
候选 vector(项目/包别名常为 pgvector)。
本地 PoC:
type: vector(3)
distance: L2
index: HNSW vector_l2_ops
query vector: [1,0,0]
top ids: 1,2,5已证明:
- control/安装 SQL/动态库存在并有 hash;
trusted=false,非超级用户创建以42501失败;- 管理员能在目标 schema 创建 0.8.4;
- 表、类型、HNSW opclass/index 有目录证据;
- 应用角色可查询但没有表写权限;
embedding::text可导出五行;- 全库 dump 用
CREATE EXTENSION vector表示成员。
未证明:
- 真实 embedding model、dimension 与 normalization;
- 真实语料 relevance/recall;
- 过滤组合下 ANN 行为;
- 索引 build、内存、磁盘、WAL 与更新成本;
- 并发 P95/P99;
- 物理备库/failover;
- clean restore;
- PostgreSQL major upgrade;
- 在 Pigsty L1 所有节点的包一致性。
结论:
pilot vector 0.8.4
bounded to an isolated workload and evidence plan任何真实业务接入前必须补上第 15 章的质量语料与 L1 生命周期证据。
问题 C:分布式分片
候选 Citus。
当前事实:
no measured single-cluster capacity breach
no shard-key contract
no co-location model
no cross-shard transaction budget
no rebalance/failure test本地 Homebrew server 的 pg_available_extensions 也没有 Citus,但这不是
拒绝的主要理由。Pigsty 当前扩展目录提供 Citus 14.0.0;平台有包仍不能替
架构证明问题。
当前替代:
- 修正查询与索引;
- 生命周期/归档治理;
- PostgreSQL declarative partitioning;
- 垂直扩容;
- 读副本或分析副本;
- 到第 17 章测量单集群容量边界。
重新打开 ADR 的条件:
measured capacity/SLO crossover
+ stable distribution key
+ transaction and uniqueness model
+ rebalance/failure/backup plan结论:
reject Citus now拒绝的是当前采用时机,不是产品评价。
把结论写进数据库
setup.sql 建立:
CREATE TABLE shop_ch14.extension_review (
candidate text PRIMARY KEY,
extension_name text NOT NULL,
package_alias text NOT NULL,
decision text NOT NULL
CHECK (decision IN ('accept', 'pilot', 'reject')),
problem text NOT NULL,
success_criterion text NOT NULL,
exit_path text NOT NULL,
review_trigger text NOT NULL,
reviewed_on date NOT NULL
);最终必须精确得到:
citus:reject,pg_trgm:accept,vector:pilot把 ADR 行放进实验数据库不是建议生产数据库存文档;它使 fixture checksum 同时覆盖数据与决策,防止测试脚本与文字结论分叉。
最小架构
shop_ch14
├── extension_review
├── candidate_doc
│ ├── title text
│ └── embedding vector(3)
├── pg_trgm 1.3 -> 1.6
│ └── GIN gin_trgm_ops
└── vector 0.8.4
└── HNSW vector_l2_ops所有 schema、扩展与非成员 relation/index 带 marker:
pg36 ch14 extension lifecycle lab; safe to rebuild扩展成员通过 pg_depend.deptype='e' 识别,不要求逐个添加 comment。
14.7.2 在 L1 安装并验证原生对象与平台状态
标题中的 L1 是目标运行形态,不是本地证据伪装。流程分两步:
- 在受控直连 PostgreSQL 完成机制 fixture;
- 把同一合同移植到 Pigsty L1,补齐节点、HA 与恢复证据。
1. 准备 libpq service
[pg36-admin]
host=/path/to/socket-or-host
port=5432
dbname=pg36_shop
user=postgresexport PGSERVICEFILE=/path/to/pg_service.conf
export PGSERVICE=pg36-adminservice 文件权限收窄,不在命令行或 evidence 打印密码。
context guard 要求:
database=pg36_shop
writable primary/direct PostgreSQL
server major=14..18
session superuser=true
can SET ROLE pg36_owner
ch04-v1 model exists
pg36_app is constrained LOGIN
pg_trgm 1.3 and 1.6 support files available
vector 0.8.4 support files available版本不符时脚本拒绝;读者应复制 proposal、更新版本与 golden 后重新评审, 不应删掉 guard。
2. 分阶段入口
./static/labs/ch14/task.sh setup
./static/labs/ch14/task.sh inventory
./static/labs/ch14/task.sh upgrade
./static/labs/ch14/task.sh dump每个会精确重建 fixture,适合单独教学。最终只认:
PG36_EVIDENCE_DIR="$PWD/evidence/ch14" \
./static/labs/ch14/task.sh all3. 先验证支持文件
package-manifest.txt 记录:
pg_config path/version
server major
sharedir/pkglibdir
validation_path=direct-postgresql
pigsty_l1=not-run并对:
pg_trgm.control
pg_trgm--1.3.sql
pg_trgm--1.3--1.4.sql
pg_trgm--1.4--1.5.sql
pg_trgm--1.5--1.6.sql
pg_trgm.dylib/.so
vector.control
vector--0.8.4.sql
vector.dylib/.so生成 SHA-256。
脚本先比较 pg_config major 与 live server major。PATH 指向错误 PG 安装时
立即失败,不会拿另一套支持文件做出“可用”结论。
在 Pigsty L1,这份清单要按所有主备 host 展开,而不是只在 primary 生成。
4. 碰撞保护与 trusted 安装
setup.sql 若发现:
shop_ch14marker/owner 不符;pg_trgm或vector已位于别的 schema;- extension marker、owner 或版本不在允许集合;
- schema 中有未知非 extension relation/routine/type/operator/opclass;
就拒绝重建。
随后:
SET ROLE pg36_owner;
CREATE SCHEMA shop_ch14 AUTHORIZATION pg36_owner;
CREATE EXTENSION pg_trgm
WITH SCHEMA shop_ch14
VERSION '1.3';
RESET ROLE;结果:
pg_trgm_owner=pg36_owner
pg_trgm_version=1.3
vector_installed=false这证明 trusted 规则与数据库 owner 权限,不表示 pg36_owner 是超级用户。
5. 注入预期特权失败
SET ROLE pg36_owner;
CREATE EXTENSION vector
WITH SCHEMA shop_ch14
VERSION '0.8.4';必须:
psql exit=3
SQLSTATE=42501
Must be superuser to create this extension若它意外成功,说明 control/权限环境与 proposal 不同,review 失败,而不是 把差异忽略。
管理员再执行 install-vector.sql:
CREATE EXTENSION vector
WITH SCHEMA shop_ch14
VERSION '0.8.4';并由 owner 建表:
CREATE TABLE shop_ch14.candidate_doc (
doc_id bigint PRIMARY KEY,
title text NOT NULL,
embedding shop_ch14.vector(3) NOT NULL
);6. 建立两个可验证索引
CREATE INDEX candidate_doc_title_trgm_idx
ON shop_ch14.candidate_doc
USING gin (title shop_ch14.gin_trgm_ops);
CREATE INDEX candidate_doc_embedding_hnsw_idx
ON shop_ch14.candidate_doc
USING hnsw (embedding shop_ch14.vector_l2_ops)
WITH (m = 8, ef_construction = 32);目录验收:
| index | AM | opclass | valid/ready/live |
|---|---|---|---|
candidate_doc_title_trgm_idx | gin | shop_ch14.gin_trgm_ops | true/true/true |
candidate_doc_embedding_hnsw_idx | hnsw | shop_ch14.vector_l2_ops | true/true/true |
CREATE INDEX 成功还不够;检查 pg_index、pg_am 与 pg_opclass,防止
名字相同但实现漂移。
7. 采集扩展与成员目录
name
object version
owner
nominal schema
relocatable
superuser/trusted/requires
member count
marker更新前 PostgreSQL 18.4:
pg_trgm 1.3 owner=pg36_owner trusted=t members=37
vector 0.8.4 owner=postgres trusted=f members=237member-catalog.sql 再按 catalog 分解:
pg_trgm:
pg_opclass, pg_operator, pg_opfamily, pg_proc, pg_type
vector:
pg_am, pg_cast, pg_opclass, pg_operator,
pg_opfamily, pg_proc, pg_type更新后 pg_trgm 成员为 47。数量只冻结本次 PG18.4 build;其他 major 可有
条件差异。
8. 权限矩阵
pg36_app:
USAGE shop_ch14 = true
SELECT review/docs = true
INSERT/UPDATE/DELETE = false
extension owner = falseapp-query.sql 成功使用函数、操作符与类型;随后:
ALTER EXTENSION pg_trgm UPDATE TO '1.6';必须:
SQLSTATE 42501
must be owner of extension pg_trgm应用使用能力与扩展管理权被分离。
9. 行为 baseline
模糊检索:
SELECT
doc_id,
round(
shop_ch14.similarity(
title,
'PostgreSQL extenson'
)::numeric,
6
) AS score
FROM shop_ch14.candidate_doc
ORDER BY score DESC, doc_id
LIMIT 3;结果:
1 0.620690
5 0.305556
2 0.205128向量检索:
SELECT
doc_id,
round(
(
embedding
OPERATOR(shop_ch14.<->)
'[1,0,0]'::shop_ch14.vector(3)
)::numeric,
6
) AS distance
FROM shop_ch14.candidate_doc
ORDER BY
embedding
OPERATOR(shop_ch14.<->)
'[1,0,0]'::shop_ch14.vector(3),
doc_id
LIMIT 3;结果:
1 0.000000
2 0.141421
5 0.28284310. 索引计划
五行表优化器自然可能选择 seq scan。实验:
SET enable_seqscan = off;只用于证明索引路径存在,不用于性能结论。
trigram:
Bitmap Index Scan on candidate_doc_title_trgm_idxvector:
Index Scan using candidate_doc_embedding_hnsw_idx生产验收应恢复默认 planner 配置,用真实数据比较:
EXPLAIN (ANALYZE, BUFFERS, WAL, SETTINGS)并检查结果质量;不能用 enable_seqscan=off 证明索引值得使用。
11. 更新 1.3 → 1.6
update-paths.sql 先证明:
1.3--1.4--1.5--1.6upgrade.sql 要求 source 精确为 1.3:
SET ROLE pg36_owner;
ALTER EXTENSION pg_trgm UPDATE TO '1.6';
RESET ROLE;若 source 已经变化,返回本章自定义 P3640,不猜迁移路径。
更新后重新采集:
- available version installed flag;
- extension/member catalog;
- index validity/opclass;
- ACL;
- 两个查询;
- 两个计划。
除 pg_trgm_version 与成员清单外,行为 golden 不变。
12. dump 的正反例
全库:
pg_dump \
--schema-only \
--no-owner \
--no-privileges \
--dbname='service=pg36-admin' \
> database-schema.sql必须包含:
CREATE EXTENSION ... pg_trgm
CREATE EXTENSION ... vector且不展开 shop_ch14 扩展成员函数/类型。
选择性 schema:
pg_dump \
--schema-only \
--schema=shop_ch14 \
--no-owner \
--no-privileges \
--dbname='service=pg36-admin' \
> selected-schema.sql它包含应用表和索引,却没有 CREATE EXTENSION。review 把这个“不完整依赖”
作为预期证据,提醒恢复 runbook 先供应并创建扩展。
13. portable exit
doc_id,title,embedding_text
1,PostgreSQL extension guide,"[1,0,0]"
...review 要求:
- header 精确;
- 五个主键按 1..5;
- 每个 embedding 是 bracketed text。
生产退出还需导入目标、语义比对和删依赖;本章只证明可携带 representation。
14. 最终不变量
review_rows=3
document_rows=5
pg_trgm_version=1.6
vector_version=0.8.4
pg_trgm_members=47
vector_members=237
trigram_top_ids=1,5,2
vector_top_ids=1,2,5
business_checksum=5398634500fe53ba1fb683e9a2c6e745checksum 包含:
- 三行 ADR 内容;
- 五行文档/向量文本;
- extension name/version/schema/relocatable。
它不包含管理员用户名,避免换一个受控超级用户就改变业务 golden。
15. 精确复位
手工入口:
PG36_RESET_TOKEN=RESET_CH14_EXTENSION_LAB \
PG36_RESET_TARGET='pg36_shop/shop_ch14/pg_trgm+vector' \
./static/labs/ch14/task.sh resetreset.sql 在删除前验证:
- database、writable instance、server 与角色;
- token/target;
- schema marker/owner;
- extension name/version/schema/owner/marker;
- 非成员 relation/type/routine/operator/opclass 白名单;
- 没有
pg36-ch14-*活跃 worker。
删除顺序:
DROP TABLE shop_ch14.candidate_doc;
DROP TABLE shop_ch14.extension_review;
DROP EXTENSION vector;
DROP EXTENSION pg_trgm;
DROP SCHEMA shop_ch14;没有 CASCADE。若仍有未知业务依赖,DROP EXTENSION 失败并暴露它。
all 还注入:
wrong token -> P3650
wrong target -> P3651
active worker -> P3653拒绝后才精确复位,再完整重建第二遍。最终环境保留通过验收的 fixture。
16. 移植到 Pigsty L1
先审查 Pigsty 声明片段:
pg_extensions:
- pgvector
pg_databases:
- name: pg36_shop
schemas:
- { name: app_ext, owner: pg36_owner }
extensions:
- { name: vector, schema: app_ext }stock Pigsty 默认把 pg_trgm 启用在 public,无需与本地 shop_ch14 布局
完全相同。
L1 执行顺序:
review inventory diff
-> verify repo/alias availability for exact PG/OS/arch
-> install package on all nodes
-> hash control/SQL/library on all nodes
-> verify no preload requirement for these exact versions
-> create extension through reviewed database migration
-> query catalog/member/index/ACL
-> run behavior and negative tests
-> test replica query and controlled switchover
-> clean restore to fresh L1/clone
-> attach evidence to a new proposalL1 不应强行复用本地 proposal checksum,因为:
- schema 布局可能不同;
- package build/OS 不同;
- owner 名或 default extension state 不同;
- 应补主备/restore evidence。
复制 ADR 结构,生成属于目标 L1 的新 baseline。
14.7.3 产出供 ch15–ch17 复用的 ADR 模板
交付包
本章交付不是一张“推荐扩展”表,而是:
candidate-review.md
extension-adr-template.md
baseline-v1.2-proposal.json
pigsty-declaration.example.yml
lab-contract.md
SQL/Bash/Python executable evidence chaincandidate-review.md 记录三项结论; extension-adr-template.md 提供十段结构:
- 决策元数据;
- 问题与边界;
- 候选与原生替代;
- 成功与停止标准;
- 供应链与运行条件;
- 数据与兼容性;
- 安全与治理;
- 最小 PoC;
- 退出路径;
- 结论。
第 15 章:检索候选如何复用
继承通用字段,再增加:
language/tokenizer/dictionary/config identity
query grammar
ranking formula
golden relevance corpus
GIN/GiST/RUM/other index behavior
write/pending-list/bloat cost
adversarial query boundarypg_trgm 的 accept 不能自动批准所有字段。每个字段/查询形态仍需索引与
相关性 ADR。
vector 的 pilot 进入第 15 章后,要补:
embedding model/version
dimension
normalization
distance metric
exact-vs-ANN control
recall@k
filter selectivity
HNSW/IVFFlat build/update/maintenance第 16 章:时空候选如何复用
增加:
SRID
coordinate order and units
geometry/geography choice
validity and precision
spatial predicate semantics
temporal interval/time zone
GiST/SP-GiST/BRIN behavior
WKT/WKB/GeoJSON exportPostGIS 若被采用,自定义类型的 restore/exit 门槛不能因为生态成熟而省略。
第 17 章:分析与分布式候选如何复用
增加:
single-node measured ceiling
shard/distribution key
co-location
global uniqueness/FK
cross-shard transaction
rebalance
node failure
DDL propagation
backup/restore and topology exitCitus 只有在这些字段有证据后才从 reject 重新进入 proposed;“Pigsty 有包” 不是触发批准。
自动审校器检查什么
review.py 不比较终端输出的外观,而比较关系:
manifest proposal identity
package support-file hashes
two exact SQLSTATE 42501 failures
three candidate decisions and availability
before/after extversion
trusted/owner/schema/member relationships
update path
index AM/opclass/validity
least-privilege matrix
query results stable across update
forced index paths present
full dump vs selective dump semantics
portable export shape
final checksum
no-CASCADE reset source关系式 review 比“命令 exit 0”更接近发布验收。
审校结果
正式两轮输出:
status=ok
decision=pg_trgm:accept/vector:pilot/citus:reject
boundary=package+control+database-object
failure=42501-owner+42501-superuser
upgrade=pg_trgm:1.3->1.6-behavior-stable
index=gin+hnsw
dump=create-extension+selective-dependency-warning
exit=portable-text-export
pigsty_l1=not-run
release=1.2-proposal
release_candidate_checksum=6a4b74baec5f522eb098c868f1d4f1b441bf5b5f6708411588af0a8793f7f573第一轮通过后,脚本证明复位 guard,再删除并重建,第二轮得到同一关系和 proposal identity。
哪些结论可以带走
可以:
- 扩展要同时管理供应、进程和数据库三层;
- trusted/untrusted 与 owner 边界必须负面测试;
- package version 与 extversion 分开;
- update 前后比较 catalog、行为和计划;
- dump 不携带支持文件,选择性 dump 不保证依赖闭包;
- 自定义类型采用前先定义交换格式;
- Pigsty 声明后回到原生证据;
- ADR 允许 accept/pilot/reject,而不是所有候选二选一。
不能:
pg_trgm对所有搜索都足够;- pgvector 0.8.4 已通过生产验证;
- Citus 不值得使用;
- PostgreSQL 14–17 会得到相同成员数;
- Homebrew 文件 hash 能代表 Pigsty 包;
- 五行查询速度能代表生产性能。
能清楚说出“实验没有证明什么”,是扩展治理成熟度的一部分。
本章最终检查
完成本章后,面对新扩展先写:
problem
native alternative
success/stop
data/exit
lifecycle
maintenance/license
privilege/supply
version scope
review triggers然后才写:
CREATE EXTENSION ...顺序反过来,数据库很快会积累一组谁也不敢升级、恢复或删除的隐性平台。
上一节:建立可复用扩展 ADR · 返回本章目录 · 下一章:见微知著:全文、模糊与向量检索 · 查看全书目录 · 查看索引中心