# 背景

使用 hive jdbc 连接 impala 查询,最近遇到了三个问题,查询表现都是 jdbc 拿到的结果数量不对。

  1. 查询计算成功,在拉取数据的时候,客户端处理数据比较慢,所以拉数据也慢,导致拉取的两批数据之间间隔超过了 impala 默认的设置(FETCH_ROWS_TIMEOUT_MS=10000),impala 报错 session closed,客户端没有获取到这个报错,误认为拉取数据结束,返回给客户端的数据少了很多。这种情况调大 FETCH_ROWS_TIMEOUT_MS 就解决了。
  2. 客户端查询到的结果是空的,看那个查询的 profile,state 显示 FINISHED,status 显示 OK,但是在查询时间线中,Rows available 之后是 Execution cancelled,耗时 10s,正常情况下应该是 First row fetched。在 impala-shell 中无法复现,但是这两个的时间间隔有 3m10s,看执行情况,scan kudu 耗时 1 或 2 分钟,数据却在 4s 的时候准备好了,应该是边向客户端输出,边进行后续的数据扫描,而刚开始的输出可能是空的(其实这个地方不太理解,impala 究竟是怎么处理数据的,有空研究下)。但是 3m10s 的间隔肯定是超出了 10s 的限制,所以尝试调整了 FETCH_ROWS_TIMEOUT_MS 到 10min,问题解决。
  3. 服务端计算完成后,客户端在拉数时,服务端因为行数大小超出了 MAX_ROW_SIZE 大小报错,但是客户端并没有拿到这个失败,误认为服务端计算结果是空的,调大 impala 的 MAX_ROW_SIZE 可解决。

# 排查过程

上述情况都是在拉去数据的过程中 impala 有报错,jdbc 没有获取到错误状态,结束了拉数。用 impala-shell 均不能复现,因此对比了 impala-shell 和 jdbc 发起的 TCP 包。命令分别是:
sudo tcpdump -i lo port 21050 -Xx
sudo tcpdump -i eth0 port 21050 -Xx

两者的区别在于,jdbc fetch 一次数据后就停止了,而 impala-shell 会持续轮询,直到获取到 error 状态。

仔细观察每次 fetch 的返回结果,刚开始状态是 success,且 results 是空的,但是 hasMoreRows 字段是 true:

最后状态变为 error:

而 hive jdbc 在 fetch 结果时,只判断了 results 是否为空,没有判断 hasMoreRows 字段。hive 社区也有相关 jira 和 pr:https://issues.apache.org/jira/browse/HIVE-24861https://github.com/apache/hive/pull/2070/files,这个修改没有提交进去,jira 和 pr 下面也有作者回复。

# 原因分析

impala 依靠 hasMoreRows 这个字段判断是否拉取完成数据。

RETURN_IF_ERROR(
      query_handle->FetchRows(fetch_size, result_set.get(), block_on_wait_time_us));
*num_results = result_set->size();
fetch_results->__isset.results = true;
fetch_results->__set_hasMoreRows(!query_handle->eos());

eos 只有当拉取的行数等于所有行数时,才为 true。

eos_.Store(num_rows_fetched_ == all_rows.size());

当 executor 写结果数据报错时,coordinator 获取失败状态有一点点的延迟,所以会出现客户端获取到 results 结果为空,但是 hasMoreRows 为 true 的情况。