📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 7.4-7.4

1. 错误异常的基本描述 #

仓库清理操作开始前,Elasticsearch 会先读取当前 RepositoryData,并确认仓库 generation 与自己预期的 repositoryStateId 一致。如果发现 generation 已变化,说明在 cleanup 真正开始前,仓库已经被其他操作并发修改,于是抛出 concurrent modification of the repository before cleanup started

常见现象 #

  • 执行仓库 cleanup 时失败,但并不是只读仓库或权限问题。
  • 错误信息里会同时出现预期 generation 和实际 generation。
  • 同一时间窗口里通常还有快照创建、删除快照或其他仓库更新操作。

典型报错与异常栈 #

concurrent modification of the repository before cleanup started; expected current generation [41]; actual current generation [42]

2. 为什么会发生这个错误 #

这属于典型的并发保护机制。cleanup 要基于一个稳定的仓库视图工作,如果它准备清理时发现仓库代际已经变化,就说明另一个操作先一步改写了仓库元数据。为了避免在旧视图上错误删除数据,Elasticsearch 会直接中止 cleanup。

常见触发原因包括:

  • cleanup 启动前后,有新的快照写入仓库。
  • 其他任务正在删除快照,导致仓库 generation 前进。
  • 多个自动化任务同时操作同一个仓库。
  • 多集群误共享写权限,导致仓库元数据被外部并发改动。

3. 如何排查和解决这个异常和解决这个异常 #

  1. 检查失败时间窗口内是否有其他仓库操作并发执行。
  2. 查看快照任务状态:
GET /_snapshot/_status
GET /_tasks
  1. 如果仓库由定时任务驱动,检查是否把 cleanup、snapshot、delete snapshot 安排在同一时间段。
  2. 如果同一个仓库可能被多个集群访问,确认是否违反了单写原则。
  3. 重新发起 cleanup 前,先确保仓库 generation 不再持续变化。

排查时需要注意的问题 #

  • 这不是坏事,反而说明 Elasticsearch 阻止了错误清理。
  • 真正要解决的是并发来源,而不是强行重试更多次。
  • 如果 generation 一直快速增长,说明仓库仍在被频繁写入,cleanup 很难稳定完成。

4. 如何解决这个错误 #

常用修复思路 #

  • 将 cleanup 调整到快照创建和删除窗口之外执行。
  • 确保同一仓库只有一个写入来源,其他集群只读访问。
  • 为自动化任务增加互斥控制,避免多个作业同时操作仓库。
  • 待仓库静止后重新执行 cleanup。

相关 Elasticsearch API #

  • POST /_snapshot/{repository}/_cleanup:执行仓库清理。
  • GET /_snapshot/_status:查看当前快照执行状态。
  • GET /_tasks:发现是否还有其他管理任务并发运行。

借助 INFINI 产品提升排障效率 #

  • INFINI Console 有助于把仓库任务时间线和异常日志放在一起分析。
  • INFINI Gateway 可以审计多个自动化来源是否在同一时段对仓库发起了并发操作。

5. 小结 #

concurrent modification of the repository before cleanup started 的本质是 cleanup 前置一致性检查失败。说明仓库在你开始清理前已经被其他操作更新,应该优先治理并发来源,而不是把它当成普通重试类错误处理。

相关错误 #

附:日志上下文 #

throw new RepositoryException(metadata.name(), "cannot run cleanup on readonly repository");
}
final RepositoryData repositoryData = getRepositoryData();
if (repositoryData.getGenId() != repositoryStateId) {
	throw new RepositoryException(metadata.name(), "concurrent modification of the repository before cleanup started; " +
		"expected current generation [" + repositoryStateId + "]; actual current generation ["
		+ repositoryData.getGenId() + "]");
}