medcl/esm

跨集群迁移索引数据不一致

lianmeng0 opened this issue · 22 comments

从集群5.6.4迁移索引到7.17.4,迁移界面是成功的,数据也是全部写入了,但是在7.17.4集群查询时,发现文档数不一致。ESM使用的是0.6.1版本。但是当多次运行迁移程序后,数据可以成功补全。
image
第一次迁移:
image
image
多次迁移后:
image
image

请问是怎么回事儿?还请指教。谢谢!

medcl commented

查看之前,有手动刷新么?

medcl commented

应该是 bulk 请求的部分文档失败了,esm 没有处理 429 的情况。体积一般是需要合并,或者和 mapping 有关系。

我加入了 SortField, TruncateOutFile, SkipFields 等配置信息, 可以通过导出到本地文件的方式进行比较.
而且似乎代码中以前使用 fasthttp 等 http 库似乎有问题, 我简单的想通过 --regenerate_id --repeat_times=10 来生成测试数据也不行. 而更改 http 使用方式后自己就好了(我没有改这部分的实现). 这些一起体现在 #84 里.

示例:

   esm --sort=_id --source=http://localhost:9200 --src_indexes=bank --truncate_output --skip=_index --output_file=src.json
   esm --sort=_id --source=http://localhost:9200 --src_indexes=bank_sync --truncate_output --skip=_index --output_file=dst.json
   diff -W 200 -ry --suppress-common-lines src.json dst.json

如果是为了生成测试数据,可以使用 Loadgen 来做:https://infinilabs.com/docs/latest/gateway/getting-started/benchmark/

你好!那该如何解决或者去避免 bulk 请求的部分文档失败?现有跨集群迁移索引,相同模板中的索引,小索引可以正常迁移,但是大索引却发现文档数相差太大。
小索引:
企业微信截图_20240318145932

大索引:
企业微信截图_20240318150206

@lianmeng0 如果是因为 429 部分失败,可以尝试避免产生太大的压力,尽管迁移速度会慢一些,但是不会失败。
本工具只适合小规模迁移,比较复杂的数据迁移,可能需要完整的解决方案,极限网关的迁移功能处理了 bulk 的返回处理,可以试试

个人建议:

  1. 尝试使用 --output_file + --fields 的功能, 将所有 records 的 id 等特殊字段导出到文件, 然后通过 diff/Beyond Compare 等工具比较到底是哪些记录没有被同步.
  2. 然后通过 --source_proxy 和 --dest_proxy= 指定本地的 fiddler 等工具抓包; 通过 --query 指定没有同步的数据 , 然后通过 fiddler, 日志 等查看出错的原因.

然后将使用的 esm版本 + 详细命令 + fiddler + 日志等 贴上来. 帮着分析是代码的问题还是使用方式的问题.

另外, 可以试试 v0.7.0 版本中提供的 --sync 功能. 我用之前的版本同步, 也发现有数据丢失的现象. 重构代码后, 再也没有发现丢失数据的现象.

个人建议:

  1. 尝试使用 --output_file + --fields 的功能, 将所有 records 的 id 等特殊字段导出到文件, 然后通过 diff/Beyond Compare 等工具比较到底是哪些记录没有被同步.
  2. 然后通过 --source_proxy 和 --dest_proxy= 指定本地的 fiddler 等工具抓包;通过 --query 指定没有同步的数据 , 然后通过 fiddler, 日志 等查看出错的原因.

然后将使用的 esm版本 + 详细命令 + fiddler + 日志等 贴上来.帮着分析是代码的问题还是使用方式的问题.

另外, 可以试试 v0.7.0 版本中提供的 --sync 功能.我用之前的版本同步, 也发现有数据丢失的现象.重构代码后, 再也没有发现丢失数据的现象.

使用新版本后,发现不能够跨版本迁移
使用命令:/usr/local/esm/bin/esm -s http://10.10.8.50:9201 -x umedia_correction_sd_www_202212 -d http://10.10.7.51:8201 -y umedia_correction_sd_www_202212 -w 10 -b 20 -u "_doc" -c 5000 --sliced_scroll_size=10 --buffer_count=50000 -t 180m --refresh

返回报错:
[03-19 18:35:53] [INF] [ migrator.go:115 ,ParseEsApi] source es version: 5.6.4
[03-19 18:35:53] [ERR] [ v5.go:75 ,NewScroll] server error: {"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"Fielddata access on the _uid field is disallowed"}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"umedia_correction_sd_www_202212","node":"lmCdTd3aRESZwTswuGtfcQ","reason":{"type":"illegal_argument_exception","reason":"Fielddata access on the _uid field is disallowed"}}]},"status":400}
[03-19 18:35:53] [ERR] [ main.go:144 ,main] server error: {"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"Fielddata access on the _uid field is disallowed"}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"umedia_correction_sd_www_202212","node":"lmCdTd3aRESZwTswuGtfcQ","reason":{"type":"illegal_argument_exception","reason":"Fielddata access on the _uid field is disallowed"}}]},"status":400}

看起来是类似 https://stackoverflow.com/questions/45427887/how-to-get-the-maximum-id-value-in-elasticsearch 的原因.
因为 --sync 功能需要排序, 所以默认采用了 _id(我只通过 docker 尝试并验证了 6.x 及以后的版本, 确实没有测试 5.x 版本) ,
你可以尝试加入 --sort= ( 表示不指定排序字段 ), 应该可以规避这个问题.

如果想使用 --sync 功能的话, 必须指定排序字段. 如果你的 src/dst 数据中有唯一可以排序的字段(比如你们自己的业务 id), 可以尝试以下命令(最好先在一些不重要的 index 上测试):

/usr/local/esm/bin/esm --sync --sort="你的业务ID" -s http://10.10.8.50:9201 -x umedia_correction_sd_www_202212 
  -d http://10.10.7.51:8201 -y umedia_correction_sd_www_202212 -u "_doc"

看起来是类似 https://stackoverflow.com/questions/45427887/how-to-get-the-maximum-id-value-in-elasticsearch 的原因. 因为 --sync 功能需要排序, 所以默认采用了 _id(我只通过 docker 尝试并验证了 6.x 及以后的版本, 确实没有测试 5.x 版本) , 你可以尝试加入 --sort= ( 表示不指定排序字段 ), 应该可以规避这个问题.

如果想使用 --sync 功能的话, 必须指定排序字段. 如果你的 src/dst 数据中有唯一可以排序的字段(比如你们自己的业务 id), 可以尝试以下命令(最好先在一些不重要的 index 上测试):

/usr/local/esm/bin/esm --sync --sort="你的业务ID" -s http://10.10.8.50:9201 -x umedia_correction_sd_www_202212 
  -d http://10.10.7.51:8201 -y umedia_correction_sd_www_202212 -u "_doc"

你好!本次运行中却又出现其它问题
image
它并没有明显的错误信息就直接退出运行程序,我甚至都无从下手去找出问题所在

你这很奇怪. 按我执行 --sync 的效果,应该有类似如下的日志, 但你的完全看不到.

[03-20 00:02:13] [INF] [ migrator.go:115 ,ParseEsApi] source es version: 7.17.9
[03-20 00:02:13] [INF] [ migrator.go:115 ,ParseEsApi] dest es version: 6.2.2
[03-20 00:02:14] [INF] [ migrator.go:424 ,SyncBetweenIndex] src total count=34023
[03-20 00:02:22] [INF] [ migrator.go:575 ,SyncBetweenIndex] src-index-202402(34023) to dest-index-202402(33963), add=60, update=0, delete=0
  1. 你执行命令的方式? 应该是没有使用 --sync 方式 ?
  2. esm 的版本( 可以执行 esm -h 并贴上结果),
  3. 确认并确保 core dump 能生成( 但从日志来看, 你的执行应该没有core)
  4. 增加 --log=info 参数, 并且通过重定向(或 tee)的方式, 将完整日志保存下来. 比如 /usr/local/esm/bin/esm 各种参数 >> sync_es.log 2>&1 , 并提交.

你这很奇怪. 按我执行 --sync 的效果,应该有类似如下的日志, 但你的完全看不到.

[03-20 00:02:13] [INF] [ migrator.go:115 ,ParseEsApi] source es version: 7.17.9
[03-20 00:02:13] [INF] [ migrator.go:115 ,ParseEsApi] dest es version: 6.2.2
[03-20 00:02:14] [INF] [ migrator.go:424 ,SyncBetweenIndex] src total count=34023
[03-20 00:02:22] [INF] [ migrator.go:575 ,SyncBetweenIndex] src-index-202402(34023) to dest-index-202402(33963), add=60, update=0, delete=0
  1. 你执行命令的方式? 应该是没有使用 --sync 方式 ?
  2. esm 的版本( 可以执行 esm -h 并贴上结果),
  3. 确认并确保 core dump 能生成( 但从日志来看, 你的执行应该没有core)
  4. 增加 --log=info 参数, 并且通过重定向(或 tee)的方式, 将完整日志保存下来. 比如 /usr/local/esm/bin/esm 各种参数 >> sync_es.log 2>&1 , 并提交.

你好!非常抱歉,又来打搅您了。本次按你的要求添加各种参数去执行迁移命令。
这是ESM截图
企业微信截图_20240325103045
使用的命令:
/usr/local/esm/bin/esm7 --sync --sort="id" -s http://10.10.8.50:9201 -x umedia_net_manage_content_check_www_201801 -d http://10.10.7.51:8201 -y umedia_net_manage_content_check_www_201801 -w 10 -b 20 -u "_doc" -c 5000 --sliced_scroll_size=5 --buffer_count=300000 -t 180m --log=info --refresh >> sync_es.log 2>&1
但是运行后却是报错:
{"index":{"_index":"umedia_net_manage_content_check_www_201801","_type":"doc","_id":"0445309d381d38fa858bd8de299f2528","status":400,"error":{"type":"illegal_argument_exception","reason":"Invalid type: expecting [_doc] but got [doc]"}}}
然后就是程序停止,日志输出也停止:
企业微信截图_20240325113340
企业微信截图_20240325113633

经过确认, 是在增加 --sync 功能时, 没有支持 -u 参数, 且没有在 es 5.x 的版本上测试.
可以下载以下编译好的 esm 进行测试(注意: 不要使用 -w/-b/-c 等参数, 在 sync 功能中没有使用, 且会造成数据错误), 如果确认 OK 的话, 我会提交 PR.

esm-linux-amd64.zip
esm-windows-amd64.zip

自测方式( docker )

    1. 创建 es 5.6.4 的 docker 作为 src , 并引入数据(官方的 accounts )
docker run -ti --rm -p 9200:9200 -e cluster.name=es_src --name es_src elasticsearch:5.6.4
curl -H "Content-Type: application/json" -X POST "localhost:9200/bank/account/_bulk?pretty" --data-binary  @accounts.json
    1. 创建 es 7.17.0 的 docker 作为 dst ( 没有找到 7.15.1 的镜像)
docker run -ti --rm --privileged --name es_dst
  -e discovery.type=single-node -e discovery.seed_hosts=127.0.0.1 -e cluster.name=es_dst -p 9201:9200
  elasticsearch:7.17.0 bash -c "ulimit -l unlimited && sysctl -w vm.max_map_count=262144 && /usr/local/bin/docker-entrypoint.sh"
    1. 同步数据
esm --sync --sort="account_number" -s http://127.0.0.1:9200 -x bank -d http://127.0.0.1:9201 -y bank -u "_doc"
    1. 导出数据并比较( 排除不同的 _type 字段, 如果完全相同, 则 diff 没有任何输出 )
esm --sort=account_number -s http://localhost:9200 -x bank --truncate_output --skip=_type -o=src.json
esm --sort=account_number -s http://localhost:9201 -x bank --truncate_output --skip=_type -o=dst.json
diff -W 200 -ry --suppress-common-lines src.json dst.json

经过确认, 是在增加 --sync 功能时, 没有支持 -u 参数, 且没有在 es 5.x 的版本上测试. 可以下载以下编译好的 esm 进行测试(注意: 不要使用 -w/-b/-c 等参数, 在 sync 功能中没有使用, 且会造成数据错误), 如果确认 OK 的话, 我会提交 PR.

esm-linux-amd64.zip esm-windows-amd64.zip

自测方式( docker )

    1. 创建 es 5.6.4 的 docker 作为 src , 并引入数据(官方的 accounts )
docker run -ti --rm -p 9200:9200 -e cluster.name=es_src --name es_src elasticsearch:5.6.4
curl -H "Content-Type: application/json" -X POST "localhost:9200/bank/account/_bulk?pretty" --data-binary  @accounts.json
    1. 创建 es 7.17.0 的 docker 作为 dst ( 没有找到 7.15.1 的镜像)
docker run -ti --rm --privileged --name es_dst
  -e discovery.type=single-node -e discovery.seed_hosts=127.0.0.1 -e cluster.name=es_dst -p 9201:9200
  elasticsearch:7.17.0 bash -c "ulimit -l unlimited && sysctl -w vm.max_map_count=262144 && /usr/local/bin/docker-entrypoint.sh"
    1. 同步数据
esm --sync --sort="account_number" -s http://127.0.0.1:9200 -x bank -d http://127.0.0.1:9201 -y bank -u "_doc"
    1. 导出数据并比较( 排除不同的 _type 字段, 如果完全相同, 则 diff 没有任何输出 )
esm --sort=account_number -s http://localhost:9200 -x bank --truncate_output --skip=_type -o=src.json
esm --sort=account_number -s http://localhost:9201 -x bank --truncate_output --skip=_type -o=dst.json
diff -W 200 -ry --suppress-common-lines src.json dst.json

你好!使用该方法依然报错,这是报错截图:
image
这是使用的命令:
/root/esm-linux-amd64 --sync --sort="id" -s http://10.10.8.51:9201 -x umedia_net_manage_content_check_www_202205 -d http://10.10.7.52:8201 -y umedia_net_manage_content_check_www_202205 -u "_doc"

我确定我这边的服务链接正常,集群稳定

@lianmeng0 从出错状态和代码来看, 是向服务器发送 更新请求以后, 返回状态不对( 非 200), 而且没有从服务器读到响应信息( server error 后为 空).
看是否可以提供 tcpdump 对 dest es 发送请求的抓包情况.
根据你的执行情况, 在执行 esm 前,另开一个终端, 执行以下命令(以下是 centos 的抓包命令, 如果提示错误, 可先自行度娘解决):

  sudo /sbin/tcpdump -nn -vvv -i any "host 10.10.7.52 and port 8201" -w dst_es.pcap 

然后执行 esm 后, 将 dst_es.pcap 提供( 注意: 其大小应该大于 24 字节, 表示抓到了包 ).
如果觉得数据不能通过 github 这种公开的渠道提供, 可以给我发邮件: fishjam@163.com

可以使用这个新的包测试一下.
使用方式:

  1. 推荐: 将之前已经同步过的目标 index 删除, 因为采用 --sync 时使用了完全不同的方式, 有可能以前同步过的数据会冲突.
  2. 使用命令( 更改了 --sort 参数, 拆分成了 --ssort 和 --dsort 从而支持两者 es5/es7 不一样的排序字段 )
  3. 如果失败,可能会生成一个名为 esm.log 的文件,记录了 error 日志, 可以提供其最后的内容.
./esm-linux-amd64 --sync 
  --ssort="id" -s http://10.10.8.51:9201/ -x umedia_net_manage_content_check_www_202205 
  --dsort="id" -d http://10.10.7.52:8201/ -y umedia_net_manage_content_check_www_202205 -u "_doc"

esm-linux-amd64.zip

可以使用这个新的包测试一下. 使用方式:

  1. 推荐: 将之前已经同步过的目标 index 删除, 因为采用 --sync 时使用了完全不同的方式, 有可能以前同步过的数据会冲突.
  2. 使用命令( 更改了 --sort 参数, 拆分成了 --ssort 和 --dsort 从而支持两者 es5/es7 不一样的排序字段 )
  3. 如果失败,可能会生成一个名为 esm.log 的文件,记录了 error 日志, 可以提供其最后的内容.
./esm-linux-amd64 --sync 
  --ssort="id" -s http://10.10.8.51:9201/ -x umedia_net_manage_content_check_www_202205 
  --dsort="id" -d http://10.10.7.52:8201/ -y umedia_net_manage_content_check_www_202205 -u "_doc"

esm-linux-amd64.zip

你好!这是测试产生的error日志最后的内容:
net/http: HTTP/1.x transport connection broken: write tcp 10.10.7.55:36710->10.10.8.51:9201: write: connection reset by peer
[04-01 17:50:08] [ERR] [ v0.go:414 ,DeleteScroll] server error: code=400, length=-1, info={"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"request [//_search/scroll] contains unrecognized parameter: [scroll_id]"}],"type":"illegal_argument_exception","reason":"request [//_search/scroll] contains unrecognized parameter: [scroll_id]"},"status":400}
[04-01 17:50:08] [WRN] [ verify.go:54 ,checkAndHandleError] E:/CodeGo/fishjam-esm/migrator.go:594: (SyncBetweenIndex) FAIL(*errors.errorString), msg=server error: code=400, length=-1, info={"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"request [//_search/scroll] contains unrecognized parameter: [scroll_id]"}],"type":"illegal_argument_exception","reason":"request [//_search/scroll] contains unrecognized parameter: [scroll_id]"},"status":400}
Progress 0 / ? [---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------=] 0s
Source End
[04-01 17:50:08] [INF] [ migrator.go:605 ,SyncBetweenIndex] sync umedia_net_manage_content_check_www_202205(0/0) to umedia_net_manage_content_check_www_202205(0), add=0, update=0, delete=0

全部内容已发送到邮件:fishjam@163.com 还请查收

看来是我给的示例命令有点问题, 改成如下试试 ( 删除了 -s 和 -d 参数后面的最后一个 "/" ),

./esm-linux-amd64 --sync 
  --ssort="id" -s http://10.10.8.51:9201 -x umedia_net_manage_content_check_www_202205 
  --dsort="id" -d http://10.10.7.52:8201 -y umedia_net_manage_content_check_www_202205 -u "_doc"

@lianmeng0 你好,现在怎么样?

@lianmeng0 你好,现在怎么样?

很抱歉,这么久才回复。目前你提供的示例命令运行有误,不知是否是参数问题还是其它,错误如下:
./esm-linux-amd64 --sync
--ssort="id" -s http://10.10.8.51:9201 -x umedia_net_manage_content_check_www_202205
--dsort="id" -d http://10.10.7.52:8201 -y umedia_net_manage_content_check_www_202205 -u "_doc"
当执行命令时,会有如下日志输出:
[04-15 14:37:49] [ERR] [ v0.go:83 ,Bulk] server error: code=413, length=0, info=
[04-15 14:37:49] [WRN] [ verify.go:54 ,checkAndHandleError] E:/CodeGo/fishjam-esm/migrator.go:383: (bulkRecords) FAIL(*errors.errorString), msg=server error: code=413, length=0, info=

添加参数限制 -c 5000 也还是有相同的错误告警
然后还有其它的告警,我是在10.10.9.55主机上运行的命令:
Progress 150000 / 6609750 [===>-----------------------------------------------------------------------------------------------------------------------------------------------------------] 2.27% 2h40m54s[04-15 10:26:41] [ERR] [ http.go:227 ,Request] Post "http://10.10.4.19:8201/_bulk": readfrom tcp 10.10.9.55:57662->10.10.4.19:8201: write tcp 10.10.9.55:57662->10.10.4.19:8201: write: broken pipe
[04-15 10:26:41] [ERR] [ v0.go:83 ,Bulk] Post "http://10.10.4.19:8201/_bulk": readfrom tcp 10.10.9.55:57662->10.10.4.19:8201: write tcp 10.10.9.55:57662->10.10.4.19:8201: write: broken pipe
[04-15 10:26:41] [WRN] [ verify.go:54 ,checkAndHandleError] E:/CodeGo/fishjam-esm/migrator.go:383: (bulkRecords) FAIL(*url.Error), msg=Post "http://10.10.4.19:8201/_bulk": readfrom tcp 10.10.9.55:57662->10.10.4.19:8201: write tcp 10.10.9.55:57662->10.10.4.19:8201: write: broken pipe
当然,命令程序并没有中止,还在正常运行。

这是命令执行后的开始日志:
企业微信截图_20240415102624
这是命令执行后的结束日志:
image
但是在目的集群中并没有索引的文档
image

这是日志中关键的地方:
[04-01 17:50:08] [ERR] [ v0.go:414 ,DeleteScroll] server error: code=400, length=-1, info={"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"request [//_search/scroll] contains unrecognized parameter: [scroll_id]"}],"type":"illegal_argument_exception","reason":"request [//_search/scroll] contains unrecognized parameter: [scroll_id]"},"status":400}

说请求参数不正确,整个日志已发送到你的邮箱,还请注意查收