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.6 直连服务,未在 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
结论:
拒绝的是当前采用时机,不是产品评价。
把结论写进数据库
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 = postgres
export PGSERVICEFILE = /path/to/pg_service.conf
export PGSERVICE = pg36-admin
service 文件权限收窄,不在命令行或 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 all
3. 先验证支持文件
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_ch14 marker/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. 注入预期特权失败
owner-create-vector.sql :
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 );
目录验收:
CREATE INDEX 成功还不够;检查 pg_index、pg_am 与 pg_opclass,防止
名字相同但实现漂移。
7. 采集扩展与成员目录
extension-inventory.sql :
name
object version
owner
nominal schema
relocatable
superuser/trusted/requires
member count
marker
更新前 PostgreSQL 18.6:
pg_trgm 1.3 owner=pg36_owner trusted=t members=37
vector 0.8.4 owner=postgres trusted=f members=237
member-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.6 build;其他 major 可有
条件差异。
8. 权限矩阵
pg36_app:
USAGE shop_ch14 = true
SELECT review/docs = true
INSERT/UPDATE/DELETE = false
extension owner = false
app-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.282843
10. 索引计划
五行表优化器自然可能选择 seq scan。实验:
SET enable_seqscan = off ;
只用于证明索引路径存在,不用于性能结论。
trigram:
Bitmap Index Scan on candidate_doc_title_trgm_idx
vector:
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 先证明:
upgrade.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
portable-export.sql :
doc_id,title,embedding_text
1,PostgreSQL extension guide,"[1,0,0]"
...
review 要求:
header 精确;
五个主键按 1..5;
每个 embedding 是 bracketed text。
生产退出还需导入目标、语义比对和删依赖;本章只证明可携带 representation。
14. 最终不变量
final-state.sql :
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=5398634500fe53ba1fb683e9a2c6e745
checksum 包含:
三行 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 reset
reset.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 proposal
L1 不应强行复用本地 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 chain
candidate-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 boundary
pg_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 export
PostGIS 若被采用,自定义类型的 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 exit
Citus 只有在这些字段有证据后才从 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
然后才写:
顺序反过来,数据库很快会积累一组谁也不敢升级、恢复或删除的隐性平台。
上一节:建立可复用扩展 ADR · 返回本章目录 · 下一章:见微知著:全文、模糊与向量检索 ·
查看全书目录 · 查看索引中心