compatible-with=7 失效,ES 9.0 移除了 REST API V7 兼容

举报
岳涛 发表于 2026/10/20 12:29:09 2026/10/20
【摘要】 ES 的 REST API 有一套版本兼容机制:请求头里带 compatible-with=8……

ES 的 REST API 有一套版本兼容机制:请求头里带 compatible-with=8,9.x 会尽量按 8.x 的格式处理请求和响应。9.0 把这套机制里的 V7 分支删掉了。官方 compatibility 文档对这套机制的定位写得很直白——“只跨一个大版本”,而源码里的校验是:请求头声明的版本低于上一个大版本时直接返回 400,并告诉你只接受 9 或 8。所以 9.x 上带 compatible-with=7 的请求是请求失败,不是被忽略。依赖 RestApiVersion.V_7 这个枚举的插件则是编译不过。

说明

实测环境:本地起的 Elasticsearch 9.4.2 单节点实例(官方 tar 包直接跑,堆内存 512MB,安全与机器学习关闭),curl 连本地 9200 端口。本文「实测」小节的命令和输出都出自这个实例。

官方条目

9.0 的 changelog 标为 breaking(9.0.0.yml 第 849–859 行 @ v9.4.2),area: Infra/Core,PR 118103(不是 118104,那个是移除 _knn_search)。

details:

This PR removes all references to V_7 in the Rest API. V7 features marked for deprecation have been removed.

impact:

This change is breaking for any external plugins/clients that rely on the V_7 enum or deprecated version 7 functionality

两个受影响的群体:

  • 引用 RestApiVersion.V_7 这个 Java 枚举的插件;
  • 依赖已弃用的 V7 功能的客户端,主要是还带着 compatible-with=7 请求头的那些。

这套兼容机制是干什么的

官方 compatibility 文档(compatibility.md 第 11–26 行 @ v9.4.2)的说法:为了让 REST 客户端缓解 breaking change 的影响,ES 提供按请求开启的兼容模式;ES 会尽量套用上一个版本的 URL、请求体、响应体和 HTTP 参数。文档同时给了两条限定:

For compatible APIs, this has no effect— it only impacts calls to APIs that have breaking changes from the previous version. An error can still be returned in compatibility mode if Elasticsearch cannot automatically resolve the incompatibilities.

REST API compatibility does not guarantee the same behavior as the prior version. It instructs Elasticsearch to automatically resolve any incompatibilities so the request can be processed instead of returning an error.

对使用者来说,它的价值是让升级不必"大爆炸":集群先升,客户端分批改。升级期间客户端继续声明上一个版本,拿到旧格式响应,等自己的代码改好了再切。

请求头怎么写(第 39–51 行):Accept 必须有,Content-Type 只在带请求体时需要;对 8.x 或 9.x 的服务端,合法值是 compatible-with=8。另外可以用环境变量 ELASTIC_CLIENT_APIVERSIONING=true 给所有请求开启兼容模式。

Accept: application/vnd.elasticsearch+json; compatible-with=8
Content-Type: application/vnd.elasticsearch+json; compatible-with=8

9.x 上带 compatible-with=7 会怎样

这一条原文标了"未实测、未找到说明",其实源码里写得很清楚。

RestApiVersion 枚举在 v9.4.2 里只剩两个值(RestApiVersion.java 第 21–23 行):

V_9(9),
V_8(8);

minimumSupported 返回的是 V_8(第 46–48 行)。请求头解析在 RestCompatibleVersionHelper 第 34–58 行:从 Accept 和 Content-Type 里各取出 compatible-with 的数值,然后校验。Accept 那一段:

// accept version must be current or prior
if (acceptVersion > RestApiVersion.current.major || acceptVersion < RestApiVersion.minimumSupported.major) {
    throw new ElasticsearchStatusException(
        "Accept version must be either version {} or {}, but found {}. Accept={}", RestStatus.BAD_REQUEST, ...

current 是 9、minimumSupported 是 8,声明 7 小于 8,所以直接抛 ElasticsearchStatusException,HTTP 状态码 400,消息里会写明接受 9 或 8、实际收到 7。带请求体时 Content-Type 那段(第 53–58 行)有同样的校验。这个解析由 RestRequest 第 145 行调用。

结论:是请求直接失败,且报错里说清了允许哪两个值,不是静默忽略。 这个失败方式比忽略好——调用方立刻知道要改。

还有两条同一段代码里的行为,用这个头时都要注意:

  • 两个头都带 compatible-with 时,值必须一致,否则 400(第 64–73 行);只带其中一个也会 400(第 75–83 行);
  • compatible-with 的值走 Byte.parseByte(第 100–106 行),写非数字会抛 NumberFormatException。

被删的是"隔了两代"的那一档制本身

机制还在。v9.4.2 的 BaseRestHandler 第 108–115 行仍按 request.getRestApiVersion 决定哪些响应参数合法:

.filter(p -> responseParams(request.getRestApiVersion).contains(p) == false)
...
candidateParams.addAll(responseParams(request.getRestApiVersion));

删掉的是 V_7 这一档和它对应的已弃用功能。这个取舍在官方文档里有明确表述(compatibility.md 第 26 行):

REST API compatibility should be a bridge to smooth out the upgrade process, not a long term strategy. REST API compatibility is only honored across one major version: honor 8.x requests/responses from 9.x.

"只跨一个大版本"是写进文档的定位。9.0 移除 7.x 兼容是按这个定位执行的,不是意外。

同一篇文档还有一处滞后::api-conventions.md 第 485–497 行(链接)至今还写着"对 8.0 声明 7.x 格式"的例子,compatible-with=7 在 9.x 上已经不合法。这类文档滞后在 breaking 变更里很常见,判断行为请以源码和 compatibility 文档为准。

谁会受影响

插件开发者:改的是引用 V_7 的 Java 代码。

枚举常量没了,编译不过。这个失败方式是好事:编译期暴露,不会等到运行时。改法是换成 RestApiVersion.V_8 或按当前版本处理请求。

客户端:请求会报 400。

还在发 compatible-with=7 的客户端,在 9.x 上每个请求都会失败。改法是把值改成 8(如果目标是过渡)或者干脆去掉这个头。

排查办法:

grep -rn "compatible-with" --include=*.py --include=*.go --include=*.java --include=*.js --include=*.sh .

只看自己代码还不够。兼容头可能在依赖里:

  • 老的官方 SDK 可能内置了这个头。官方客户端支持开启兼容模式(compatibility.md 第 53 行),升级前要确认 SDK 版本与目标 ES 版本的匹配声明;
  • 有些 SDK 用环境变量 ELASTIC_CLIENT_APIVERSIONING 统一开启,这种情况 grep 代码找不到,要看部署环境。

这个头该怎么用

按文档给的定位用:

  • 声明的是"我当前用的格式版本",不是"服务器版本"。文档原话是(api-conventions.md 第 491 行):把 compatible-with 设成与你正在运行的版本相同的主版本没有即时效果,但能保证 ES 升级之后请求仍然可用;
  • 升级窗口内声明上一个大版本,争取时间改客户端;
  • 兼容模式生效时,ES 会往 deprecation 日志写 compatible_api 类别的记录(compatibility.md 第 28 行)。查这个类别能找出哪些调用还在依赖兼容模式,作为改造的清单;
  • 别把它当长期方案。声明 7 的客户端从 8 升到 9 就会遇到这次的移除;而且"只跨一个大版本"本来就写在文档里。

和"渐进式升级"的关系

这套兼容机制服务于一个目标:让升级不必要求所有调用方在同一时刻改完。bulk 严格解析(bulk 严格化)官方给了 v8 兼容模式过渡、_source mode 设置迁移(_source.mode)建议新旧双写过渡,都是同一思路:给你时间,但不是永久。

compatible-with 是同一思路在 REST 接口层面的实现。规律也一样:过渡期给一代,下一代移除,移除是计划内的。

在 9.4.2 上跑一遍

同一个请求,只差一个请求头:

v7-compat

图 4:本地 9.4.2 单节点实例上的实测输出。上:不带版本头;下:带 compatible-with=7

curl -s -o /dev/null -w 'HTTP %{http_code}\n' ""

curl -s -H 'Accept: application/vnd.elasticsearch+json;compatible-with=7' \
  "" \
  | jq -r '"HTTP \(.status): \(.error.caused_by.reason)"'

不带版本头是 HTTP 200;带上 compatible-with=7 返回 400,原因是 Accept version must be either version 9 or 8, but found 7。

注意这句原因来自 caused_by,外层的 root_cause 只写 Invalid media-type value on headers [Accept, Content-Type]——只看外层那句会以为是请求头格式写错了,容易往编码、分号、大小写上排查。

结论是:这类 400 的定位顺序是先看 caused_by 再看 root_cause;SDK 自动加的头,用 curl 手打一次就能确认,比翻 SDK 源码快。

写在最后

这条 breaking 只影响两类对象:引用 RestApiVersion.V_7 的插件(编译不过),和还发着 compatible-with=7 的客户端(400)。前者编译期就能发现,后者报错里会告诉你允许 9 或 8。

值得记下来的是这套兼容机制的定位:只跨一个大版本。用它争取时间可以,把"去掉兼容头、按新格式解析"作为改造任务的验收标准之一,否则下一代的移除会再次遇到。

排查时别忘了依赖:兼容头可能在 SDK 里,也可能由 ELASTIC_CLIENT_APIVERSIONING 统一开启,grep 自己的代码找不到。

参考

本文对照 Elasticsearch v9.4.2 标签的 changelog、官方文档与源码核对,引文与代码逐字取自原文。9.x 上带 compatible-with=7 返回 400 这一结论已在本地 9.4.2 单节点实例上用 curl 复现(图 4)。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。