DFLYCLUSTER CONFIG 被拒绝并返回 "Invalid cluster configuration" 怎么定位?
你在用集群管理器(cluster manager)向 Dragonfly 节点推送集群拓扑时,节点回复了 -ERR Invalid cluster configuration.,配置没有生效。这个错误是 DFLYCLUSTER CONFIG <json> 对一段无法通过解析或校验的集群 JSON 给出的统一拒绝信息,它本身不告诉你具体是哪条规则失败。这篇文章给出定位路径:先确认拒绝后节点处于什么状态,再读服务器日志定位到具体校验项,最后按 docs/cluster-mode.md 中的校验规则逐条核对 JSON、修复后重新推送并验证。
适用前提:节点以 --cluster_mode=yes --admin_port=<p> 启动。DFLYCLUSTER 是隐藏的管理员命令,只接受 --admin_port 配置的管理监听器上的连接,客户端监听器会拒绝它。
先确认拒绝对节点意味着什么
被拒绝的配置不会破坏任何状态,这是定位时最重要的背景:
- 校验在安装之前执行,失败时节点原样保留之前的配置(docs/cluster-mode.md §4.2、§4.3)。已迁移中的 slot、已有的数据都不受影响。
- 如果这是节点启动后收到的第一个配置,节点会继续处于未配置状态:它不拥有任何 slot,所有数据面命令(以及
CLUSTER SHARDS/SLOTS/NODES)返回-ERR Cluster is not yet configured。 - 如果之前已经装过一个有效配置,节点继续按旧配置服务。此时集群里会出现短暂的状态不一致:这个节点上的 slot 归属还是旧视图,打到该节点上的客户端可能收到过期的
-MOVED。文档明确此时集群管理器必须观察到拒绝并推送修正后的配置(docs/cluster-mode.md §7.4)。
注意区分错误串的精确形态:完整回复是 -ERR Invalid cluster configuration.(末尾带句点,见 src/server/cluster/cluster_family.cc)。它和另外两个容易混淆的错误不同:
-ERR Cluster is disabled. Use --cluster_mode=yes to enable.—— 节点没有以真实集群模式运行(未设--cluster_mode=yes,或处于emulated模式),此时DFLYCLUSTER整个命令都不可用,不是配置内容的问题。-ERR Cluster is not yet configured—— 数据面命令在首个配置安装前的回复。
准备:拿到定位所需的两个输入
- 节点 ID。在任意监听器上执行
CLUSTER MYID获取本节点身份。配置 JSON 中本节点对应 shard 的master.id必须与此一致(它是集群管理器写入master.id、replicas[].id、migrations[].node_id的依据)。 - 被推送的 JSON 原文。定位问题需要的是集群管理器实际发出的那段 JSON,而不是"应该发"的版本。
定位第一步:用 CLUSTER INFO 判断节点当前状态
在被拒绝的节点上执行:
CLUSTER INFO
根据输出判断节点属于哪种情况(字段取值参考 src/server/cluster/cluster_family_test.cc 中的断言,以下为文档测试示例):
-
首次配置就失败(节点从未有配置):
cluster_state:fail cluster_slots_assigned:0 cluster_slots_ok:0 cluster_known_nodes:0 cluster_size:0此时
CLUSTER SHARDS、CLUSTER SLOTS、CLUSTER NODES会返回-ERR Cluster is not yet configured。 -
拒绝的是一次更新:
CLUSTER INFO仍显示旧配置的状态(cluster_state:ok、cluster_slots_assigned:16384等),说明旧配置仍在服务。这种情况要额外警惕集群内其他节点已应用新配置,拓扑出现分歧。
定位第二步:读服务器日志,锁定具体失败的校验项
校验逻辑集中在 src/server/cluster/cluster_config.cc 的 IsConfigValid 与 JSON 解析函数中。每类失败都会向日志打出一条对应的 ERROR 记录(命令处理侧另有一条 Can't set cluster config 的 WARNING,见 src/server/cluster/cluster_family.cc),日志行直接指明规则。在 Dragonfly 的日志输出中查找与推送时刻对应的行,按下表对照:
| 日志内容(摘录) | 含义 |
|---|---|
Can't parse JSON for ClusterConfig ... | 整段输入不是合法 JSON |
Invalid JSON cluster config: not an array ... | 顶层必须是 shard 对象组成的 JSON 数组(空数组 [] 也在此之后被拒,因为没有任何 slot 被分配) |
Invalid JSON cluster config: slot_ranges is not an array ... | 字段缺失或类型不符(同类日志还有 invalid id for node、invalid ip for node、replicas is not an array、invalid health status for node: ... 等) |
Invalid cluster config: some slots were missing. | [0, 16383] 中有 slot 没有被任何 shard 覆盖 |
Invalid cluster config: slot=N was already configured by another slot range. | 某个 slot 被两个 shard 同时声明 |
Invalid cluster config: start=X is larger than end=Y | 某个 slot range 不满足 start <= end |
Master <id> appears more than once / Replica <id> appears more than once | 节点 ID 重复:同一 id 作为 master 出现两次,或作为同一 master 的 replica 出现两次 |
Invalid cluster config: migration target equals source master=... | migrations[] 条目指向源 shard 自己 |
Invalid cluster config: migration target <id> is not a shard master in the config | migration 目标 id 不是本配置中的某个 master |
Invalid cluster config: duplicate migration target <id> in shard master=... | 同一源 shard 对同一目标有两条 migration(每个节点对至多一条 migration) |
Invalid cluster config: empty migration slot_ranges to target ... | migration 的 slot_ranges 为空 |
Invalid cluster config: bad migration slot range ... | migration 的 slot range 不合法 |
Invalid cluster config: migration range ... is not owned by shard master=... | migration 的 slot 范围没有完全落在源 shard 的 slot_ranges 内 |
Invalid cluster config: overlapping migration ranges ... and ... in shard master=... | 同一源 shard 上两条 migration 的 slot 范围重叠 |
完整消息集合以 src/server/cluster/cluster_config.cc 源码为准。日志定位到规则后,回到被推送的 JSON 中找到对应的 shard 或 migration 条目即可。
定位第三步:按校验规则逐条核对 JSON
如果日志不方便查看,可以直接对照 docs/cluster-mode.md §4.1–§4.2 的规则人工核对。一份配置的 JSON 顶层是 shard 数组,每个 shard 包含:
slot_ranges:闭区间列表,start <= end,取值0..16383,所有 shard 的 range 必须恰好覆盖[0, 16383]且不重叠。这是最常见的失败原因——少覆盖一段(some slots were missing)或多覆盖一段(was already configured)。master:id(字符串,须与目标节点的CLUSTER MYID一致)、ip、port(uint16,向客户端通告并嵌入-MOVED回复)、可选的health(online|loading|fail|hidden,大小写不敏感,缺省online,见 docs/cluster-node-health.md)。replicas:数组,可为空;字段与master相同。注意它只是描述性信息——复制关系要靠REPLICAOF单独建立,replicas[]本身不会让任何节点开始复制。migrations(可选):本 master 向外发起的 slot 迁移,每条含目标 master 的node_id/ip/port(目标的 admin port)和非空slot_ranges。
规则核对清单(docs/cluster-mode.md §4.2 原文):
- 任何 slot 不能被 0 个或多个 shard 拥有;
- 节点 id 不能作为两个 master 出现,也不能作为同一 master 的两个 replica 出现;
- migration 不能指向自己的源 shard;
- migration 目标 id 必须是同一配置中的某个 master;
- 同一源 shard 不能有两条目标相同的 migration;
- migration 的
slot_ranges非空、合法、完全包含在源 shard 的slot_ranges内、且同一源上不与其他 migration 重叠。
一个最小可验证的单机集群配置形态(结构参考 src/server/cluster/cluster_family_test.cc 中的测试模板,id 替换为本节点 CLUSTER MYID 的实际值,ip/port 替换为实际地址):
[
{
"slot_ranges": [
{ "start": 0, "end": 16383 }
],
"master": {
"id": "<本节点 CLUSTER MYID 的输出>",
"ip": "10.0.0.1",
"port": 7000,
"health": "online"
},
"replicas": []
}
]
修复后重新推送并验证
修正 JSON 后,在管理监听器上重新执行 DFLYCLUSTER CONFIG <json>。成功时回复 +OK(如果新配置与当前配置完全相同,也直接回复 +OK)。然后验证:
CLUSTER INFO
期望看到(单 shard 集群的文档测试示例):
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_known_nodes:1
cluster_size:1
再执行 CLUSTER SHARDS、CLUSTER SLOTS、CLUSTER NODES,确认返回的拓扑(slot 区间、节点 id、ip、port、health)与配置一致。此前返回 -ERR Cluster is not yet configured 的数据面命令应当恢复正常。
最后确认集群管理器侧的动作闭环(docs/cluster-mode.md §2.2、§7.4):
- 配置必须推送到所有节点——master 和 replica 都要收到,replica 需要用它来计算
-MOVED目标。 - 如果这次拒绝只发生在单个节点上,管理器需要观察到拒绝并对修正后的配置重新推送该节点;在该节点修好之前,集群对 slot 归属的视图是不一致的。
- 节点重启后不保留集群配置,需要重新推送当前 JSON。
边界与限制
DFLYCLUSTER是隐藏命令,客户端监听器不接受;如果客户端库意外发出该命令,看到的不是本节讨论的拒绝。- 如果节点实际没有以
--cluster_mode=yes启动,收到的会是-ERR Cluster is disabled. Use --cluster_mode=yes to enable.,那是模式配置问题而非 JSON 问题,先修正启动参数再继续本文流程。 - 被拒绝的配置不产生任何副作用:旧配置保留、无数据删除动作。slot 删除清扫只在一个有效的新配置把 slot 从节点移除后才发生。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



