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

适用版本: 6.8-8.9

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

recovery was canceled reason [reason] 表示某个分片恢复流程已经被框架主动终止。取消的原因会被拼进异常消息里;如果取消时分片已经关闭,还可能转成 IndexShardClosedException

常见现象 #

  • 分片恢复进行到一半突然中断。
  • 同一时间窗口可能伴随节点离线、分片关闭、重新分配或新的恢复流程启动。
  • 日志中的 reason 往往直接告诉你取消触发条件,例如 shard closed、node left、relocation restarted。

典型报错与异常栈 #

recovery was canceled reason [shard closed]

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

恢复流程内部使用 CancellableThreads 管理可中断任务。一旦恢复上下文失效,比如分片关闭、节点离开、恢复目标变化、上层任务取消,线程会触发取消回调并抛出该异常。换句话说,这更多是在描述“恢复不该继续了”,而不是某个具体 I/O 操作失败。

常见触发原因包括:

  • 分片在恢复中被关闭或索引被关闭。
  • 节点离开集群,恢复目标发生变化。
  • 新的恢复流程替换了旧流程。
  • 上层任务取消,导致底层恢复一并终止。

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

  1. 先查看异常中的 reason 文本,它通常是最直接的线索。
  2. 查看分片和恢复状态:
GET /_recovery?active_only=true&detailed=true
GET /_cat/shards?v
  1. 检查失败时间点内是否有节点重启、索引关闭、reroute、手动取消任务等动作。
  2. 如果恢复是快照恢复链路的一部分,同时检查仓库访问和恢复源节点状态。
  3. 若恢复频繁被取消,重点排查是否存在集群不稳定或分片频繁重分配。

排查时需要注意的问题 #

  • 这是恢复流程的“终止通知”,真正的上游原因可能在更早的日志里。
  • reason 文本非常关键,不要忽略。
  • 如果分片已关闭,再重试恢复前要先确认索引和分片状态是否允许继续。

4. 如何解决这个错误 #

常用修复思路 #

  • 先解决触发取消的上游事件,例如节点不稳定、索引误关闭、恢复来源变化。
  • 避免在节点变更或索引维护窗口中叠加大规模恢复操作。
  • 若取消来自人工操作或自动化任务,优化流程编排,减少恢复中的状态切换。
  • 集群稳定后重新发起恢复,并确认目标分片不再频繁变化。

相关 Elasticsearch API #

  • GET /_recovery?active_only=true&detailed=true:查看当前恢复状态。
  • GET /_cat/shards?v:查看分片所在节点和状态。
  • GET /_tasks:排查是否有恢复相关任务被取消或替换。

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

  • INFINI Console 可将恢复失败、节点事件和索引状态变化聚合展示。
  • INFINI Gateway 适合观察是否有自动化系统频繁触发取消或重试。

5. 小结 #

recovery was canceled reason [reason] 说明恢复流程已经被系统认定不应继续。处理它的关键不是只看这条异常,而是结合 reason、分片状态和节点事件,找到谁让当前恢复上下文失效了。

相关错误 #

附:日志上下文 #

cancellableThreads.setOnCancel((reason, beforeCancelEx) -> {
    final RuntimeException e;
    if (shard.state() == IndexShardState.CLOSED) {
        e = new IndexShardClosedException(shard.shardId(), "shard is closed and recovery was canceled reason [" + reason + "]");
    } else {
        e = new CancellableThreads.ExecutionCancelledException("recovery was canceled reason [" + reason + "]");
    }
    if (beforeCancelEx != null) {
        e.addSuppressed(beforeCancelEx);
    }
    IOUtils.closeWhileHandlingException(releaseResources, () -> future.onFailure(e));
});