apache / apache/iotdb

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

Đang mở
#18,633 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Java
Star
6.4k
Fork
1.2k
Merge trung bình
1 ngày 23 giờ
Pull request đã merge (30 ngày)
115

Mô tả

## 现象与复现

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 增加故障端点退避/隔离,或调整服务端过滤规则?是否已有对应修复或推荐配置?

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

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Bắt đầu với ShowAvailableUrlsTask.java, NodesSupplier.java và Session.java tại các vị trí v2.0.11 được liên kết, sau đó tái hiện kịch bản TableSessionPoolBuilder với một node bị ngắt kết nối và so sánh khi auto-discovery được bật và khi bị tắt. Theo dõi cách các endpoint không khả dụng được chọn và cách các kết nối thất bại được xử lý. Hoàn thành khi ngăn được độ trễ lặp lại do connection timeout, trong khi các node khỏe mạnh vẫn tiếp tục phục vụ các truy vấn và việc khôi phục vẫn khả thi.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
java
Lĩnh vực
databases, distributed-systems
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
48/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.