#68169·doris

[错误] (fe) 客户端断开连接会导致 active_queries 中的孤立查询卡死,并造成工作负载组队列槽位泄露

作者: GJ100创建于 2026年9月18日更新于 2026年9月18日

问题描述

当客户端应用程序(例如,具有 3-5 秒查询超时的微服务或 Web 后端)由于客户端超时而断开连接,而查询仍在等待 BE(例如,等待行集/删除位图锁或在负载高时在队列中等待),则会发生以下级联故障:

  1. FE 网络层默默移除连接:FE 的 AcceptListener 检测到来自客户端的 TCP FIN/RST,并触发 connection.setCloseListener(...) -> connectScheduler.getConnectPoolMgr().unregisterConnection(context)。连接从 connectionMap 中移除。
  2. 没有发送取消信号ConnectPoolMgr.unregisterConnection()connectionMap 中移除上下文,但 不调用 context.cancelQuery()。没有向 Coordinator 发送取消信号,也未向 BE 节点发送 cancel_plan_fragment RPC。
  3. 查询完全逃避 TimeoutChecker(幽灵查询):FE 的后台 TimeoutCheckercheckTimer)严格迭代 connectionMap.values()。由于连接已在步骤 1 中移除,因此对于该上下文,checkTimeout() 永远不会再次被调用。查询绕过 query_timeout(例如 300 秒),并在 QeProcessorImpl / information_schema.active_queries 中无限期地挂起(观察到运行超过 3400 秒 / 57 分钟,状态为 RUNNING)。
  4. FE 工作线程在 coordBase.getNext() 中卡死:由于查询正在被主动执行,并且 ReadListener.suspendAcceptQuery() 已经暂停了对套接字的读取,因此工作线程仍然处于阻塞状态,等待 BE 结果。由于没有数据写入关闭的套接字,因此不会引发 IOException / EPIPE 来打破循环。
  5. 工作负载组队列槽泄露和集群卡死:由于 Coordinator.close() 从未执行,查询的 QueueToken 从未返回到 QueryQueue。当工作负载组中的所有槽(max_concurrency)都被这些孤儿查询占用时,该工作负载组中的所有后续查询将永远处于 WAIT_IN_QUEUE 状态,直到它们以 查询队列超时 失败为止。

您期望的结果

当客户端断开连接或关闭连接时:

  1. unregisterConnection() 必须立即取消该连接上的所有正在执行的查询。
  2. Coordinator 必须通过 cancel RPC 终止 BE 片段执行,释放工作负载组的 QueueToken,并解除工作线程阻塞。
  3. 查询必须立即从 QeProcessorImplinformation_schema.active_queries 中移除。
  4. 必须及时释放工作负载组的槽,以便排队查询使用。

如何重现问题?

  1. 创建具有严格并发性的工作负载组:
sql
CREATE WORKLOAD GROUP wg_test PROPERTIES ('max_concurrency'='1', 'max_queue_size'='10', 'queue_timeout'='60000');
…