# 第 15 章 配套代码 · 复制与高可用

## 准备工作

1. 跑 `psql -h 127.0.0.1 -U postgres -d learn_pg -f ../init.sql` 在主库创建 `ch15_orders / ch15_order_items` 等业务表 + `ch15_pub_orders` 发布 + `ch15_standby1_slot` 物理槽
2. 安装依赖：`pip install "psycopg[binary]>=3.1"`
3. 02 / 04 是 docker 化的「一主一从」搭建脚本，需要本机能跑 `docker`，并且 5432 / 5433 端口空闲
4. （可选）通过环境变量覆盖默认连接：
   - `export PG_DSN="host=... port=... dbname=... user=..."`（01、05）
   - `export MASTER_DSN=... REPLICA_DSN=... SUB_CONN=...`（03 用）

> ⚠️ **本章很多脚本需要两个 PG 实例**（一主一从）。流复制 / 逻辑复制都做不了「单实例自演」。`02_setup_streaming_replication.sh up` 会自动起两个 docker 容器，03 / 04 / 05 在此基础上演示。

## 脚本一览（推荐运行顺序）

| 脚本 | 一句话说明 | 关键 PG 特性 |
|------|------------|--------------|
| `01_show_replication_status.py` | 单纯读 `pg_stat_replication / pg_replication_slots / pg_publication`，看主库上挂了哪些从库 | `pg_stat_replication`、`pg_walfile_name` |
| `02_setup_streaming_replication.sh` | docker 一键起一主一从；子命令 `up/status/write/read/failover/down` | `pg_basebackup -R -S`、`hot_standby` |
| `03_logical_replication.py` | 主从两个独立实例之间的逻辑复制；`setup/write/verify/status/teardown` | `CREATE PUBLICATION / SUBSCRIPTION` |
| `04_promote_failover.sh` | 演示「主库挂了 → `pg_promote` 提升 → `pg_rewind` 让旧主回来」 | `pg_promote`、`pg_rewind` |
| `05_replication_lag.py` | 持续监控复制延迟（字节 + 时间双维度） | `pg_wal_lsn_diff`、`replay_lag` |

## 运行示例

```bash
bash 02_setup_streaming_replication.sh up
bash 02_setup_streaming_replication.sh status
bash 02_setup_streaming_replication.sh write
bash 02_setup_streaming_replication.sh read

python3 05_replication_lag.py --interval 1
bash 04_promote_failover.sh demo
bash 02_setup_streaming_replication.sh down
```

## 预期输出

`05_replication_lag.py` 输出每 2 秒刷新一次：

```
[14:23:01]  1 个从库连接
  app                  addr             state      sync       send      write     flush     replay    total | wlag   flag   rlag
  ----------------------------------------------------------------------------------------------------
  walreceiver          172.18.0.3       streaming  async      0.0B      0.0B      0.0B      0.0B      0.0B  | 12ms   18ms   24ms
```

## 常见报错与依赖

- `connection refused` → PG 未启动，或 docker 网络没建好
- `relation "ch15_orders" does not exist` → 没跑 `init.sql`
- `WARNING: out of replication slots` → 调高 `max_replication_slots`
- `pg_basebackup: FATAL: number of requested standby connections exceeds max_wal_senders` → 调高 `max_wal_senders`
- `pg_rewind` 报「源端缺少 WAL」→ 旧主未开 `wal_log_hints` 或 `data_checksums`，回退方案是删 data 目录重新 `pg_basebackup`
- 03 需要 5433 端口启一个独立 PG 实例（不是物理 standby），自行用 docker 起：`docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=postgres postgres:16 -c wal_level=logical`
- 04 / `pg_promote` 后旧主再回来必须先 `pg_rewind` 或重新基础备份，否则会出现 timeline 分叉
