备份与恢复
日志是 Wow 服务保存的唯一事实。快照、投影的视图、数仓和搜索索引都派生自日志,都可以重建;无法重建的,是日志以及同一个数据库中位于它旁边的少量状态。备份计划保护的是这些,其余一切都按重建来处理。
备份什么
每个服务的数据库(PostgreSQL 或 MongoDB)的一份一致的备份,用数据库自己的工具获取。它包含:
| 数据 | 为什么无法重建 |
|---|---|
| 日志:每个聚合已提交的流,以及各分区的计数器 | 它就是历史本身 |
| 预留 | 哪个聚合持有每个唯一键 |
| 定时器 | 为稍后安排的命令 |
| 失败台账 | 订阅停放的、等待运维人员处理的聚合 |
| 检查点和租约 | 每个订阅在日志中的位置,以及哪个节点拥有哪个分区 |
| 格式记录 | 每张表或每个集合创建时所用的格式 |
把它们一起备份:只备份日志而不备份检查点,恢复出来的订阅不知道自己读到了哪里。PostgreSQL 和 MongoDB 列出了这些表和集合。
快照保存在同一个数据库中,数据库备份也会包含它们:与日志一起恢复时,它们与日志一致。不过,它们不是备份要保护的对象,恢复也从不需要它们是最新的。
不备份什么
- 保存在自己存储中的投影视图:它们的订阅会从日志重新写出。
- 保存在别处的快照,例如在 Elasticsearch 上:从日志重建。
- 数仓:ClickHouse Sink 会从日志重新写出(ClickHouse)。
- 服务发布的 Kafka topic:它们为其他上下文携带的是服务提交的副本。
重建比恢复慢,所以大型读模型为了速度仍然值得单独备份。但它的正确性从不依赖备份。
恢复读模型
丢失或出错的投影视图、快照表或搜索索引要重建,而不是从备份恢复(重建与重放):
- 投影:以新的订阅名部署它,它会从日志开头开始,追上之后再把读取方切换到它的新视图;
- 快照:从日志重建,可以针对选定的聚合,也可以针对新的存储;
- 数仓和搜索索引:从头启动它们的 Sink 订阅。
在此期间,服务照常处理命令。
恢复数据库
当服务的数据库丢失时:
- 停止服务的每个节点。 任何节点都不能写入正在恢复的数据库。
- 恢复数据库最近一份一致的备份。
- 启动节点。 订阅从恢复出的检查点继续,定时器触发,停放的聚合在台账中等待。
- 重建保存在数据库之外的读模型。 根据恢复后的日志中已不存在的提交写出的视图或索引,领先于日志;按上面的方式重建它。
备份之后的提交随之丢失。发送这些提交的调用方得到了成功的回答,其他上下文也可能已经从发布的事件流中读到了它们,所以恢复到更早的时间点是一个影响超出这个服务的决定:恢复到数据库所能提供的最近时间点,如果数据库有持续归档就利用它,并告知读取该服务事件流的那些上下文的负责人。
测试恢复
备份由一次恢复来证明:恢复到一个隔离的环境中,在其上启动一个节点,读取几个聚合的状态,并向它们发送一条命令,这条命令必须在下一个版本追加。