Skip to content

[Bug] 2.0.10 故障切换后 DataNode 保留过期 Region 路由,查询持续访问离线副本并超时 #18630

Description

@AlanDevise

现象与环境

三节点集群断网测试后,两个在线 DataNode 均可登录、读取表结构,但同一条数据查询在 B 节点成功,在 C 节点持续超时。已确认 C 缓存了过期的 Region 副本顺序;仅重发一次 ConfigNode 当前路由,查询立即恢复。

  • IoTDB 2.0.10(BuildInfo 3c26382),Table model;openEuler 24.03 LTS-SP3,Temurin OpenJDK 25.0.3+9-LTS。
  • 三台物理机,每台部署一个 ConfigNode 和一个 DataNode。A(DN3)断网,B(DN4)、C(DN5)在线,C 为 ConfigNode Leader。
  • ConfigNode / SchemaRegion 使用 Ratis,Schema 副本数 3;DataRegion 使用 IoTConsensus,数据副本数 2。
  • 默认参数:read_consistency_level=strongenable_topology_probing=false;连接和查询超时均为 60000 ms。
  • DataRegion 1 位于 A/C,Leader 为 C;DataRegion 2 位于 A/B,Leader 为 B。每个 Region 均有在线副本,B/C 之间相关内部端口可达。

主机名和库表名已脱敏,Node ID、Region ID 与耗时保留实测值。

现场复现与验证

  1. 在已有三节点集群中进行网络中断/恢复测试,期间出现 ConfigNode Leader 切换;排查时保持 A 断网。

  2. 等待集群显示 A 为 Unknown、B/C 为 Running,分别通过 B/C 执行:

    SELECT * FROM test_db.test_table LIMIT 5;
    SELECT COUNT(*) FROM test_db.test_table;
  3. B 正常返回,C 多次约 60 秒后返回 720: Current query is time out。DBeaver 和安装包自带 CLI 均可复现;C 服务端日志显示仍尝试连接离线的 A:

    can't execute request on node TEndPoint(ip:node-A, port:10730),
    error msg is java.net.SocketTimeoutException: Connect timed out
    

通过只读 Java Attach 诊断读取现有 JVM 对象,确认差异位于 ClusterPartitionFetcher.partitionCache.groupIdToReplicaSetMap

路由来源 DataRegion 2 副本顺序
ConfigNode 当前路由 [4, 3],在线 B 优先
B 的 PartitionCache [4, 3]
C 的 PartitionCache [3, 4],离线 A 优先

随后读取 ConfigNode 的 getLatestRegionRouteMap(),通过一次 updateRegionCache(TRegionRouteReq) 将其原始时间戳和路由发给 C,返回状态 200。C 的顺序变为 [4, 3],相同查询立即恢复:LIMIT 5 为 0.058 秒,COUNT(*) 为 0.095 秒、返回 40 行。

整个验证期间 A 仍断网,C 的进程未重启,未修改配置或数据库数据。这证明过期路由与本次超时直接相关;重发路由只是临时恢复措施。

关键源码与疑似触发条件

以下链接固定到现场版本提交 3c26382bebee73ec1c9593f836ef4cdcc0376062

1. 路由广播排除 Unknown 节点,并且依赖优先级发生变化。

RouteBalancer.java,615–653 行 中,仅当新旧优先级不同才设置 needBroadcast;广播目标过滤条件为:

.filterDataNodeThroughStatus(
    NodeStatus.Running, NodeStatus.Removing, NodeStatus.ReadOnly)

节点状态变化回调,771–775 行 调用 handleBalanceAction(balanceAllEnabledRegionLeaders()),没有直接向恢复的节点补发路由。若后续权威优先级不变,可能无法再次触发广播。

现场 ConfigNode 日志时间顺序(UTC+8):

09:20:58.106  DN3/4/5:null -> Unknown
09:20:58.111  DataRegion 2:null -> [4,3]
09:20:58.115  DN4/5:Unknown -> Running

因此怀疑:Leader 切换初始化时,C 处于 Unknown 而错过路由,恢复 Running 后未获得补发。尚未捕获当次广播的实际接收者或 RPC 报文,也未在全新集群中稳定复现首次漏发;这个具体触发时序仍是推断。

2. DataNode 命中旧缓存后,不会主动拉取最新路由。

PartitionCache.java,572–619 行getRegionReplicaSet() 先查本地 Map,仅在 result.isEmpty() 时向 ConfigNode 获取路由。该 Map 没有 TTL,因此已有 Region 的旧副本顺序可持续命中。

3. 连接离线副本可能耗尽整个查询时间。

FragmentInstanceDispatcherImpl.java,583–615 行 在连接异常后检查查询截止时间;若已超过,直接返回查询超时。现场连接与查询超时同为 60 秒,与日志中的连接超时后返回 720 一致。

希望确认

  • 是否存在上述路由漏发且缺少恢复补发/缓存重新校验的问题?除了当前配置,还有哪些触发条件需要验证?
  • 2.0.11 引入的元数据租约机制(相关 PR Cyb/metadata table #18127modify the read logic of metadata lease timeout #18228)能否覆盖此场景,还是需要独立修复路由同步?目前未升级验证,也未找到明确对应本次完整现象的已修复 issue。

预期:集群状态稳定且每个 Region 仍有在线副本时,各在线 DataNode 应最终更新路由,避免持续将查询发往已离线的副本。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions