Archive 保留与清理:到底能删掉多少 log?
archive 卷快满了,有人想删掉旧的 cluster-log 段。archive API 会很配合地让你删。purgeSegments 懂段边界,也懂哪些
recording 正在活动——但它完全不知道你删掉的这些字节,是不是某个节点免于重建的唯一依靠。
本页就是那道缺失的检查。它给出该测哪些位置、哪条不变式决定「重启还是重建」,以及一套失败时也安全的清理流程。 节点已经故障之后该怎么办,见快照失败与恢复;为重建路径兜底的备份, 见备份与灾难恢复。
这个场景归根到底只有一个问题
Section titled “这个场景归根到底只有一个问题”在删任何东西之前,在启动一个已经离线一阵子的节点之前,你回答的其实是同一个问题:这个节点能正常重启,还是必须重建?
| 节点本地状态 | 活的日志源是否覆盖到该节点的位置? | 结论 |
|---|---|---|
| 完整快照集 + 本地 log 连续 | 是(L ≤ P) | 正常重启。 加载本地快照 → 本地回放 → 向 leader 追赶。 |
| 完整快照集 + 本地 log 连续 | 否(L > P) | 重建。 追赶中间有个缺口,重启多少次都补不上。 |
| 无快照,本地 log 从位置 0 起连续 | 是 | 正常重启,全量本地回放。慢,但成立。 |
| 无快照,本地 log 起点不为 0 | —— | 重建,除非这是一份有意 rebase 过、且经过验证的镜像。 |
| 最新快照提交了却加载不起来 | 上一代快照集及其 log 仍在 | 先 invalidate-latest-snapshot,然后重新核对这张表。 |
| 空盘(换过的实例) | 活的日志源从 0 开始都还留着 | 重启能成,但通常比直接用备份provision更慢。 |
| 空盘 | 活的日志源已清理过 0 之后的部分 | 从经过验证的镜像重建。 没有例外。 |
下面全部内容,就是怎么测出 L 和 P,以及怎么在清理时永远不把某个节点从第 1 行推到第 2 行。
按时间做保留决策——「留七天」——只是对真正重要的东西的一种近似,真正重要的是位置。要测的是这几个:
| 记号 | 含义 | 从哪里读 |
|---|---|---|
S | 某个完整快照集所对应的 log 位置 | ClusterTool recovery-plan |
A | 该节点 cluster-log recording 中实际还在盘上的最早位置 | ArchiveTool describe |
P | 重启节点本地可用 log 的末端(append 位置) | ClusterTool recovery-plan / archive 描述 |
L | 所选活日志源能回放或复制的最早位置 | 在那个源上跑 ArchiveTool describe |
C | 清理后 recording 的新起点(也就是切点) | 你的清理方案 |
一个完整快照集是一份 consensus module 快照(serviceId = -1)加上每个已配置 service 各一份快照,且全部处于同一
leadership term 和同一 log 位置。少了一个 service 的快照集不是「退路」——它什么都不是。
由此直接得到两条不变式。
本地快照恢复成立的条件:
A ≤ S ≤ P滞后节点能追上的条件:
L ≤ P又因为清理只能按段边界进行,本地实际可用的切点是:
C = 满足 C ≤ S 的最大有效段边界千万别把 C 向上取到 S 之后。本地恢复的回放正是从快照位置开始的;把 S 处的 log 拿走,你精心保留的那个快照就成了废物。
选一个保留模型,并且写下来
Section titled “选一个保留模型,并且写下来”上面的不变式带来一个策略后果,而多数团队会一直把它留在默认状态,直到某次故障逼着面对。请明确地选:
模型 A —— 任何已配置成员都能正常重启。 对每个成员,你都保留一份完整且经过验证的快照集、此后的本地 log,以及活日志源上 足以从该成员最后一次确认的 append 位置接续的那段 log。你的清理永远不会越过任何离线成员的恢复位置。这会限制你回收磁盘的力度, 而且一个长期离线的节点就能把整个集群的保留策略绑住。
模型 B —— 窗口之外的节点一律重建。 你只保留够最近重启用的量,跑一份持续验证的 ClusterBackup,更老的 log 就放掉。
落在窗口之外的节点直接重新归类为重建,而不是重启。
对可替换的云实例来说,模型 B 才是合理的默认值,而且这个模型更诚实——它把备份变成了承重结构,也逼着你必须去测。模型 A 则是在替一个 几周没人检查过的节点,悄悄许下「能正常重启」的承诺。
启动滞后节点之前:go/no-go 关卡
Section titled “启动滞后节点之前:go/no-go 关卡”「文件都在,那就启动吧」——20 分钟的重建就是这样变成 4 小时故障的。先跑下面这套,保持节点停止。
1. 收集事实
Section titled “1. 收集事实”AERON_ALL_JAR=/opt/aeron/aeron-all.jarCLUSTER_DIR=/var/lib/my-cluster/clusterARCHIVE_DIR=/var/lib/my-cluster/archiveSERVICE_COUNT=1
java -cp "$AERON_ALL_JAR" io.aeron.cluster.ClusterTool "$CLUSTER_DIR" recording-logjava -cp "$AERON_ALL_JAR" io.aeron.cluster.ClusterTool "$CLUSTER_DIR" recovery-plan "$SERVICE_COUNT"java -cp "$AERON_ALL_JAR" io.aeron.archive.ArchiveTool "$ARCHIVE_DIR" describe记下 S、A、P、L,再加上 H(当前集群 commit 位置)和 B(最新一份经过验证的备份镜像的位置)。然后确认:恢复计划
选中的每个 recording 都真的存在;快照集覆盖了 consensus module 和每一个 service;本地 log 在将要回放的那段范围内连续;
成员 ID、集群 ID、service 数量、应用版本和各个 endpoint 都正确。
2. 给每条可行路径估算工作量
Section titled “2. 给每条可行路径估算工作量”本地回放量 = P - S (有快照)全量本地回放量 = P - A (无快照)leader 传输 / 追赶 = H - P备份镜像的尾部追赶 = H - B这些是字节范围,不是时间——本地 service 回放通常受 CPU/应用限制,而 archive 复制受存储或网络限制,所以要把它们换算成时间, 得靠你自己恢复演练量出来的吞吐。把那些数字留着:回放 位置/秒、恢复 字节/秒、复制 字节/秒。
最新的基线并不自动等于最快的基线。 一个只落后 20 分钟、但回放要按应用速度走的节点,可能反而输给一份老了一小时、 却能按磁盘速度恢复的备份镜像。选「准备 + 回放 + 传输 + 校验」总和最低的那条。
3. 出现下列任一情况,直接否掉正常启动这条路
Section titled “3. 出现下列任一情况,直接否掉正常启动这条路”恢复计划选中的快照 recording 不可用或本地回放范围不连续或 L > P或没有快照,且 A 并不代表一个有效的初始状态4. 启动之前先定好放弃阈值
Section titled “4. 启动之前先定好放弃阈值”提前把放弃条件定下来,因为故障中途的乐观情绪正是吃掉时间窗口的东西:
- 在一个恢复超时周期内,恢复位置毫无推进;
- 反复回到同一个选举状态;
- 请求的回放位置早于源 recording 的起点;
- archive 报告 recording 未知或段不可用;
- 预估完成时间已经超出你的 RTO。
恢复进行期间,暂停清理任务,不要让两个 follower 同时冲同一个 leader archive 恢复,也不要去限速你正指望着的那条复制链路。
5. 盯对的信号
Section titled “5. 盯对的信号”盯选举状态、本地复制与回放位置、append 位置、commit 位置、剩余差距 H − 本地位置,以及 archive/cluster 错误计数器——
计数器和告警规则见指标与告警。成功的标准是 FOLLOWER_READY、位置收敛
与应用状态校验全部通过,而不是「进程还活着」。
你会依次经过的三个 follower 状态:
具体会跑到哪几个阶段,取决于节点的位置——比较新的节点可能直接进追赶,滞后的节点三个阶段都要走。
安全的清理流程
Section titled “安全的清理流程”purgeSegments 只保证两件事,而且都写在它自己的 javadoc 里:新起点「必须是现有起点之后某个段的首字节位置」,
以及「正在录制或正在被回放的段是无法分离的」。这两条都不是恢复层面的检查。下面这套流程才是。
阶段 1 —— 先立一个恢复点
Section titled “阶段 1 —— 先立一个恢复点”触发一次集群快照,等所有需要支持本地正常重启的成员都完成,并在每个成员上确认拿到了完整快照集。记录每份快照的位置和
recording ID。然后验证这些快照 recording 真的能回放出来——recording.log 里有条目只是元数据,不是证据。对备份也做同样的事,
并记下它的位置。
阶段 2 —— 算出边界
Section titled “阶段 2 —— 算出边界”每个成员保留的最旧完整快照位置记为 S_member;每个被许诺可以正常重启的离线成员,其最后一次确认的 append 位置记为
P_member。
C_live ≤ min(所有被许诺正常追赶的成员的 P_member)C_member ≤ S_member向下取到一个有效的段边界。如果某个离线节点落在了拟定切点之后,就把选择摊开来做:要么推迟这次清理,要么撤回「可以正常重启」 这个承诺、把该节点标记为待重建。别让算术悄悄替你决定。
阶段 3 —— 空跑
Section titled “阶段 3 —— 空跑”动存储之前先产出一份清理清单:集群与成员 ID、cluster-log recording ID、当前起止位置、拟定的新起点、保留下来的各个快照集、 被保护的最旧那个可正常重启成员的位置、备份位置与最近一次成功恢复测试的日期,以及预计会变成可分离状态的确切段文件。
出现下列情况就否掉这次清理:没有保留任何完整的本地快照集;切点落在 S 之后;某个受保护的滞后节点位置早于 C_live;
在模型 B 下备份缺失或未经测试;选中的 recording 中有正在活动或正被回放的;catalog、recording.log 与实际 recording 三者不一致。
阶段 4 —— 保守地执行
Section titled “阶段 4 —— 保守地执行”全程保住 quorum,并且一次只动一个节点:
- 干净地停掉一个 follower;
- 备份它的 cluster 元数据和 archive catalog;
- 把段分离到算好的边界;
- 校验 archive 和恢复计划;
- 只有校验通过之后,才删除已分离的段;
- 重启该 follower,等到
FOLLOWER_READY; - 校验 service 状态和位置收敛,然后再动下一个 follower。
leader 放到最后处理,等 leadership 已经转移,或者放进一个受控的维护窗口里。
永远不要把文件系统删除当成清理手段。走 archive API,好让 catalog 和 recording 起点保持一致——对段文件 rm 一下,
catalog 就还在描述已经不存在的数据了。
阶段 5 —— 证明恢复可行
Section titled “阶段 5 —— 证明恢复可行”磁盘腾出来了不算成功。在退掉上一代之前,要测试:当前节点能否重启;处于所支持最旧滞后位置的节点能否重启;能否回退到上一代保留快照; 能否从备份重建一台空主机;以及追赶完成后应用状态是否一致。
到底哪些能删
Section titled “到底哪些能删”| 对象 | 规则 |
|---|---|
| cluster-log 段 | 在算好的安全边界之前可以分离,前提是满足上面那些不变式。只要 recording.log 还在引用某条 recording,就绝不要把它整个清掉。 |
| 快照 recording | 要删的完整代必须比所有保留退路都旧,并且不再被目标恢复计划或任何恢复流程引用。存储允许就保留两个完整代。绝不要只留某一代里的部分 service。 |
| 孤立的快照 recording | 失败快照留下的、从未写进 recording.log 的 recording,在确认没有恢复元数据引用它们之后可以回收。 |
recording.log | 恢复元数据,不是批量数据。不要跟着段一起清,也不要手工改。 |
按位置做自动化,别按磁盘用量
Section titled “按位置做自动化,别按磁盘用量”时间或剩余空间策略可以提议一个切点;但只有位置检查有权批准它:
候选切点 = 受下列因素约束的段边界: 保留下来的本地快照位置、 受保护的滞后成员位置、 备份 / 重建策略、 recording 与回放的活动状态除了集群计数器之外,还要盯 archive recording 的起止位置、每个成员最新两个完整快照 的位置、备份状态与年龄、磁盘耗尽的预计时间,以及最近一次空节点恢复测试成功的日期。这最后一项指标才是关键——它能告诉你 模型 B 到底是不是真的可靠。
具体到 EC2 部署:如果希望实例替换之后本地恢复能力还在,就把 archive 和 cluster 状态放到持久化存储上;否则就宣布实例是
一次性的,并把备份 provision 做进 bootstrap 流程。至少跑一份持续验证的 ClusterBackup,放在集群故障域之外。另外,
除非你有意支持「从 0 全量 log」恢复、或者那台主机已经先 provision 过,否则绝不要在空主机上复用成员 ID。
这些如何影响你的数字
Section titled “这些如何影响你的数字”- RTO 几乎完全由你选了哪个恢复基线决定,而这个选择又被你几周前清理掉的东西约束着。
P − S(回放距离)是你靠提高快照 频率来控制的那个数;H − B是你靠备份新鲜度来控制的那个。 - p99 / 最大延迟 只受间接影响,但这条联系是真实的:阶段 1 要求做一次集群快照,而在 OSS 里所有成员都在同一 log 位置 打快照,于是一次保留周期就会给热路径带来一次 stop-the-world 停顿。在繁忙的撮合引擎上,这会落在最大延迟上——如果你用 Premium,这就是standby 快照的理由;如果没用,那就把清理窗口排到低峰期。
- 吞吐量 不受清理本身影响,但一个正在恢复的 follower 会和实时流量抢 leader 的 archive 回放、磁盘和网络。同时恢复两个, 就是把一次恢复变成一次吞吐故障的做法。
- 清理语义逐字引自
AeronArchive.java中purgeSegments、detachSegments、deleteDetachedSegments的 javadoc(「must be first byte position of a segment after the existing start position」;「not possible to detach segments which are active for recording or being replayed」),服务端的强制检查在ArchiveConductor.purgeSegments(isValidDetach/isDeleteAllowed)。 - follower 追赶状态取自
ElectionState.java(FOLLOWER_LOG_REPLICATION(9)、FOLLOWER_REPLAY(10)、FOLLOWER_CATCHUP(13)、FOLLOWER_READY(16))与Election.java; 启动时从本地元数据恢复见ConsensusModuleAgent.java, 恢复计划的构建与校验见RecordingLog.java。 seed-recording-log-from-snapshot把位置 rebase 到零、以及recording.log.bak备份,连同recording-log/recovery-plan/invalidate-latest-snapshot各命令,均出自ClusterTool.java。 archive 侧工具(describe、verify、compact、delete-orphaned-segments、mark-invalid)出自ArchiveTool.java。- 运维指引:Cluster Backup 与 Operating Aeron Cluster。
- 背景:The Aeron Files。
本站与 Adaptive Financial Consulting Limited 或 Aeron 项目无任何关联,未获其背书或赞助。 Aeron 是 Adaptive Financial Consulting Limited 的注册商标。
Aeron 是 Adaptive Financial Consulting Limited 在英国及其他国家/地区的商标。