我已经写信给一个实用工具,重新索引特定的文件,原来是放在错误的elasticsearch索引。我试图理解,尽管同一批中的其他文档(可能由于同样的原因而失败)成功地重新编制了索引,但关于未能重新编制特定文档索引的报告似乎是虚假的,或者至少是不一致的。
背景
我的公司有一个系统,可以创建从现场设备接收到的事件的每日索引。由于设计上的误解,一个特定事件被写入的索引是由它到达我们系统的日期决定的;而实际要求是将事件放入与其发生日期相对应的索引中。
对于超过99%的事件,这两个时间产生相同的日期(因此也产生相同的索引)。大多数例外情况是在第n天和第n+1天的utc午夜附近发生的事件。还有一小部分是由连接或其他故障引起的,这些故障可能导致事件任意延迟(几天或几周)传递到系统。
我在go中编写了一个实用程序,用于查找这些异常事件文档并将其移动到它们所属的索引中。它使用 github.com/olivere/elastic
包(v6.2.26)与elasticsearch群集交互。
大多数情况下,这个实用程序工作得很好,但偶尔(而且越来越多地)它会以 400 Bad Request
错误来自 _reindex
操作,仅为特定日期的批处理中的事件的子集重新编制索引。
不幸的是 elastic
当包返回来自的错误时,它不会返回响应主体 ReindexService.Do
,所以我不得不修改本文中的代码,截取响应主体并将其记录在http客户机中,以查看详细信息。
问题
下面是一些与此错误的典型事件相关联的日志行(为便于阅读而手动重新格式化)。 message_id
是一个64位纳秒分辨率的unix历元,编码为十六进制字符串,表示事件发生的时间。
在这种特殊情况下,由于Map冲突(无法更改字段的类型),十二个批中的两个事件文档不会重新编制索引:
findEventTime event_time="2019-05-19 13:45:52.562001331 +0000 UTC"
event_ts=15a0198a3b5815b3
message_id=15a0198a3b5815b3
event time event_time="2019-05-19T13:45:52.562001331Z"
events_date=2019-05-19
source_index=event-store-2019-06-25
...
ES request method=POST
req_body="{\"dest\":{
\"index\":\"event-store-2019-05-19\",
\"op_type\":\"create\",
\"version_type\":\"external\"
},\"source\":{
\"index\":\"event-store-2019-06-25\",
\"query\":{
\"range\":{
\"message_id\":{
\"from\":\"159fec78e07d0000\",
\"include_lower\":true,
\"include_upper\":false,
\"to\":\"15a03b0d71cc0000\"}}}}}"
url="http://events.es.yoyodyne.com/_reindex"
ES response rsp_body="{\"took\":183,
\"timed_out\":false,
\"total\":12,
\"updated\":0,
\"created\":10,
\"deleted\":0,
\"batches\":1,
\"version_conflicts\":0,
\"noops\":0,
\"retries\":{\"bulk\":0,\"search\":0},
\"throttled_millis\":0,
\"requests_per_second\":-1.0,
\"throttled_until_millis\":0,
\"failures\":[
{\"index\":\"event-store-2019-05-19\",
\"type\":\"_doc\",
\"id\":\"5eadba4f-dd1f-4141-8fdb-4dc9e363b9ff\",
\"cause\":{\"type\":\"illegal_argument_exception\",
\"reason\":\"mapper [params.severity_metrics.torque.value] cannot be changed from type [long] to [float]\"},
\"status\":400},
{\"index\":\"event-store-2019-05-19\",
\"type\":\"_doc\",
\"id\":\"8070134f-5f2b-4cbe-85a7-c4636c1f529f\",
\"cause\":{\"type\":\"illegal_argument_exception\",
\"reason\":\"mapper [params.severity_metrics.torque.value] cannot be changed from type [long] to [float]\"},
\"status\":400}]}"
status=400
现在,这两个事件确实有 params.severity_metrics.torque.value
是虚线小数,因此表面上报告的错误似乎合理:
$ ./esq.sh -q -I event-store-2019-06-25 -c params.severity_metrics.torque -e id 8070134f-5f2b-4cbe-85a7-c4636c1f529f | jq '.hits.hits[] | ._source.params.severity_metrics'
{
"torque": {
"severity": "med",
"value": 3.9
}
}
$ ./esq.sh -q -I event-store-2019-06-25 -c params.severity_metrics.torque -e id 5eadba4f-dd1f-4141-8fdb-4dc9e363b9ff | jq '.hits.hits[] | ._source.params.severity_metrics'
{
"torque": {
"severity": "med",
"value": 4.4
}
}
然而。。。
1在这十份成功编入索引的文件中 _reindex
手术,其中五个也有 params.severity_metrics.torque.value
是虚线小数(其他五个具有 0
):
$./esq.sh -q -I event-store-2019-06-25 -z 12 -c id -c params.severity_metrics.torque \
-r message_id gte 159fec78e07d0000 lt 15a03b0d71cc0000 | jq -c '.hits.hits[] | {id: ._source.id, severity_metrics: ._source.params.severity_metrics}'
{"id":"5eadba4f-dd1f-4141-8fdb-4dc9e363b9ff","severity_metrics":{"torque":{"severity":"med","value":4.4}}}
{"id":"d9274787-58c8-46bf-93ad-dd9d0d3d854a","severity_metrics":{"torque":{"severity":"high","value":29.5}}}
{"id":"1b5a2072-8b88-4a90-a12b-91db171f6210","severity_metrics":{"torque":{"severity":"none","value":0}}}
{"id":"a8e20619-d4c6-44f2-8e9a-16826e933892","severity_metrics":{"torque":{"severity":"high","value":13.3}}}
{"id":"96e951fe-2841-4d69-b7d6-36c31201ff3d","severity_metrics":{"torque":{"severity":"high","value":31.3}}}
{"id":"d63141d5-1714-4caa-9542-5860c0eb0881","severity_metrics":{"torque":{"severity":"none","value":0}}}
{"id":"432fecec-762f-4a66-81ef-3071b3544e70","severity_metrics":{"torque":{"severity":"med","value":3.5}}}
{"id":"85eb8492-7e4f-4441-b747-d5333b322992","severity_metrics":{"torque":{"severity":"high","value":381.4}}}
{"id":"8070134f-5f2b-4cbe-85a7-c4636c1f529f","severity_metrics":{"torque":{"severity":"med","value":3.9}}}
{"id":"398dee7e-4160-4697-9a2e-41e1c00c7381","severity_metrics":{"torque":{"severity":"none","value":0}}}
{"id":"5304ef1f-1160-4ed4-9423-f9cf672f7ddf","severity_metrics":{"torque":{"severity":"none","value":0}}}
{"id":"11fadf45-71d4-4d7a-b92e-9f1515ead36f","severity_metrics":{"torque":{"severity":"none","value":0}}}
$ ./esq.sh -q -I event-store-2019-05-19 -z 12 -c id -c params.severity_metrics.torque \
-s -e id d9274787-58c8-46bf-93ad-dd9d0d3d854a \
-e id a8e20619-d4c6-44f2-8e9a-16826e933892 \
-e id 96e951fe-2841-4d69-b7d6-36c31201ff3d \
-e id 432fecec-762f-4a66-81ef-3071b3544e70 \
-e id 85eb8492-7e4f-4441-b747-d5333b322992 | jq -c '.hits.hits[] | {id: ._source.id, severity_metrics: ._source.params.severity_metrics}'
{"id":"d9274787-58c8-46bf-93ad-dd9d0d3d854a","severity_metrics":{"torque":{"severity":"high","value":29.5}}}
{"id":"a8e20619-d4c6-44f2-8e9a-16826e933892","severity_metrics":{"torque":{"severity":"high","value":13.3}}}
{"id":"96e951fe-2841-4d69-b7d6-36c31201ff3d","severity_metrics":{"torque":{"severity":"high","value":31.3}}}
{"id":"432fecec-762f-4a66-81ef-3071b3544e70","severity_metrics":{"torque":{"severity":"med","value":3.5}}}
{"id":"85eb8492-7e4f-4441-b747-d5333b322992","severity_metrics":{"torque":{"severity":"high","value":381.4}}}
那为什么 _reindex
重新索引行动(而不是抱怨)那五份文件?
2源索引和目标索引的Map都同意所讨论的字段是 long
:
$ curl -s $ESHOST/event-store-2019-06-25/_mapping | jq '.["event-store-2019-06-25"] | .mappings._doc.properties.params.properties.severity_metrics.properties.torque'
{
"properties": {
"severity": {
"type": "keyword"
},
"value": {
"type": "long"
}
}
}
$ curl -s $ESHOST/event-store-2019-05-19/_mapping | jq '.["event-store-2019-05-19"] | .mappings._doc.properties.params.properties.severity_metrics.properties.torque'
{
"properties": {
"severity": {
"type": "keyword"
},
"value": {
"type": "long"
}
}
}
这当然提出了一个问题:“那些 params.severity_metrics.torque.value
作为小数点存在于源索引中 event-store-2019-06-25
首先呢?”
三。如果我手动从源索引中删除成功重新编制索引的十个文档,然后重新运行我的实用程序,则 _reindex
操作愉快地接受它在上一次运行中拒绝的两个文档,并毫无怨言地将它们添加到目标索引中。世界跆拳道联盟?
那么:这一切怎么可能呢?关于Map或 _reindex
能合理解释我所看到的所有这些(看似矛盾的)症状的手术?
顺便说一句,如果你想知道, esq.sh
是一个本地脚本,用于简化elasticsearch查询的组合。上述选项包括:
-I <index-name>
-c <source-field>
-e <name> <val> (equality)
-r <name> <op> <val> [<op> <valu>...] (range)
-q (quiet; suppress echo of formatted request body)
-s (should; roughly logical-OR)
-z <num> (size)
更新:有些事情现在更清楚了。。。
对乔大喊大叫,因为他让我看到了一些事情:
首先,除了说明 params
是一个 object
,并且——通过一个Map模板——任何 params.*
其值为字符串的对象将存储/索引为 keyword
,这些索引的Map规范没有说明如何存储/索引 params.*
具有其他类型的值。
{
"mappings":{
"_doc":{
"properties":{
...
"params":{
"type":"object"
},
...
},
"dynamic_templates":[
{
"Keyword Params":{
"mapping":{
"type":"keyword"
},
"match_mapping_type":"string",
"path_match":"params.*"
},
...
]
}
}
}
(这是有一定意义的,因为我们的事件来自20多种不同的消息类型;虽然不同消息类型的参数有一些共同点,但也有很多不同之处。我不是100%确定,但我认为我们的应用程序通常不会对数字消息参数值进行聚合,甚至查询,因此目前我不愿意干扰现有的Map定义。我猜最初的设计人员并不关心索引中的数字参数值会被表示成什么样子,而只关心字符串类型的参数值不会被分析。)
显然,这个关于数值参数类型的(非)决定意味着elasticsearch根据它看到的进入索引的字段的第一个值来选择特定索引中字段的类型。而且由于json封送数字的方式(如果它是零,则忽略小数部分),如果一个整数值(通常是0)是该参数到达索引的第一个值,则默认情况下,该参数将是一个 long
,否则将成为 float
.
这也为我澄清了这样一个想法:json文档源中的值的表示在某种程度上独立于elasticsearch索引/存储的Map值。这是完全合理的(如果有点混乱)看到源代码值,如 4.4
以及 3.9
对于将这些值存储为 4
以及 3
. 这就是(我假设)为什么第一次被索引的原始文档不会引起elasticsearch的任何抱怨,即使参数值的清单表示如 4.4
接收索引的类型Map无法完全容纳该字段。
在重新编制索引的情况下,我还可以看到,如果源索引中字段的Map类型与目标索引中字段的Map类型不同,elasticsearch将如何(或者无论如何可能)标记错误(当然,这是假设 _reindex
操作在尝试将源索引类型信息重新索引到目标中时,会携带源索引类型信息,而不仅仅是索引原始json源文档。)
所以,现在有些方面的情况更清楚了。但我还是不明白为什么会出现这样的错误:
对于一批中的某些文档,但对于看起来有相同问题的其他文档,
当源索引和目标索引都同意字段的类型时
以及为什么它们不会在重试相同文档时发生。
更新2:进入 java 兔洞
我对报告的错误做了更多的搜索(“mapper cannot be changed from type”),找到了这篇包含函数链接的文章( MappedFieldType.checkTypeName
)这会产生错误消息(elasticsearch 6.7的链接)。
所以elasticsearch的某个地方认为 params.severity_metrics.torque.value
特定传入文档的字段应为 float
,即使对于同一批中的其他文档,已经确定它是 long
.
这让我开始思考在不同的碎片中可能会发生什么,这让我想到了这个(2014)elasticsearch问题#8688(“Map更新应该是同步的”)。
这个问题已经解决了,但我现在看到的似乎仍然有可能是这个问题解决的结果。也就是说,如果存在冲突,那么两个Map中只有一个能够获胜,而那些失败的Map最终会出现在响应主体中 failures
数组。
可能是这样,也可能不是这样,但似乎并不是特别与shard相关,因为我的示例中的两个失败文档之一(值为 4.4
)作为不同文档路由到同一个shard(0)(值为 29.5
)一开始就成功了 _reindex
请求。
$ curl -s events.es.yoyodyne.com/event-store-2019-05-19/_search?pretty -H "Content-Type: application/json" -d '{
"explain": true,
"query": {
"bool": {
"should": [
{ "term": { "id": "8070134f-5f2b-4cbe-85a7-c4636c1f529f" } }, <=== failed
{ "term": { "id": "5eadba4f-dd1f-4141-8fdb-4dc9e363b9ff" } }, <=== failed
{ "term": { "id": "d9274787-58c8-46bf-93ad-dd9d0d3d854a" } },
{ "term": { "id": "a8e20619-d4c6-44f2-8e9a-16826e933892" } },
{ "term": { "id": "96e951fe-2841-4d69-b7d6-36c31201ff3d" } },
{ "term": { "id": "432fecec-762f-4a66-81ef-3071b3544e70" } },
{ "term": { "id": "85eb8492-7e4f-4441-b747-d5333b322992" } }
]
}
}
}' | jq -c '.hits.hits[] | [._id, ._shard, ._source.params.severity_metrics.torque.value]'
["432fecec-762f-4a66-81ef-3071b3544e70","[event-store-2019-05-19][2]",3.5]
["85eb8492-7e4f-4441-b747-d5333b322992","[event-store-2019-05-19][2]",381.4]
["d9274787-58c8-46bf-93ad-dd9d0d3d854a","[event-store-2019-05-19][0]",29.5] <=== succeeded (shard 0)
["5eadba4f-dd1f-4141-8fdb-4dc9e363b9ff","[event-store-2019-05-19][0]",4.4] <=== failed (shard 0)
["8070134f-5f2b-4cbe-85a7-c4636c1f529f","[event-store-2019-05-19][3]",3.9] <=== failed (shard 3)
["a8e20619-d4c6-44f2-8e9a-16826e933892","[event-store-2019-05-19][1]",13.3]
["96e951fe-2841-4d69-b7d6-36c31201ff3d","[event-store-2019-05-19][1]",31.3]
我已经克隆了elasticsearch repo,但它当然是个怪物,我对请求如何通过elasticsearch没有任何基础;另外,我每年只看一次java代码(如果我幸运的话!)。因此,我怀疑我是否能够花必要的时间在源代码中找到这个问题。
也许有了解elasticsearch的人可以帮忙?
1条答案
按热度按时间ie3xauqp1#
我无法复制这个(测试w/
7.2.0
).然而,能缓解这种状况的是环境
coerce: true
在long
Map。现在,这可能是一个(合理的)遗留决定的问题,但是
long
因为这样的价值观似乎没有道理。你有十进制值,比如4.4
以及3.9
我猜你会对它们做些数学计算。在当前Map下,即使是最简单的数值agg,sum,最终会去掉小数部分(强制
long
)给你一个7
.所以我推荐一个
double
(一点也不float
--试试看为什么)。让我们看看这个看似莫名其妙的问题是否会再次出现。