# 发布窗口中的平台观察

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

---

数据库 catalog 只能回答“当前对象是什么、谁在等谁”；发布平台还要回答“哪条流量进入哪里、影响面多大、过去几分钟发生了什么、是否仍在安全水位内”。

一次发布观察闭环：

```text
change identity + UTC window
  → exact Pigsty service / database / role / application_name
  → PostgreSQL live catalog
  → Prometheus/Grafana historical trend
  → application release + SLI/error evidence
  → pause/switch/contract decision
```

仪表盘不是 DDL 正确性的替代品；SQL postcheck 也不是容量与用户影响的替代品。

## 11.6.1 从服务入口隔离实验流量 {#item-11-6-1}

### Pigsty 的四类默认服务

Pigsty v4.5 的默认 PostgreSQL 服务：

| Service | Port | 默认路径 | 典型用途 |
|---|---:|---|---|
| primary | 5433 | HAProxy → primary pool | 生产读写 |
| replica | 5434 | HAProxy → replica pool | 生产只读 |
| default | 5436 | HAProxy → primary PostgreSQL | DBA、ETL、直接写 |
| offline | 5438 | HAProxy → offline/replica PostgreSQL | OLAP、离线、交互 |

端口与 selector 来自 `pg_default_services` / `pg_services` 配置，以目标集群的当前 [Pigsty Service 文档](https://pigsty.io/docs/pgsql/service/) 和 inventory 为准。

模式变更不应随便借用 production application pool：

- session-level DDL 设置可能被 pool 复用污染；
- transaction pooling 可能不支持所需 session 语义；
- maintenance traffic 与用户请求难以区分；
- pool timeout 与 migration timeout 可能互相覆盖；
- 连接数/排队会混入应用 SLI。

通常使用受控 DBA/default direct service 或专用 migration service；仍必须确认它路由到 writable primary。

### “连到 5436”仍不是目标证明

连接后第一条证据：

```sql
SELECT
    current_database(),
    session_user,
    current_user,
    current_setting('server_version'),
    pg_is_in_recovery(),
    current_schemas(false),
    current_setting('application_name');
```

还要验证：

- cluster/instance identity；
- expected schema version；
- object owner/marker；
- target relation OID/definition；
- transaction pooling 是否绕过；
- connection string 没有展开 secret；
- role 能力只覆盖本次 change。

错误 primary 上的正确 DDL 仍是事故；正确 primary 上的错误 database 也是。

本章 service file 只保存受控连接 identity，脚本使用：

```text
service=pg36-admin
application_name=pg36-ch11-<phase>
```

manifest 记录 service name，不展开密码/URI。

### 用 application_name 切出发布流量

每个 phase 使用稳定前缀：

```text
pg36-ch11-lock-holder
pg36-ch11-lock-waiter
pg36-ch11-backfill
pg36-ch11-index-build
pg36-ch11-partition-attach
pg36-ch11-partition-detach
```

这样可以在：

```sql
SELECT
    pid,
    backend_start,
    application_name,
    state,
    xact_start,
    query_start,
    wait_event_type,
    wait_event,
    left(query, 300)
FROM pg_stat_activity
WHERE application_name LIKE 'pg36-ch11-%';
```

区分 phase，保存 live evidence，并让 reset 在 worker 存活时 fail closed。

`application_name` 是可伪造 label，不是 authentication/authorization。取消 session 前仍要组合：

```text
database + pid + backend_start + role + client + application + query identity
```

防止 PID reuse 或同名前缀误伤。

### 隔离流量不等于隔离资源

专用 service/role/application_name 改善 attribution 和权限边界，却仍共享：

- primary CPU/IO/buffer；
- WAL 与 replication；
- disk；
- autovacuum；
- lock manager；
- checkpoint；
- network；
- replicas 的 replay。

因此 L1 演练服务不能证明生产容量。若要在生产 shadow/backfill，必须设置实际资源和 SLI 水位。

### 不要为一次 DDL 临时改平台配置

例如为了让 backfill 更快，随手全局修改：

```text
max_wal_size
checkpoint_timeout
autovacuum
statement_timeout
work_mem
max_parallel_maintenance_workers
```

会把一个 schema change 变成 schema + config 联合 change，扩大变量和恢复面。优先使用 session/local setting；确需 config change 时，独立变更单、独立 evidence 和独立复位。

## 11.6.2 观察锁、复制延迟、WAL 与资源水位 {#item-11-6-2}

### 从发布 SLI 开始

发布期间先看用户影响：

```text
request success/error rate
p50/p95/p99 latency
timeout/retry rate
queue depth
connection acquisition latency
business invariant errors
```

再解释数据库侧原因。一个 WAL spike 如果不影响 SLO 且在预算内，可能可接受；一个没有明显 CPU spike 的 lock convoy 也可能让 tail latency 立即恶化。

### Pigsty dashboard 路线

Pigsty v4.5 当前文档列出的相关面板：

```text
PGSQL Activity:
  sessions, load, active/idle, locks

PGSQL Xacts:
  transaction rate/time, rollback, lock

PGCAT Locks:
  live activity and lock waits from catalog

PGSQL Query / PGCAT Query:
  affected query family, calls, latency, plan/stat context

PGSQL Persist:
  WAL, checkpoint, archive, IO, XID

PGSQL Replication:
  physical/logical replication, slots, pub/sub

PGSQL Service / Proxy / PgBouncer:
  routing, backend health, queue and pool

PGSQL Instance / NODE dashboards:
  CPU, memory, disk, filesystem, IO, network
```

名称与布局会升级，以当前 [Pigsty Dashboard 文档](https://pigsty.io/docs/pgsql/dashboard/) 为准，不把截图坐标或 panel ID 写进长期 runbook。

### 锁观察必须落回 exact graph

历史曲线回答：

```text
when did lock waits rise?
how many sessions and how long?
which database/cluster?
did user latency rise together?
```

live catalog 回答：

```sql
SELECT
    waiter.pid AS waiter_pid,
    waiter.backend_start AS waiter_epoch,
    waiter.application_name AS waiter_app,
    waiter.wait_event_type,
    waiter.wait_event,
    blocker.pid AS blocker_pid,
    blocker.backend_start AS blocker_epoch,
    blocker.application_name AS blocker_app,
    blocker.xact_start,
    blocker.state
FROM pg_stat_activity AS waiter
CROSS JOIN LATERAL unnest(
    pg_blocking_pids(waiter.pid)
) AS edge(blocker_pid)
LEFT JOIN pg_stat_activity AS blocker
  ON blocker.pid = edge.blocker_pid;
```

对 DDL 还要查 relation lock：

```sql
SELECT
    activity.application_name,
    lock.relation::regclass,
    lock.mode,
    lock.granted,
    lock.waitstart
FROM pg_locks AS lock
JOIN pg_stat_activity AS activity
  ON activity.pid = lock.pid
WHERE lock.relation IS NOT NULL;
```

看到 `AccessExclusiveLock` 不能直接杀 session；先确定 holder、waiter、队列和业务影响。

### WAL 观察不是只看一个 counter

模式变更会影响：

```text
WAL generation rate
WAL directory size
archive throughput/failure
replication slot retained WAL
replica receive/replay lag
checkpoint frequency and write pressure
network throughput
backup/PITR window
```

单节点本章用 `pg_current_wal_insert_lsn()` 前后差作为相对 A/B；生产需时间序列。counter 应使用 rate/increase，并注意 restart/reset epoch。还要区分：

- generated WAL；
- sent/received WAL；
- replay progress；
- slot `restart_lsn` retention；
- archive queue。

主库 lag 为零不代表 replica 已应用；bytes lag 与 time lag也不能互换。

### Replication lag 的停止线

backfill/validation/CREATE INDEX 可让 replica：

- replay 落后；
- read query 与 replay 冲突；
- WAL 堆积占盘；
- failover 后 RPO/RTO 风险增加。

停止条件应同时绑定：

```text
max replay bytes/time lag
max retained WAL / minimum disk free
replica read SLI
archive success
failover readiness policy
```

如果发布时 primary failover，默认暂停 change，重新验证：

```text
new primary identity
committed schema phase
checkpoint state
concurrent index/partition intermediate state
worker ownership
application routing
```

不能假设脚本连接自动漂移后可从上一行继续。

### 资源水位与 workload attribution

至少保存：

| 维度 | 观察 | 解释 |
|---|---|---|
| CPU | instance/node usage | transform/index build CPU |
| IO | read/write latency/throughput | scan/rewrite/checkpoint |
| disk | data/WAL/temp free | rewrite double space/retention |
| memory | buffer/cache/OS pressure | scan cache displacement |
| connection | app/admin/pool queues | migration contention |
| vacuum | dead tuples/xmin/age | backfill cleanup debt |
| query | user + migration families | regression attribution |

发布 connection 的 `application_name`、UTC window 和 query identity 让这些图能与具体 phase 对齐。

### 指标盲点也是停止条件

如果 exporter、Grafana、logs、application telemetry 任一关键链路不可用：

```text
cannot observe ≠ no impact
```

对于高风险 rewrite/backfill/contract，应暂停，而不是在盲区继续。监控恢复后先补采 live state，再决定 resume。

## 11.6.3 配置变更与模式变更分别留证 {#item-11-6-3}

### 三种 identity

一次完整发布至少有：

```text
application release:
  image/git SHA, rollout/flag, version population

database migration:
  migration ID, source checksum, phase, schema post-state

platform/config change:
  inventory commit, rendered diff, apply task, runtime value
```

它们相互引用，但不能共享一个模糊“release-2026-07-29”后失去可归因性。

### Database evidence

本章 evidence：

```text
manifest.txt
preflight.txt
setup/expand/validate/switch outputs
lock-attempt.stderr
lock-graph.csv
default-catalog.csv
constraint-before.csv
constraint-after.csv
backfill-interrupted.json
backfill-resumed.json
partition-summary.json
contract-gate.stderr
verify.txt
model-verify-after.txt
review.txt
```

manifest 保存：

- UTC capture time；
- action/service；
- psql/Python/server version；
- database/user/recovery；
- source SHA-256。

它不保存 secret，也不把 dynamic PID/filenode/timing 当 golden。

### Pigsty config evidence

如果本次确实改变 `pg_services`、PostgreSQL 参数或监控配置，另存：

```text
inventory/config source commit
target cluster/instance selector
rendered before/after diff
validation/lint output
exact Ansible tag/command
changed vs restarted instances
pg_settings source/sourcefile/pending_restart
service routing/health postcheck
rollback/reapply command
```

不要只保存 `SHOW parameter`。runtime value 不能证明它来自哪个 inventory version，也不能说明 restart 后会不会保持。

反过来，config repo diff 也不能证明 runtime 已生效；两边都要。

### Schema migration 不应修改不可变历史

[baseline-v0.6-proposal.json](/labs/ch11/baseline-v0.6-proposal.json) 不改写 v0.1 baseline，而是：

```text
base checksum
  + dependency v0.5 checksum
  + SAFE-MIGR-006 statement change
  + DEFAULT-VERS-010 runtime check change
  + ch11 evidence paths
  + promotion conditions
```

同理，生产 migration 文件一旦发布，不应原地改内容后保留同 migration ID。若修复：

- 发布新 migration identity；
- 说明依赖和前置 phase；
- 保留旧 artifact/checksum；
- fresh install 继续由同一权威链生成或验证。

### Change log 与 evidence package

建议目录：

```text
change-id/
  intent.md
  approvals/
  application/
  database/
  platform/
  observations/
  decision-log.md
  final-review.json
```

decision log 记录：

```text
timestamp UTC
observer/decision owner
current phase
watermarks
continue/slow/pause/switch/contract
reason and evidence links
next review time
```

这样事故复盘能回答“当时基于什么证据继续”，而不是只有最后成功/失败。

### 敏感信息与保留期

SQL、query parameter、client address、logs、application payload 可能包含：

- customer identifiers；
- tokens/credentials；
- financial/personal data；
- internal topology。

证据包要：

```text
redact secrets and payload
retain stable hashes/IDs when enough
apply access control
declare retention and deletion
preserve chain of custody for incidents
```

不要为了“完整证据”把 password-bearing URI、`PGPASSWORD` 或完整用户 payload 写入 artifact。

### Final review 的职责边界

自动 review 可以确认：

```text
files exist
checksums/dependencies match
SQLSTATE and catalog relationships
checkpoint monotonicity
data checksums and cleanup
```

人/发布系统仍要确认：

```text
production SLI acceptable
replication/disk within budget
old consumers truly zero
observation window elapsed
contract authority granted
```

让工具拒绝它不能证明的 contract，是质量而不是自动化不完整。

## 本节验收问题

1. 发布使用哪个 Pigsty service、port、pool/direct path；
2. 连接后是否再次验证 database/role/primary/schema；
3. 每个 phase 是否有独立 application_name；
4. service 隔离与资源隔离是否被正确区分；
5. user SLI、lock、WAL、lag、disk、pool 是否同窗观察；
6. lock metric 是否能落回 exact blocker graph；
7. failover 后是否默认暂停并重新发现 state；
8. 监控盲点是否是明确停止条件；
9. application、database、platform identity 是否分离；
10. config source diff 与 runtime postcheck 是否都有；
11. migration/source artifacts 是否 checksum 固定且不可变；
12. evidence 是否脱敏、有权限和保留期；
13. 自动 review 是否诚实拒绝无法证明的生产事实。

## 参考资料

- [Pigsty：Service/Access](https://pigsty.io/docs/pgsql/service/)
- [Pigsty：PostgreSQL Dashboards](https://pigsty.io/docs/pgsql/dashboard/)
- [Pigsty：PostgreSQL Metrics](https://pigsty.io/docs/pgsql/metric/)
- [PostgreSQL 18：Monitoring Database Activity](https://www.postgresql.org/docs/18/monitoring-stats.html)
- [PostgreSQL 18：Viewing Locks](https://www.postgresql.org/docs/18/monitoring-locks.html)

---

[上一节：数据回填与流量切换](../05/) · [返回本章目录](../) · [下一节：实战：无中断演进订单模式](../07/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
