适用版本: 6.8-8.9
1. 错误异常的基本描述 #
这里的 cannot open ccr restore session if shard closed 是一条非常直接的状态前置检查。源码在为某个分片打开新的 CCR restore session 之前,会先判断 indexShard.state();只要状态是 IndexShardState.CLOSED,就立刻抛出 IndexShardClosedException,不会继续获取 commit 或建立 session。
常见现象 #
- CCR 恢复、复制初始化或相关会话建立动作直接失败。
- 日志里能看到 session 正在尝试为某个 shard 打开,但随后被 closed 状态拦住。
- 常见于索引被关闭、分片正在下线、恢复期间执行了关闭操作,或状态切换尚未完成。
典型报错与异常栈 #
常见日志形态类似下面这样:
IndexShardClosedException[shard [[index][0]] closed, reason [cannot open ccr restore session if shard closed]]
2. 为什么会发生这个错误 #
CCR restore session 需要在一个可用的 shard 上拿到安全提交点并维持会话生命周期。如果 shard 已经关闭,底层文件、engine 和恢复上下文都不再适合建立新会话,因此 Elasticsearch 会直接拒绝,而不是冒险继续。
常见触发场景包括:
- 目标索引或分片已经被手动关闭。
- 集群缩容、重分配或恢复过程里,分片正处于关闭或切换阶段。
- 自动化流程里先触发了 close,再触发 CCR restore session,顺序出现竞争。
- 旧 session 已存在或刚结束,新 session 建立时刚好遇到 shard 状态变化。
3. 如何排查和解决这个异常和解决这个异常 #
建议按下面的顺序排查:
- 确认报错对应的是哪个 index 和 shard。
- 查看该 shard 当前状态,确认它是否处于 closed 或相关切换阶段。
- 回看同一时间窗口内是否有人为 close、索引维护、节点迁移或恢复操作。
- 检查 CCR/恢复脚本是否存在并发顺序问题。
- 如果 shard 应该保持打开,继续追查是谁把它关闭了。
排查时需要注意的问题 #
- 这条错误不是“网络抖动导致会话失败”,而是本地分片状态不允许。
- 如果 shard 频繁在 open/close 间切换,问题根因通常在上游运维流程,而不是 CCR 本身。
- 即使 session UUID 合法,只要 shard 关闭,也不会进入真正的 restore session 建立阶段。
4. 如何解决这个错误 #
常用修复思路 #
- 先确保目标索引和 shard 处于打开且可恢复的状态,再发起 CCR restore session。
- 调整自动化顺序,避免 close/open 操作与恢复会话初始化并发发生。
- 对维护窗口内的索引操作加互斥控制,减少状态竞争。
- 如果 shard 因故障被关闭,先解决底层恢复问题,再重新发起会话建立。
后续注意事项与推荐建议 #
- 为 CCR 相关作业增加 shard state 预检查,避免无意义重试。
- 把关闭索引、迁移分片和恢复会话三类任务分开调度。
- 在高频复制场景里记录 session 建立失败的 shard 状态分布,便于发现流程冲突。
借助 INFINI 产品提升排障效率 #
- INFINI Console 适合查看集群健康度、节点指标、索引状态、错误趋势和请求画像,帮助快速判断异常是局部问题还是系统性问题。
- INFINI Gateway 适合部署在 Elasticsearch 前面做请求观测、限流、熔断、缓存和流量治理,尤其适合定位高频错误请求、异常重试和不合理 DSL。
- 如果需要长期治理,建议把异常日志、慢查询、调用来源和变更记录统一接入监控面板,缩短从“发现问题”到“定位根因”的时间。
5. 小结 #
这条错误的核心非常明确: shard 已关闭,所以 CCR restore session 根本不会被创建。修复重点是让目标分片回到可用状态,并避免恢复流程与 close 操作相互打架。
相关错误 #
- recover-snapshot-files-cancelled - 如何解决此 Elasticsearch 异常
- restore-failed - 如何解决此 Elasticsearch 异常
- failed-to-restore-snapshot-snapshotid-如何解决此Elasticsearch异常
附:日志上下文 #
下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:
logger.debug("not opening new session [{}] as it already exists"; sessionUUID);
restore = onGoingRestores.get(sessionUUID);
} else {
logger.debug("opening session [{}] for shard [{}]"; sessionUUID; indexShard.shardId());
if (indexShard.state() == IndexShardState.CLOSED) {
throw new IndexShardClosedException(indexShard.shardId(); "cannot open ccr restore session if shard closed");
}
final Engine.IndexCommitRef commitRef = indexShard.acquireSafeIndexCommit();
final SetfileNames = Set.copyOf(commitRef.getIndexCommit().getFileNames());
restore = new RestoreSession(sessionUUID; indexShard; commitRef; fileNames; scheduleTimeout(sessionUUID));
onGoingRestores.put(sessionUUID; restore);





