apache / apache/iotdb

[Bug] Java SDK 自动发现持续选择已断网的 DataNode,导致查询周期性等待连接超时

Open
#18,633 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
6.4k
Forks
1.2k
Avg merge
1d 23h
Merged PRs (30d)
115

Description

## 现象与复现

IoTDB 服务端和 Java SDK 均为 **2.0.10**,Table model,三节点各部署 ConfigNode/DataNode;Schema 三副本,Data 两副本,数据共识为 IoTConsensus。

1. 将节点 A 断网,保持 B/C 正常。等待 `SHOW CLUSTER` 显示 A 为 `Unknown`、B/C 为 `Running`。
2. 此时 `SHOW AVAILABLE URLS` 仍返回 A/B/C 三个地址。
3. 使用原生 `TableSessionPoolBuilder`,初始 `nodeUrls` **只配置 B/C**,设置 `maxSize(1)`、`connectionTimeoutInMs(3000)`,开启 `enableAutoFetch(true)` 和 `enableRedirection(true)`,串行重复执行同一条 `SELECT … LIMIT 5`,完整读取并关闭结果集。

绕过业务适配层仍能复现,12 次查询均成功返回 5 行:

| 设置 | 12 次查询结果 |
| --- | --- |
| 自动发现、重定向均开启 | 第 1/4/7/10 次分别约 2423/2976/3029/3028 ms,其余约 22–29 ms |
| 两项均关闭,其他配置及 SQL 不变 | 全部约 21–30 ms |

节点 A 在整个对照期间保持断网。即使初始地址仅填写 B/C,自动发现仍会将 A 加回查询候选列表。这不是故障发生瞬间的一次切换等待,而是持续的周期性延迟。

## 关键源码

以下链接固定到 **v2.0.11**:对比发现,本问题涉及的关键逻辑仍保留;**尚未在完整 2.0.11 集群实测复现**。

**1. 服务端仅排除 Removing,没有排除 Unknown。**

[`ShowAvailableUrlsTask.buildTsBlock()`](https://github.com/apache/iotdb/blob/v2.0.11/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowAvailableUrlsTask.java#L58-L63):

```java
for (TDataNodeInfo dataNodeInfo : showDataNodesResp.getDataNodesInfoList()) {
String status = dataNodeInfo.getStatus();
if (RegionStatus.Removing.getStatus().equals(status)) {
continue;
}
```

**2. SDK 根据发现列表轮询端点。**

[`NodesSupplier`](https://github.com/apache/iotdb/blob/v2.0.11/iotdb-client/session/src/main/java/org/apache/iotdb/session/NodesSupplier.java) 使用以下策略;`getQueryEndPoint()` 调用 `policy.chooseOne(get())`:

```java
private final QueryEndPointPolicy policy = new RoundRobinPolicy();
```

**3. 连接失败后回落默认连接,但没有在该路径隔离失败端点。**

[`Session.getQuerySessionConnection()`](https://github.com/apache/iotdb/blob/v2.0.11/iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java#L998-L1011),该方法与 2.0.10 完全一致:

```java
endPointToSessionConnection.computeIfAbsent(
endPoint.get(),
k -> {
try {
return constructSessionConnection(this, endPoint.get(), zoneId);
} catch (IoTDBConnectionException ex) {
return null;
}
});
```

`computeIfAbsent` 返回 null 时不会保存映射;方法随后回落到默认连接。故障地址仍留在轮询列表,下次选中时再次尝试连接,和实测“每三次出现一次秒级等待”吻合。

## 期望与希望确认

当一个节点持续不可达且其他节点可完成查询时,希望 SDK 能暂时隔离失败端点并探测恢复,避免后续查询反复承担完整连接超时。

请确认:`SHOW AVAILABLE URLS` 包含 Unknown 是否为预期?此场景是否应由 SDK 增加故障端点退避/隔离,或调整服务端过滤规则?是否已有对应修复或推荐配置?

目前临时通过关闭自动发现、重定向并只配置健康节点规避,但这需要用户手动维护节点列表。

Contributor guide

Open the contributing guide

Research direction

Start with ShowAvailableUrlsTask.java, NodesSupplier.java, and Session.java at the linked v2.0.11 locations, then reproduce the TableSessionPoolBuilder scenario with a disconnected node and compare auto-discovery enabled versus disabled. Trace how unavailable endpoints are selected and how failed connections are handled. Done means the repeated connection-timeout delay is prevented while healthy nodes continue serving queries and recovery remains possible.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
databases, distributed-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.