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

适用版本: 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. 如何排查和解决这个异常和解决这个异常 #

建议按下面的顺序排查:

  1. 确认报错对应的是哪个 index 和 shard。
  2. 查看该 shard 当前状态,确认它是否处于 closed 或相关切换阶段。
  3. 回看同一时间窗口内是否有人为 close、索引维护、节点迁移或恢复操作。
  4. 检查 CCR/恢复脚本是否存在并发顺序问题。
  5. 如果 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 操作相互打架。

相关错误 #

附:日志上下文 #

下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:

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);