# 用 Pigsty 管理扩展可用性

LLMS 索引： [llms.txt](/llms.txt)

---

Pigsty 能把仓库、包、配置和数据库声明统一管理，但平台声明不是 PostgreSQL
事实的替代品。正确用法是：

```text
declare intent in Pigsty
  -> converge nodes and databases
  -> verify live package/config/catalog/query evidence
```

本节以 Pigsty 4.5 文档为基线。参数与扩展目录会变化，目标集群变更前应使用
同版本文档和 inventory，而不是照抄本章时间点。

## 14.5.1 包、仓库、模板与节点差异 {#item-14-5-1}

### Pigsty 中的四步模型

当前 Pigsty 扩展文档把过程分成：

```text
Download
  从上游仓库取得包，或同步到本地仓库

Install
  在 PGSQL cluster 的所有相关节点安装 OS 包

Configure
  处理 shared_preload_libraries 和扩展参数

Create
  在指定数据库执行 CREATE EXTENSION
```

这与上一节三层状态一致：

| Pigsty 动作 | 主要对象 | 原生验证 |
|---|---|---|
| Download | repo/cache | repo metadata、artifact、checksum |
| Install | node package/files | package inventory、control/library |
| Configure | Patroni/PostgreSQL 参数 | `pg_settings`、restart、日志 |
| Create | database object | `pg_extension`、成员、查询 |

不是每个扩展都需要 preload，也不是每个已安装插件都需要创建数据库对象。

### `pg_packages` 与 `pg_extensions`

Pigsty 4.5 文档区分：

```yaml
pg_packages:
  - pgsql-main pgsql-common

pg_extensions:
  - postgis timescaledb pgvector
```

- `pg_packages`：通用基础包组，通常用于所有 cluster 的核心组件；
- `pg_extensions`：特定 PGSQL cluster 需要的扩展软件包，初始化时安装，
  也可对已存在 cluster 执行 `pg_extension` tag 收敛。

示例：

```yaml
pg-meta:
  hosts:
    10.10.10.10: { pg_seq: 1, pg_role: primary }
    10.10.10.11: { pg_seq: 2, pg_role: replica }
  vars:
    pg_cluster: pg-meta
    pg_extensions: [pgvector]
```

对于已运行 cluster，先修改受版本控制的声明，再执行目标明确的 playbook：

```bash
./pgsql.yml -l pg-meta -t pg_extension
```

临时命令行覆盖：

```bash
./pgsql.yml -l pg-meta -t pg_extension \
  -e '{"pg_extensions":["pgvector"]}'
```

适合受控应急或实验，但若不回写 inventory，下一位维护者看不到持久意图。

参见 [Pigsty: Install Extensions](https://pigsty.io/docs/pgsql/ext/install/)。

### 包别名是跨发行版映射

Pigsty 使用稳定别名：

```text
pgvector
postgis
timescaledb
```

映射到 PG major 与 OS 对应包，例如：

```text
pgvector
  -> pgvector_18*                 # EL
  -> postgresql-18-pgvector       # Debian/Ubuntu
```

还提供 `pgsql-rag`、`pgsql-gis`、`pgsql-fts` 等类别别名。类别安装范围大，
实验便利不等于生产应一次装整类。生产 ADR 优先列精确候选，避免供应面无意
扩大。

包别名与 SQL 名必须在清单中同时保存：

| 项目 | package alias | SQL extension |
|---|---|---|
| pgvector | `pgvector` | `vector` |
| PostGIS | `postgis` | `postgis`、`postgis_topology` 等 |
| pg_trgm | 随 `pgsql-main`/默认包集合供应 | `pg_trgm` |

参见 [Pigsty: Extension Package Aliases](https://pigsty.io/docs/pgsql/ext/pkg/)。

### 默认供应与默认启用不是同一层

Pigsty 4.5 当前默认文档说明：

- `pgvector` 随默认 `pgsql-main` 包集合安装；
- `pg_trgm` 位于 `pg_default_extensions`，默认在数据库的 `public`
  schema 启用；
- `pg_stat_statements`、`auto_explain` 等进入默认 preload/观测集合。

这些默认值会随 Pigsty release 演进。目标环境要检查自身 inventory，而不是
从“4.4 默认”反推一个已升级多次的 cluster。

还要注意：若 `pg_trgm` 已在数据库 `public` 创建，就不能再在
`app_ext` 创建第二份同名扩展。自定义 schema 前必须协调
`pg_default_extensions`，不能让两个声明互相竞争。

参见 [Pigsty: Default Extensions](https://pigsty.io/docs/pgsql/ext/extension/)。

### 仓库可达性与本地仓库

在线环境可能直接从配置的上游/第三方仓库下载。受限环境通常由 Pigsty infra
节点维护本地软件仓库。无论哪种模式，验证：

```text
inventory requests alias
  -> alias resolves for OS + PG major + architecture
  -> artifact exists in chosen repository snapshot
  -> every PG node installs same build
  -> future restore/new-node path can obtain it
```

“主库现在有文件”不能证明：

- 新副本能加入；
- 灾备站点能恢复；
- 离线仓库仍保留旧版本；
- major upgrade 目标包已经可用。

扩展变更单应附 repo snapshot/version，而不只附公共下载 URL。

参见 [Pigsty: Download Extensions](https://pigsty.io/docs/pgsql/ext/download/)。

### 节点漂移要按拓扑检查

至少覆盖：

```text
primary
all synchronous/asynchronous standbys
delayed standby
disaster-recovery nodes
backup/restore worker image
future replacement node template
```

可以用 Pigsty/Ansible 采包事实：

```bash
ansible pg-meta -b -a 'pig ext status -c -v 18'
```

具体 `pig` 子命令以目标版本为准。更稳妥的检查还包括：

```bash
pg_config --version
pg_config --sharedir
pg_config --pkglibdir
```

对 control/library 取 hash，结果按 host 保存。不要只看 play recap：

```text
ok=...
changed=...
```

它说明自动化执行状态，不说明数据库能加载文件。

### 漂移矩阵

| host | role | PG build | package build | control hash | library hash | preload live |
|---|---|---|---|---|---|---|
| pg-1 | primary |  |  |  |  |  |
| pg-2 | replica |  |  |  |  |  |
| pg-3 | replica |  |  |  |  |  |

任一 host 不同，先修供应层，不急着执行数据库 DDL。

## 14.5.2 声明安装与数据库内 `CREATE EXTENSION` {#item-14-5-2}

### 三个参数分别回答三个问题

```yaml
pg_extensions: [pgvector]

pg_libs: 'pg_stat_statements, auto_explain'

pg_databases:
  - name: pg36_shop
    extensions:
      - { name: vector, schema: app_ext }
```

含义：

| 参数 | 问题 |
|---|---|
| `pg_extensions` | cluster 节点要安装哪些扩展软件包 |
| `pg_libs` | server 启动时要 preload 哪些库 |
| `pg_databases[].extensions` | 某数据库要创建哪些 SQL extension |

三者不能互换：

- 把 `vector` 写进 `pg_extensions` 会把 SQL 名误作包别名；
- 只写 `pgvector` package 不会自动保证每个已有数据库都创建对象；
- 把不需 preload 的库塞进 `pg_libs` 会增加启动耦合；
- 只执行 `CREATE EXTENSION`，备库节点可能仍缺包。

Pigsty 当前数据库声明示例：

```yaml
pg_databases:
  - name: meta
    extensions:
      - { name: vector }
      - { name: postgis, schema: public }
      - { name: pg_stat_statements, schema: monitor }
```

这里用的是 SQL extension name。参见
[Pigsty: Create Extensions](https://pigsty.io/docs/pgsql/ext/create/)。

### 本章声明片段

[pigsty-declaration.example.yml](/labs/ch14/pigsty-declaration.example.yml)
刻意写成：

```yaml
pg_extensions:
  - pgvector

pg_databases:
  - name: pg36_shop
    schemas:
      - { name: app_ext, owner: pg36_owner }
    extensions:
      - { name: vector, schema: app_ext }
```

它没有重复声明 `pg_trgm`，因为 stock Pigsty 默认已经在 `public` 启用。

本地直连实验为了让 namespace 与成员集中可见，把：

```text
pg_trgm + vector -> shop_ch14
```

放在同一 schema。这是教学 fixture，不要求读者破坏 Pigsty 的合理默认。
平台实践可以是：

```text
pg_trgm -> public (default)
vector  -> app_ext (per-database declaration)
```

只要 ADR、查询、dump 与权限证据反映真实布局。

### 声明不应夹带对象版本假设

仓库安装“最新可用包”与数据库对象 `VERSION` 是两个控制面。Pigsty 初始化
声明能创建扩展，但生产需要单独的 SQL migration：

```sql
CREATE EXTENSION vector
WITH SCHEMA app_ext
VERSION '0.8.4';
```

或：

```sql
ALTER EXTENSION vector UPDATE TO 'reviewed-version';
```

是否在 Pigsty YAML 固定 `version` 要以该版本参数 schema 和初始化实现为
准；即使声明能写版本，已有数据库升级仍应通过有证据的迁移流程，而不是
假设重新跑初始化会更新。

推荐职责：

```text
Pigsty inventory:
  repository/package/preload/database intent

SQL migration repository:
  exact CREATE/ALTER statements
  owner/schema/ACL
  pre/post catalog and behavior assertions

evidence:
  node + live config + database facts
```

### 预加载变更的发布顺序

需要 preload 的扩展：

```text
1. install package on every node
2. update pg_libs/parameters in inventory
3. apply Patroni/PostgreSQL config
4. rolling restart under HA policy
5. verify live setting and logs on every node
6. CREATE EXTENSION in intended databases
7. verify query/metrics/failover
```

先 `CREATE EXTENSION` 再补 preload 可能直接失败；先改 preload 而节点缺库
可能导致 restart 失败。

本章两项扩展不要求 preload，因此声明不应为了“统一”加入它们。最小配置面
也是可靠性。

### owner 与 schema

Pigsty 能创建角色、数据库与 schema；扩展 migration 仍要检查最终 owner：

```sql
SELECT
    e.extname,
    e.extversion,
    pg_get_userbyid(e.extowner) AS owner,
    n.nspname AS nominal_schema
FROM pg_extension AS e
JOIN pg_namespace AS n
  ON n.oid = e.extnamespace;
```

对 untrusted extension，管理员创建后可能由高权限角色拥有。不要为了让
应用迁移工具“方便”而把 extension owner 交给 LOGIN 应用角色。

### 已有 cluster 的收敛

不要只编辑 inventory 后等下一次重建：

```text
review declaration diff
  -> download/sync repository if needed
  -> run package convergence on exact cluster
  -> configure/restart if needed
  -> run database migration
  -> verify all layers
  -> record evidence and commit identity
```

如果 playbook 只能在部分节点成功，停止数据库对象更新，先修节点一致性。

## 14.5.3 从监控和日志识别加载失败 {#item-14-5-3}

### 一张三层检查表

#### 供应层

```bash
pg_config --version
pg_config --sharedir
pg_config --pkglibdir

# 目标文件存在、owner/mode 正确、hash 与基线一致
```

数据库看到的 control：

```sql
SELECT
    name,
    default_version,
    installed_version,
    comment
FROM pg_available_extensions
WHERE name IN ('pg_trgm', 'vector');
```

若查不到，先看 server 实际 `sharedir`，不要先查 `search_path`。

#### 进程层

```sql
SELECT
    name,
    setting,
    source,
    sourcefile,
    pending_restart
FROM pg_settings
WHERE name = 'shared_preload_libraries';
```

再看每个实例启动日志：

```text
could not access file ...
could not load library ...
undefined symbol ...
must be loaded via shared_preload_libraries ...
```

配置声明、Patroni dynamic config、磁盘配置与 live setting 可能暂时不同。
以 live + restart history + log 为准。

#### 数据库层

```sql
SELECT
    current_database(),
    e.extname,
    e.extversion,
    pg_get_userbyid(e.extowner),
    n.nspname
FROM pg_extension AS e
JOIN pg_namespace AS n
  ON n.oid = e.extnamespace;
```

再查：

```sql
SELECT * FROM pg_extension_update_paths('pg_trgm');
```

最后跑真正业务 probe。`\dx` 只证明 catalog 里有一行，不证明索引有效或
查询正确。

### 失败模式到动作

| 观察 | 解释 | 安全动作 |
|---|---|---|
| `pg_available_extensions` 无记录 | 当前 server 看不到 control | 核对节点/PG major/安装目录 |
| available 有、installed 为空 | 包在，当前数据库未创建 | 走审批后的 `CREATE EXTENSION` |
| default > installed | 支持文件较新、对象仍旧 | 评审 update path，不自动升级 |
| installed 有、library 缺 | 节点漂移，failover 风险 | 阻断晋升，恢复 exact package |
| `pending_restart=true` | 配置尚未生效 | 按 HA 策略滚动重启 |
| primary 成功、replica 加载失败 | 主备供应不一致 | 停止变更/晋升，修所有副本 |
| `must be owner` | 生命周期权限边界生效 | 用受控 owner/admin migration |
| `must be superuser` | untrusted/control 要求 | 管理员评审，禁止给 app 提权 |
| object already exists | schema/历史手工对象冲突 | 盘点依赖，禁止 `CASCADE` 硬装 |
| no update path | package 脚本图不支持 | 选择受支持中间版本或迁移方案 |

### 监控哪些事实

低基数状态：

```text
extension_expected{cluster,db,name,version}
extension_installed{cluster,db,name,version}
extension_package_parity{cluster,name,build}
extension_preload_live{cluster,instance,name}
extension_probe_success{cluster,db,name}
```

不要把每个 SQL 对象或 hash 都做成高基数时序标签。详细成员、文件 hash 和
包清单保存在 inventory/evidence；监控只暴露是否与期望一致，并链接 runbook。

事件/日志告警：

- postmaster 因库加载失败重启；
- `undefined symbol`/ABI 错误；
- extension update DDL 失败；
- recovery/replica 上 extension function 报错；
- extension 相关查询错误率突增；
- ANN/特殊索引 invalid；
- 更新后 P95/P99、WAL、内存、replica lag 越界。

### L1 验证包

本章本地 evidence 明确写 `pigsty_l1=not-run`。在真实 L1 补齐：

```text
00-inventory-commit.txt
01-repository-snapshot.txt
02-node-package-matrix.csv
03-control-library-hashes.csv
04-pg-settings-all-instances.csv
05-pg-available-versions.csv
06-pg-extension-all-databases.csv
07-member-and-index-catalog.csv
08-query-plan-and-correctness.txt
09-replica/failover-probe.txt
10-clean-restore-report.txt
11-reset-or-rollback-report.txt
```

每份 evidence 带：

```text
captured_at
cluster/database/host
server and package build
command/tool version
change/commit identity
operator
```

凭证不得进入 evidence。

### Pigsty 管理扩展的停止线

- inventory 别名无法解析到目标 OS/PG major；
- 本地仓库没有灾备/新节点需要的包；
- 任一 replica 包/hash 不一致；
- preload 变更未完成滚动重启；
- 只在 `postgres` 数据库验证，业务数据库未盘点；
- 默认 `pg_trgm` 与自定义 schema 声明冲突；
- playbook 成功但原生 catalog/query probe 失败；
- 没有 clean restore 与 major-upgrade 路线。

平台自动化可以缩短执行时间，不能降低验收标准。

### 本节结论

Pigsty 提供的是可声明、可重复的控制面：

```text
package alias + cluster intent + preload + database declaration
```

PostgreSQL 提供的是 live 数据面事实：

```text
files + settings + pg_extension + members + query behavior
```

两者一致，扩展才“可用”；再加升级、恢复和退出证据，才“可运营”。

---

[上一节：生命周期与升级耦合](../04/) · [返回本章目录](../) · [下一节：建立可复用扩展 ADR](../06/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
