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

适用版本: 6.8-8.9

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

failed to build clear scroll entity 表示 Elasticsearch 客户端在发送 clear scroll 请求之前,构建 JSON 请求体时发生了序列化失败。这个错误不是 scroll 上下文清理失败,而是请求尚未到达 Elasticsearch 服务端就在客户端侧中断了。

常见现象 #

  • 应用日志中出现 failed to build clear scroll entity 异常,伴随 IOExceptionElasticsearchException
  • clear scroll 请求未实际发出,服务端未收到任何清除请求,scroll context 可能仍然残留。
  • 批量清除多个 scroll id 时,整个批次失败,导致大量 scroll 上下文无法及时释放。
  • 在 Elasticsearch 服务端日志中不会出现对应记录,因为请求没有到达服务端。

典型报错与异常栈 #

ElasticsearchException: failed to build clear scroll entity
Caused by: java.io.IOException: ...
    at org.elasticsearch.client.RestClient.performRequest(RestClient.java:...)
    at org.elasticsearch.client.RestHighLevelClient.clearScroll(RestHighLevelClient.java:...)
try (XContentBuilder entity = JsonXContent.contentBuilder()) {
    entity.startObject().array("scroll_id", scroll).endObject();
    request.setJsonEntity(Strings.toString(entity));
} catch (IOException e) {
    throw new ElasticsearchException("failed to build clear scroll entity", e);
}

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

该错误的根本原因是 XContentBuilder 在构建 JSON 请求体时抛出了 IOException。常见原因包括:

  • scroll_id 集合为空或 null:传入的 scroll id 列表为 null 或空数组,导致序列化逻辑异常。
  • scroll_id 内容损坏:scroll id 本身被篡改、截断或包含非法字符(如非 UTF-8 编码的字节),XContentBuilder 在写入时抛出 IOException
  • scroll_id 数据量异常膨胀:当 scroll id 列表非常大(例如数万个 id)时,构建 JSON 可能触发内存压力或缓冲区溢出。
  • 序列化上下文异常JsonXContent.contentBuilder() 返回的构建器处于异常状态,或底层 OutputStream 已关闭。
  • 上游传入参数不符合预期:业务代码在维护 scroll id 列表时混入了非 String 类型对象,导致 array("scroll_id", scroll) 调用时类型不匹配。

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

建议按以下步骤排查:

  1. 确认 scroll id 来源:检查传入 clearScroll 的 scroll id 列表是否为空、为 null 或包含非字符串元素。
  2. 缩小复现范围:逐个移除 scroll id,定位是否只有特定 id 会触发异常。
  3. 检查 scroll id 内容:打印 scroll id 的长度和字符编码,确认没有非法字符或异常截断。
  4. 确认客户端版本:低版本 High Level Client 在处理某些边界情况时存在已知问题,升级到较新版本可能解决。
  5. 查看完整异常栈IOException 的 cause 链中通常包含更具体的失败原因(如 JsonGenerationException)。

排查时需要注意的问题 #

  • 该错误发生在客户端侧,服务端日志无法提供直接线索,必须重点查看应用客户端日志
  • 如果使用了自定义封装的 ES 客户端,检查是否在复用 XContentBuilderJsonXContent 实例时引入了并发问题。
  • scroll id 是 Base64 编码的字符串,长度通常在几十到几百字节,如果发现异常长的 id,可能是数据损坏的信号。

4. 如何解决这个错误 #

常用修复思路 #

  • 在调用 clear scroll 前做参数校验
if (scrollIds == null || scrollIds.isEmpty()) {
    return;
}
// 过滤掉非法 id
List<String> validIds = scrollIds.stream()
    .filter(id -> id != null && !id.isEmpty())
    .collect(Collectors.toList());
if (validIds.isEmpty()) {
    return;
}
ClearScrollRequest request = new ClearScrollRequest();
validIds.forEach(request::addScrollId);
client.clearScroll(request, RequestOptions.DEFAULT);
  • 捕获并隔离异常 scroll id:如果只有个别 id 有问题,单独跳过该 id 而不是让整个批次失败:
for (String scrollId : scrollIds) {
    try {
        ClearScrollRequest request = new ClearScrollRequest();
        request.addScrollId(scrollId);
        client.clearScroll(request, RequestOptions.DEFAULT);
    } catch (ElasticsearchException e) {
        logger.warn("Failed to clear scroll id: {}, error: {}", scrollId, e.getMessage());
    }
}
  • 避免手工拼装 JSON:统一使用官方 High Level Client 提供的 ClearScrollRequest API,而不是手动构建 JSON 字符串。
  • 及时清理不再需要的 scroll 上下文:避免 scroll id 列表无限增长,在搜索结束后立即清除。

后续注意事项与推荐建议 #

  • 设置合理的 scroll 超时时间,避免 scroll context 长时间残留占用内存。
  • 对于大数据量分页场景,优先考虑使用 search_after 替代 scroll,避免 scroll context 管理开销。
  • 监控集群的 nodes.stats.indices.search.scroll_current 指标,及时发现 scroll 泄漏。

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

  • INFINI Console 可帮助查看集群 scroll 上下文数量、节点内存使用情况和慢查询趋势,快速判断是否发生了 scroll 泄漏。
  • INFINI Gateway 可部署在 Elasticsearch 前面,对 scroll 相关请求做观测、限流和缓存,帮助定位异常 scroll 请求的来源和模式。

5. 小结 #

failed to build clear scroll entity 是一个典型的客户端侧序列化异常,根源通常在于传入的 scroll id 数据异常或参数校验缺失。处理时重点排查 scroll id 的来源和内容,做好参数校验和异常隔离,避免单个异常 id 导致整个清除操作失败。对于长期运行的大数据量分页场景,建议评估是否可以用 search_after 替代 scroll,从根本上减少 scroll context 管理的复杂度。

相关错误 #

附:日志上下文 #

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

try (XContentBuilder entity = JsonXContent.contentBuilder()) {
    entity.startObject().array("scroll_id", scroll).endObject();
    request.setJsonEntity(Strings.toString(entity));
} catch (IOException e) {
    throw new ElasticsearchException("failed to build clear scroll entity", e);
}