运维
PostgreSQL 故障切换手册
PostgreSQL 故障切换期间为 go-sequent 事件库保持单写者:路由规则、六步清单、应用侧建议,以及库不做的事。
PostgreSQL 故障切换手册
目标
- 事件库使用单写者主库,不出现静默脑裂。
- 使用同步提交或 quorum 时,RPO 接近 0。这是运维层面的选择。
- 只有在可以接受异步延迟的地方,读模型查询才使用从库。
各条路径应连接哪里
| 路径 | 建议 |
|---|---|
| 命令 + SyncInTxn(写事务内同步更新读模型)Projector | 始终连主库。余额需要提交后立刻可读。 |
| 异步 Outbox 消费者(事务内记待发消息,事后轮询投递) | 主库,或对非账本视图可容忍延迟的从库。 |
| Replay 与重建 worker | 主库,或具有一致性快照的专用重建角色。 |
使用 Patroni、Cloud SQL HA、RDS Multi-AZ 或同类方案。前面放 PgBouncer 或云上的连接池,以吸收连接风暴。
切换清单
- 检测主库丢失:通过 Patroni、云健康检查或心跳缺失。
- 提升一个同步备库,确认新主库上
pg_is_in_recovery()返回 false。 - 重新指向连接池或 DSN,或更新服务发现。如果 DSN 被缓存,对应用 Pod 做滚动重启。
- 验证写入:执行一次 go-sequent 追加和一次 SyncInTxn Projector 更新。
- 验证没有双写者:旧主库不得再接受写入。使用 fencing 或 STONITH。
- 重建备库:从新主库重建,并观察复制延迟。
应用侧建议
- 保持数据库事务简短。一次
CommandService.Execute批次就是一个事务。 - 遇到
ErrOptimisticConcurrency(OCC:用 ExpectedVersion 挡住并发写冲突)时带退避重试。切换后的追赶阶段这类错误很常见。 - 不要跨 goroutine 共享
UnitOfWork。 - 只有当该探针不需要 SyncInTxn 路径时,健康检查才应降级为只读。
for attempt := 0; attempt < 5; attempt++ {
err := app.Service.Execute(ctx, cmd)
if !errors.Is(err, eventstore.ErrOptimisticConcurrency) {
return err
}
time.Sleep(backoff(attempt))
}
当每条命令都带 Idempotency 键(同一业务键重复提交只生效一次)时,重试是安全的,见 Idempotency。
go-sequent 不做的事
- 没有多主事件库,也没有 CRDT 合并。
- 库内没有自动 DSN 故障切换,请依赖连接池或平台。
相关
文档随 go-sequent 源码更新;若与 API 不一致,以源码为准。