[错误] (fe) 客户端断开连接会导致 active_queries 中的孤立查询卡死,并造成工作负载组队列槽位泄露
作者: GJ100创建于 2026年9月18日更新于 2026年9月18日
问题描述
当客户端应用程序(例如,具有 3-5 秒查询超时的微服务或 Web 后端)由于客户端超时而断开连接,而查询仍在等待 BE(例如,等待行集/删除位图锁或在负载高时在队列中等待),则会发生以下级联故障:
- FE 网络层默默移除连接:FE 的
AcceptListener检测到来自客户端的 TCP FIN/RST,并触发connection.setCloseListener(...)->connectScheduler.getConnectPoolMgr().unregisterConnection(context)。连接从connectionMap中移除。 - 没有发送取消信号:
ConnectPoolMgr.unregisterConnection()从connectionMap中移除上下文,但 不调用context.cancelQuery()。没有向Coordinator发送取消信号,也未向 BE 节点发送cancel_plan_fragmentRPC。 - 查询完全逃避
TimeoutChecker(幽灵查询):FE 的后台TimeoutChecker(checkTimer)严格迭代connectionMap.values()。由于连接已在步骤 1 中移除,因此对于该上下文,checkTimeout()永远不会再次被调用。查询绕过query_timeout(例如 300 秒),并在QeProcessorImpl/information_schema.active_queries中无限期地挂起(观察到运行超过 3400 秒 / 57 分钟,状态为RUNNING)。 - FE 工作线程在
coordBase.getNext()中卡死:由于查询正在被主动执行,并且ReadListener.suspendAcceptQuery()已经暂停了对套接字的读取,因此工作线程仍然处于阻塞状态,等待 BE 结果。由于没有数据写入关闭的套接字,因此不会引发IOException/EPIPE来打破循环。 - 工作负载组队列槽泄露和集群卡死:由于
Coordinator.close()从未执行,查询的QueueToken从未返回到QueryQueue。当工作负载组中的所有槽(max_concurrency)都被这些孤儿查询占用时,该工作负载组中的所有后续查询将永远处于WAIT_IN_QUEUE状态,直到它们以查询队列超时失败为止。
您期望的结果
当客户端断开连接或关闭连接时:
unregisterConnection()必须立即取消该连接上的所有正在执行的查询。Coordinator必须通过 cancel RPC 终止 BE 片段执行,释放工作负载组的QueueToken,并解除工作线程阻塞。- 查询必须立即从
QeProcessorImpl和information_schema.active_queries中移除。 - 必须及时释放工作负载组的槽,以便排队查询使用。
如何重现问题?
- 创建具有严格并发性的工作负载组:
CREATE WORKLOAD GROUP wg_test PROPERTIES ('max_concurrency'='1', 'max_queue_size'='10', 'queue_timeout'='60000');
…内容来源: apache/doris