20.5 切换、故障转移与重加入
角色变化不是一个动作,而是一条状态机:
preflight
-> stop/drain or detect failure
-> choose authority and candidate
-> promote
-> route new clients
-> reconcile transactions
-> rejoin/rebuild old member
-> restore redundancyplanned switchover 与 unplanned failover 经过其中不同的路径,不能用同一条 成功记录互相代替。
20.5.1 planned switchover 与 unplanned failover
两个动作的前提不同
| 维度 | planned switchover | unplanned failover |
|---|---|---|
| current leader | 健康、可协调 | 可能失联/已死/未知 |
| candidate | 可事前检查并命名 | 在不完整信息中选择 |
| client drain | 可安排 | 通常来不及 |
| WAL catch-up | 可等待 | tail 可能永失 |
| fencing | 正常 demotion 可完成 | 是核心风险 |
| detection | 人工发起,无故障检测 | 必须检测/租约 |
| data-loss risk | 通常可控 | 取决于 sync/lag/failure |
| purpose | 维护、升级、演练 | 恢复故障服务 |
Patroni 官方 patronictl
把 switchover 定位于健康 cluster,把 failover 定位于不健康 cluster,并
明确提醒 failover 可能因 candidate 落后而丢数据。
planned switchover preflight
最低清单:
change authority and window approved
target is exact cluster
current leader uniquely identified
candidate explicitly named
candidate streaming and inside lag bound
system identifier/timeline valid
Patroni not paused
DCS reachable
sync/async policy known
backup/recovery gate status known
client and long transactions assessed
service endpoint observed
rollback/baseline state defined本章 executable 还固定:
leader pg-test-1 -> candidate pg-test-2
then leader pg-test-2 -> candidate pg-test-1
pg-test-3 never promoted by this lab若 topology 与合同不一致,拒绝,而不是“选当前看起来最合适的”。
命令是 mutation
正式底层动作:
sudo -iu postgres \
patronictl -c /etc/patroni/patroni.yml \
switchover pg-test \
--leader pg-test-1 \
--candidate pg-test-2 \
--force--force 只跳过 CLI 交互;它不是“强制安全”。脚本之所以可以用,是因为
外层已有:
exact target token
nonproduction assertions
empty evidence directory
fresh chapter-19 preflight
explicit leader/candidate
safe restore condition生产 runbook 是否允许 --force,要由审批与自动化设计决定。
failover 需要更严格的 stop conditions
在 manual failover 前:
old primary definitely down/fenced?
which candidate has latest safe WAL?
what acknowledged commits may be missing?
DCS has coherent authority?
client write paths drained?
business owner accepts recovery point?如果旧主状态 unknown,最快的安全动作常是先冻结写,而不是立即 promote。
automatic failover 也需要演练
配置了 Patroni 不代表路径已验证。要测:
failure detection time
leader lock expiry
candidate choice
fencing
promotion
service health convergence
client reconnect
commit outcome
old-member rejoin
redundancy restoration并覆盖 process、host、network、DCS 等不同故障。一次 systemctl stop postgresql 只覆盖其中一个很温和的分支。
回退不是 timeline 倒退
本章“恢复基线”含义:
final teaching role = pg-test-1 primary实际历史:
5 -> 6 -> 7第二次 switchover 没有回到 timeline 5,也不应删除 timeline history。 角色布局恢复,历史继续前进。
planned 结果的正确表述
可以说:
named healthy candidate accepted planned leadership
old primary rejoined streaming
client service resumed sampled writes
all acknowledged test tokens survived
baseline role restored不能说:
automatic host failover passed
zero RPO under primary loss
watchdog fencing passed
production RTO is six seconds20.5.2 端点切换、客户端恢复与只读窗口
role 变化不会迁移现有 TCP session
promotion 后:
new primary exists不代表:
every old session teleported to it客户端可能经历:
- connection reset;
- transaction aborted;
- pool 中旧连接失效;
- DNS/VIP/cache 尚未更新;
- HAProxy health check 尚在 rise/fall window;
- driver backoff;
- application circuit breaker;
- in-flight commit outcome unknown。
所以 client RTO 通常晚于 control-plane stable。
stable endpoint
应用应连接 service identity,而不是把当前 primary IP 写死。
Pigsty 默认服务:
| service | port | 默认语义 |
|---|---|---|
| primary | 5433 | HAProxy → current primary pool |
| replica | 5434 | read-only replica pool |
| default | 5436 | current primary direct PostgreSQL |
| offline | 5438 | offline/OLAP route |
正式 probe 使用:
host=10.10.10.11
port=5433
target_session_attrs=read-write任意成员的 HAProxy 都能根据 Patroni /primary health 把新连接送到当前
primary;本次选一个固定 service host,避免 DNS/VIP 额外变量。
target_session_attrs=read-write
libpq 可以在连接后确认目标接受 read-write transaction。它能避免把连接 留在 recovery/read-only 节点,但不替代:
server authority
proxy health
fencing
transaction retry policy它是 client-side sanity check,不是 election protocol。
健康检查窗口
服务切换时间含:
Patroni role/API update
HAProxy check interval
rise/fall thresholds
old session shutdown
new connect
PgBouncer state
application retry/backoffPigsty 当前默认 service 示例会用 Patroni REST /primary,HAProxy 还可在
backend marked down 时关闭 session。准确配置应查看当前 render 后的
HAProxy,而不只照文档默认。
读服务的降级语义
replica service 可继续提供只读,但要回答:
允许多旧?
新 primary 切换时 read replica 跟哪条 timeline?
read-your-writes 是否需要?
replica 不足时是否 fallback primary?
offline replica 是否可承接 online read?Pigsty default replica service 通常:
prefer regular replicas
use primary/offline as backup according to selectors业务必须知道 fallback,否则故障时 primary 可能同时承受全部写与回退读。
连接重试与业务重试
分三层:
connect retry
建立新 TCP/database session
transaction retry
重做一个明确失败、可安全重做的 transaction
business retry
再次执行订单/支付等意图三者不能混成 driver 的无限 retry。
安全结构:
INSERT INTO payment_request(idempotency_key, ...)
VALUES ($1, ...)
ON CONFLICT (idempotency_key)
DO UPDATE SET ... -- 或返回原结果
RETURNING ...;具体业务必须保存状态和结果,不能只靠本章 synthetic table。
outcome unknown
当客户端在发送后收到 network error:
do not assume commit
do not assume rollback
look up by idempotency token本章 probe 故意把所有异常保守记为 unknown,然后查表。正式运行:
unknown=25
unknown_committed=0
unknown_absent=25
unreconciled=0另一次运行 unknown count 可能不同,甚至可能出现 committed。正确性来自 reconciliation,不来自“通常不会”。
sampled write gap
probe 每约 0.2 秒尝试一次。保守 gap:
last acknowledged before action
-> first acknowledged after Patroni topology stable正式:
action command 2.735 s
action -> stable 5.823 s
conservative write gap 6.007 s
max adjacent ack gap 5.208 s为何几个数不同:
- CLI return 早于完整 topology stable;
- acknowledged event 受 probe interval 影响;
- reconnect/HAProxy/PgBouncer 影响 client;
- conservative metric 刻意使用 stable boundary。
TLS 例外
沙箱外部 port 5433 不接受 TLS,service file 使用:
sslmode=prefer这只允许本地实验继续,形成 EX20-CLIENT-PROXY-NO-TLS。生产 connection
identity、TLS verification 与 secret rotation 在第 23 章完成,不能复制
这个选择。
20.5.3 pg_rewind、重建与时间线验证
旧 primary 为什么不能直接 start
failover 后:
new primary writes new timeline
old primary data directory may contain divergent old-timeline changes把旧 primary 直接作为 standby 指向新 primary,不能自动擦掉分叉块。需要 让它的数据目录重新成为 chosen history 的一致副本。
路径:
pg_rewind
or
fresh base backup / reinitializepg_rewind 做什么
pg_rewind
比较 source 与 target timeline history,找到 divergence point,把 target
中发生变化的 relation block 和必要文件对齐到 source。
典型角色:
source
chosen current primary / authoritative history
target
stopped old primary to be converted into standby不要把方向写反。
前提
target 需要:
data checksums enabled
or wal_log_hints=on
and full_page_writes=on还要有足够 WAL 到 divergence point,或可从 archive 取回。
本章捕获:
wal_log_hints=on
full_page_writes=on
data checksums enabled from chapter 19
Patroni use_pg_rewind=true它们证明前提意图,不证明某次 unplanned divergence rewind 已执行成功。 本章 healthy switchover 由 Patroni 正常 demote/rejoin,没有把手工 rewind 作为正式动作。
rewind 不是无风险修复
官方文档警告:若 pg_rewind 中途失败,target data directory 很可能不再
可恢复,推荐重新 base backup。
因此:
never run on the chosen source directory
stop target
verify identities/direction
retain diagnostic evidence
ensure backup/rebuild path
do not repeatedly retry partial rewind blindly它还会复制 source 的配置文件;重新作为 standby 前要检查 recovery 与 节点特有配置,避免再次启动为错误角色。
rewind 与 rebuild 的选择
| 条件 | 倾向 |
|---|---|
| 大库、小分叉、前提/WAL完整 | rewind |
| target integrity 可疑 | rebuild |
| rewind 失败 | rebuild |
| 缺失 divergence WAL 且 archive 无 | rebuild |
| 节点需要顺便换盘/版本 | rebuild |
| source authority 不清 | 两者都停止 |
“rewind 更快”不能压过 lineage safety。
rejoin acceptance
旧 member 重新加入后检查:
pg_is_in_recovery()=true
system identifier matches
receiver streams from chosen primary
Patroni state=streaming
current cluster timeline correct
replay gap inside bound
corresponding primary slot active
service selectors correct
no direct write path remains如果只是 systemctl 变绿,还没有完成。
restore redundancy
failover 后服务可能恢复,但 resilience 降级:
one primary
one fewer eligible replica
slot/WAL growing
backup schedule disrupted
capacity concentratedincident completion 应区分:
service restored
data reconciled
member rejoined
redundancy restored
root cause/remediation complete不要在“新主可写”时过早关 incident。
时间线验证
本章正式 sequence:
before system S, timeline 5
after forward system S, timeline 6
after restore system S, timeline 7同时每 phase:
Patroni one primary
SQL primary current WAL timeline agrees
two WAL receivers point to that primary
two sender/slot identities match replicaspg-test-3 checkpoint timeline 最终仍为 3,不阻止其在 Patroni timeline 7
上 streaming;详见 20.2.2。
emergency restore 只在单一安全状态触发
本章 drill.py 若 forward 后出错,只会在确认:
pg-test-2 is sole healthy leader时尝试切回 pg-test-1。若 topology ambiguous,它不会猜。自动 cleanup
不得为了“恢复初始状态”制造第二次错误历史。
切换状态机检查表
[ ] exact target and authority
[ ] unique current leader
[ ] explicit eligible candidate
[ ] data protection policy known
[ ] client service and transaction probe
[ ] controlled action
[ ] new timeline + one system identifier
[ ] old primary streaming/rebuilt
[ ] acknowledged/unknown outcomes reconciled
[ ] redundancy restored
[ ] exceptions and next gates recorded小结
switchover assumes health
failover handles unhealthy state and may lose data
service sessions do not migrate
connection retry != business retry
unknown commit needs token lookup
role baseline can return while timeline advances
rewind has direction, prerequisites, and failure risk
service restored != redundancy restored权威参考
上一节:选主、DCS 与防脑裂 · 返回本章目录 · 下一节:交付并观察 HA 集群 · 查看全书目录 · 查看索引中心