跳至内容
18.6 实战:设计 `pg36_shop` 生产蓝图

18.6 实战:设计 `pg36_shop` 生产蓝图

第 18 章实战与前几章不同:

no setup
no DDL
no DML
no reset
no service deployment

它把现有证据读出来,检查蓝图是否有资格进入下卷。风险等级是 L0。

18.6.1 选择保留在 PostgreSQL 内的能力

前置状态

实验要求第 4、13–17 章的最终 fixture 保留在同一本地开发实例:

database=pg36_shop
PostgreSQL major=18
session_user=postgres
pg36_owner=NOLOGIN non-superuser
pg36_app=LOGIN non-superuser
ch04-v1 physical model
ch13 routine guard
ch14 extension lifecycle
ch15 search quality
ch16 spatiotemporal
ch17 analytics/FDW + two shard database shells

缺失时本章直接失败,返回前章重建;它不会悄悄修复。

私有连接

沿用:

[pg36-admin]
host=/path/to/socket-or-host
port=5432
dbname=pg36_shop
user=postgres
chmod 600 /path/to/pg_service.conf
export PGSERVICEFILE=/path/to/pg_service.conf
export PGSERVICE=pg36-admin

密码或其他 secret 放在批准的连接机制中,不放命令行、脚本、evidence 或 Git。

先读实验合同

lab-contract.md 规定:

risk=L0 read-only
target=confirmed local fixture
allowed=catalog reads + JSON validation + evidence files
forbidden=DDL/DML/roles/extensions/deploy/failover/backup/reset
pigsty_l1=not-run

本章脚本没有 setup/reset action。这不是遗漏,而是用接口形状表达安全 边界。

上卷前置复核

task.sh 依次调用:

static/labs/ch04/task.sh verify
static/labs/ch13/task.sh verify
static/labs/ch14/task.sh verify
static/labs/ch15/task.sh verify
static/labs/ch16/task.sh verify
static/labs/ch17/task.sh verify

它们只验证 retained fixture。第 17 章还分别连接 pg36_shard_apg36_shard_b,防止协调端看似正常而远端状态已经漂移。

只读事务

每个 catalog capture 都显式开始:

BEGIN TRANSACTION
ISOLATION LEVEL REPEATABLE READ
READ ONLY;

再 include context.sql

context 验证:

current_database = pg36_shop
server_version_num in 18.x
session_user = postgres superuser
can inspect pg36_owner
owner role = NOLOGIN, non-superuser
app role = LOGIN, non-superuser
ch04 schema_version present

脚本结束 COMMIT,但 read-only 事务没有业务变更。

平台状态

platform-state.sql 输出稳定 key/value:

database=pg36_shop
server_major=18
server_version=18.4 (formal run)
session_user=postgres
in_recovery=false
model_version=ch04-v1
relation_checksum=f8a7bfae59c6d16cd323abecfefe1014
pigsty_reference=4.4
pigsty_l1=not-run
mutation=none

mutation=none 不是只靠自报:all 在前置复核后抓两轮,并对状态与 catalog 逐字节 cmp

能力快照

capability-snapshot.sql 把数据库事实压成九行:

capabilitylifecycleevidence
relational coreacceptedch04-v1
atomic database logicaccepted with scopech13-routine-guard-v1
lexical/fuzzy searchacceptedpg_trgm:1.6
semantic searchpilotvector:0.8.4
spatiotemporalconditionalbtree_gist:1.8,postgis:3.6.4
analytical federationlab-onlypostgres_fdw:1.2
search quality fixtureacceptedch15-search-v1
spatiotemporal fixtureacceptedch16-spatiotemporal-v1
analytics fixtureacceptedch17-analytics-v1

这里有意把“扩展安装事实”和“生命周期判断”并列。SQL 能证明版本存在, 生命周期还来自前章的质量、安全与运维边界。

extension catalog

extension-catalog.sql 记录:

extension_name
extension_version
schema_name
owner_name
relocatable
comment

正式 fixture 精确包含六项:

btree_gist 1.8
pg_trgm 1.6
plpgsql 1.0
postgis 3.6.4
postgres_fdw 1.2
vector 0.8.4

教学扩展必须保留 pg36 chXX ... safe to rebuild marker。marker 只用于本书 精确识别,不应照搬成生产对象治理方案。

schema 与 role catalog

schema-catalog.sql 冻结:

shop / shop_private
shop_ch13 / shop_ch14 / shop_ch15
shop_ch16 / shop_ch16_ext
shop_ch17 / shop_ch17_ext

每项必须由 pg36_owner 拥有并保留精确 comment。

role-catalog.sql 只导出三种相关身份,避免把环境中其他角色误收入出版 fixture。审查器验证:

pg36_app   LOGIN, !SUPERUSER, !BYPASSRLS
pg36_owner NOLOGIN, !SUPERUSER
postgres   LOGIN, SUPERUSER (formal local admin)

为什么不探测 Pigsty

当前实例不是本章声明的目标 Pigsty cluster。若脚本从本机进程名或目录猜测 Pigsty 状态,会产生伪证据。

所以蓝图准确写:

Pigsty reference mapping = documented
Pigsty L1 = not-run

第 19 章在明确 target/inventory 后才执行环境验收。

18.6.2 选择外置组件及其数据契约

五份合同先于产品选型

external-data-contracts.json 包含:

product-cache-v1
order-events-v1
product-media-v1
analytics-export-v1
external-search-projection-v1

每份必须有 17 个核心字段,包括:

id / kind / status / owner / authority
source / sink / freshness / delivery / ordering
idempotency / rebuild / failure_mode / reconciliation
deletion / security / exit

这比简单画一条箭头严格得多。

cache 合同

关键规则:

business authority=PostgreSQL
cache identity=product_id + source version
TTL <= 300 seconds proposal
stale version never replaces newer
outage falls back to bounded PostgreSQL reads
namespace can be discarded and rebuilt

validator 专门扫描 cache authority。若把:

"product_business_state": "cache"

则报:

E_CACHE_AUTHORITY

order event 合同

business state + publication intent -> PostgreSQL transaction
delivery/replay log                 -> event bus
delivery                            -> at-least-once
ordering                            -> per order_id, no global order
idempotency                         -> stable event_id

publish lag 仍是 chapter 24 pending。写出 pending 比伪造一个 P99 更准确。

media 合同

bytes -> object storage
identity/owner/state/checksum -> PostgreSQL
immutable object version
visible only after checksum + metadata agree
orphan upload quarantined
two-phase deletion

它是 accepted-boundary,表示“字节外置”这一边界已选择,不表示具体 object provider 已选择或 L1 已通过。

analytics 合同

source=snapshot or CDC
sink=versioned immutable analytical tables
watermark on every dataset
last complete state
row/aggregate/partition checksum
tombstone propagation
new generation rebuild

这份合同会在第 29 章的数据迁移与 CDC 状态机中具体化。

external search 合同

状态:

deferred-until-trigger

进入条件不是“想用”,而是 PostgreSQL 搜索基线在质量、规模、语言或独立 SLO 上失败。

启用前必须证明:

snapshot + idempotent changes
monotonic product version/tombstone
index generation
quality golden
document count/payload hash
alias swap
fallback classification
exit to PostgreSQL

正向文档关系

baseline-v1.6-proposal.json 引用所有合同。每项 capability 又引用它需要的合同。

validator 检查:

blueprint contract set == declared contract set
capability references exist
external/pilot/conditional placement has trigger
every capability has owner and evidence

这能发现拼写、遗漏和结构漂移。

七个对抗性反例

negative-cases.json 不是伪造七份静态错误文件,而是对正确文档做 JSON path mutation:

反例期望错误
无 evidence 宣称 Pigsty L1 passedE_L1_EVIDENCE
清空全部 exit pathE_EXIT_PATH
cache 成为商品业务权威E_CACHE_AUTHORITY
删除消息合同 rebuildE_CONTRACT_FIELD
loopback FDW 允许生产E_FDW_LAB_ONLY
删除 production offering objectiveE_SERVICE_OBJECTIVE
删除 vector pilot gateE_EXTENSION_GATE

测试要求实际错误码与期望码精确相同。若错误文档意外通过,或被另一个更早的 无关规则拦截,negative suite 都失败。

为什么 validator 只用 Python 标准库

validate.py 只依赖:

argparse
copy
hashlib
json
pathlib

目的不是排斥 schema 工具,而是让读者在最小环境中运行并看到业务策略代码。 生产平台可以再加 JSON Schema、OPA、CI policy 或签名。

canonical hash

报告为四份核心文档计算 canonical JSON SHA-256:

sort object keys
compact separators
UTF-8
preserve array order

这避免 indentation/key order 影响内容身份,同时让 gate/capability 顺序仍然 有意义。

注意:canonical hash 证明文档未变,不证明内容正确;内容正确还靠人工决策、 数据库证据和负例。

18.6.3 输出服务目录草案、架构 ADR 与下卷验收问题

资产目录

static/labs/ch18/
├── lab-contract.md
├── architecture-adr.md
├── platform-map.mmd
├── pigsty-declaration.example.yml
├── service-catalog.json
├── external-data-contracts.json
├── baseline-v1.6-proposal.json
├── lower-volume-gates.json
├── negative-cases.json
├── context.sql
├── platform-state.sql
├── extension-catalog.sql
├── schema-catalog.sql
├── role-catalog.sql
├── capability-snapshot.sql
├── validate.py
├── review.py
└── task.sh

没有生成的 evidence 被提交到源码目录。

单独验证文档

python3 static/labs/ch18/validate.py \
  --blueprint static/labs/ch18/baseline-v1.6-proposal.json \
  --catalog static/labs/ch18/service-catalog.json \
  --contracts static/labs/ch18/external-data-contracts.json \
  --gates static/labs/ch18/lower-volume-gates.json

预期:

{
  "status": "ok",
  "counts": {
    "offerings": 4,
    "extension_bundles": 5,
    "contracts": 5,
    "capabilities": 9,
    "lower_volume_gates": 18,
    "exit_paths": 5
  }
}

加负例:

python3 static/labs/ch18/validate.py \
  --blueprint static/labs/ch18/baseline-v1.6-proposal.json \
  --catalog static/labs/ch18/service-catalog.json \
  --contracts static/labs/ch18/external-data-contracts.json \
  --gates static/labs/ch18/lower-volume-gates.json \
  --negative-cases static/labs/ch18/negative-cases.json

预期 case_count=7 且全部 actual/expected code 相等。

单轮 capture

evidence="$(mktemp -d /tmp/pg36-ch18.XXXXXX)"

PG36_EVIDENCE_DIR="$evidence" \
  static/labs/ch18/task.sh capture

输出:

status=capture-ok
mutation=none
evidence=/tmp/...

每个 cycle 的 review.py 验证:

  • manifest target/version/hash;
  • relation checksum;
  • extension/schema/role exact identity;
  • capability lifecycle;
  • normal/negative policy report;
  • stderr 为空。

两轮正式运行

evidence="$(mktemp -d /tmp/pg36-ch18-final.XXXXXX)"

PG36_EVIDENCE_DIR="$evidence" \
  static/labs/ch18/task.sh all

正式 PostgreSQL 18.4 开发 fixture 的结果:

status=ok
preflight=ch04+ch13+ch14+ch15+ch16+ch17
cycles=2-byte-identical
documents=catalog+contracts+blueprint+18-pending-gates
counterexamples=7-rejected
pigsty_l1=not-run
mutation=none
release_candidate_checksum=beec6b6d47075a7b3b4a6aa6ee3ca2902ef8d555547fe6c2b2b009e56c25c9eb

源码后续改变时 checksum 会改变;应以当次 manifest 与 validator report 为准。

两轮比较什么

platform-state.csv
extension-catalog.csv
schema-catalog.csv
role-catalog.csv
capability-snapshot.csv
validation-report.json
negative-report.json
review.txt

manifest.txt 含 capture 时间,故不做 byte compare;其余确定性证据必须一致。

运行后状态不需要复位

本章没有数据库写操作。若运行前后出现状态变化,应当视为:

  • 外部并发变更;
  • 某个前置 verify 实现违反只读预期;
  • capture SQL/任务脚本缺陷;
  • 环境不再适合作为冻结 fixture。

不要用 reset 掩盖,应先保留 evidence 并诊断。

架构 ADR

architecture-adr.md 记录:

context
decision per capability
external contracts
service offerings
Pigsty reference mapping
proposed topology
positive consequences
costs/risks
rejected alternatives
revision triggers

明确拒绝:

everything in PostgreSQL
everything split immediately
loopback FDW as production proof
Pigsty install as production readiness

ADR 的 status 是:

proposed; accepted for lower-volume validation,
not production approval

18 个下卷 gate

lower-volume-gates.json 严格映射第 19–36 章:

gate
19deployment baseline
20HA
21backup/restore
22access/routing
23security
24governance
25observability
26capacity
27tuning
28vacuum/maintenance
29migration
30upgrade
31incident framework
32PITR
33failover/rebuild
34overload
35forensics
36postmortem/platform improvement

每个 gate 有 owner、两个核心问题、required evidence 与 pending 状态。

gate 不是章节阅读打卡

chapter completed 不等于 gate passed。例如读完第 21 章但没有在目标环境 恢复,ch21-backup-restore 仍然 pending。

通过 gate 应产生:

target identity
version
procedure
raw evidence
review result
owner approval
limitations
expiry/review trigger

服务目录如何升级

下卷完成后,不直接覆盖 1.6-proposal。应:

  1. 收集每个 gate evidence;
  2. 修改不成立的 topology/objective/bundle;
  3. 记录 ADR revision;
  4. 生成新的 catalog/blueprint release;
  5. 重新跑正负 policy;
  6. 由 owner 批准;
  7. 保留 proposal 历史。

本章通过后能说什么

可以说:

在 PostgreSQL 18.4 的受控本地 fixture 上,第 4、13–17 章证据仍然成立; pg36_shop 的服务目录、能力决策、五份外置合同和 18 个下卷 gate 引用闭合, 七个危险反例被拒绝,两次只读快照一致。

不能说:

Pigsty 生产 cluster 已部署、SLO 已实现、备份可恢复、HA 可达目标、安全已 通过、容量足够。

这条语言边界,也是本章最后一项验收。

进入下卷

上卷回答:

PostgreSQL 如何正确建模、查询、扩展与交付应用能力

下卷开始回答:

这些能力如何在真实环境中持续、可恢复、可观察、可升级地成为服务

下一步是 第 19 章:环境规划与部署基线: 把 proposal 中的主机、软件、网络、存储、故障域和 Pigsty inventory 变成第一 份目标环境证据。


上一节:Pigsty 作为参考实现 · 返回本章目录 · 下一章:开天辟地:环境规划与部署基线 · 查看全书目录 · 查看索引中心

最后更新于