适用版本: 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. 如何排查和解决这个异常和解决这个异常 #
- 先查看异常中的
reason文本,它通常是最直接的线索。 - 查看分片和恢复状态:
GET /_recovery?active_only=true&detailed=true
GET /_cat/shards?v
- 检查失败时间点内是否有节点重启、索引关闭、reroute、手动取消任务等动作。
- 如果恢复是快照恢复链路的一部分,同时检查仓库访问和恢复源节点状态。
- 若恢复频繁被取消,重点排查是否存在集群不稳定或分片频繁重分配。
排查时需要注意的问题 #
- 这是恢复流程的“终止通知”,真正的上游原因可能在更早的日志里。
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、分片状态和节点事件,找到谁让当前恢复上下文失效了。
相关错误 #
- recover snapshot files cancelled:快照恢复文件阶段通常是取消异常的直接下游表现
- failed to fetch index version after copying it over:恢复过程中目录读取失败常会进一步触发取消
- index is unrecoverable:若本地索引已不可恢复,恢复流程最终会被放弃
- cannot restore partial index:恢复被中止后可能留下 partial index 残留
- all shards failed:恢复失败后的上层查询或读请求常表现为全部分片失败
附:日志上下文 #
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));
});





