如何用 REPLTAKEOVER 把 Dragonfly 副本提升为新的主节点?
【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly
当你已经部署了一个 Dragonfly 主从复制关系,需要把写角色从旧主节点切换到某个副本(主节点故障、维护窗口或主动切换)时,可以在副本端执行REPLTAKEOVER <seconds> [SAVE]。该命令让副本与旧主完成一次精确的数据交接后提升为新主:非集群模式下旧主进程会退出,集群模式下旧主的 slot 所有权会被调整到新主上。前提是该副本已完成 full sync 且复制会话处于稳定同步(stable sync)状态,否则命令会直接报错。
命令语义与前置条件
REPLTAKEOVER <seconds> [SAVE]在副本上执行,副本会把它转发为DFLY TAKEOVER <seconds> [SAVE] <sync_id>发给主节点。设计与执行细节见 docs/replication.md 的 "5. Takeover" 一节,命令实现位于 server_family.cc。
参数说明:
<seconds>:交接过程的超时秒数,约束主侧等待 in-flight 命令排空、等待请求副本追平 LSN 的时间上限。SAVE:可选参数,触发一次同步SAVE。源码注释明确 "SAVE is used only by tests",docs/replication.md 也将其描述为 test-only knob,生产使用无需附加。
执行前必须满足以下条件,不满足会直接返回错误:
| 条件 | 不满足时的报错 |
|---|---|
本实例当前是副本(已是主节点时命令按幂等语义直接返回OK) | — |
| full sync 已完成 | Full sync not done |
主侧该副本的会话已处于STABLE_SYNC | takeover RPC 失败,返回Couldn't execute takeover: ... |
| 超时值非负 | timeout is negative(0仅测试场景可用) |
操作步骤
以下主路径适用于非集群模式的一对 Dragonfly 实例。
1. 建立复制关系并确认稳定同步
在副本上把主地址指到目标主节点(<master_ip>与<master_port>替换为你实际的主节点地址,测试代码中的写法是REPLICAOF localhost <port>,见 replication_specific_test.py):
REPLICAOF <master_ip> <master_port>然后用INFO REPLICATION确认同步已稳定。根据 docs/replication.md 的 Observability 一节:主侧会把每个副本的会话状态报告为preparation、full_sync、stable_sync(或cancelled);副本自身则暴露基于内部状态标志推导的相位,最终稳定相位为STABLE_SYNC。只有进入 stable sync 后,REPLTAKEOVER才会成功,这也对应了REPLTAKEOVER前置条件中的Full sync not done检查。
2. 在副本上发起提升
REPLTAKEOVER 10主节点收到后按 docs/replication.md 描述的顺序执行:原子地把全局状态从ACTIVE翻转为TAKEN_OVER(并发的第二次 takeover 会在这里失败);等待所有监听器上的 in-flight 命令派发放完,并禁用 key 过期以避免交接窗口内状态被改写;对请求副本发送 journalPING强制其立即 ACK,然后忙等直到请求副本在每个 shard 上的 last-acked LSN 等于主侧当前 LSN,即副本已应用全部数据;成功后回+OK。此后主节点再尽力(不强制 ACK)等待其他已连接的副本也追平,避免它们对新主需要全量重同步;最后同步SAVE(仅当带了SAVE)并关闭进程(非集群模式)或在集群模式下把 slot 所有权 reconcile 到新主。
副本侧的动作是:在发起 takeover RPC之前先把自己已执行到的 LSN 作为 journal 起点重启本节点 journal(使其成为主后立即具备 partial sync 能力),等主确认+OK并拆除旧副本角色后才切为主节点。
3. 验证提升结果
在副本(新主)上执行:
role成功时返回 master 角色。replication_resilience_test.py 中的验证方式是role返回 master 且无任何 replica 列表;另外可在新主日志中看到Takeover successful, promoting this instance to master.(server_family.cc)。非集群模式下,旧主进程会退出——replication_resilience_test.py 中通过检查旧主进程已退出(proc.poll() == 0)来确认这一点。
其他副本如何接续到新主
如果旧主还有其他副本,提升完成后把它们重新指向新主:
REPLICAOF <new_master_ip> <new_master_port>docs/replication.md 的 "Partial sync after promotion / failover" 一节说明:被提升的节点会保留旧主的master_replid与各 shard 已执行 LSN,并沿用原来的 journal LSN 编号(REPLTAKEOVER路径无条件预热 journal)。其他原本跟随旧主的副本重连时携带自己记住的last_master_id与lsn-vec,若与新主记住的旧主 id 匹配,就能从自己上次看到的 LSN 继续 partial sync,而不用全量重同步。replication_test.py 的test_repl_offset展示了这条路径:REPLTAKEOVER 5之后把一个旧副本REPLICAOF到提升节点上,断言其psync_successes == 1,即发生了 partial sync。
可选分支:集群模式下用 cluster_mgr.py 提升
在集群模式下,仓库提供的 tools/cluster_mgr.py 有一个takeover动作,把某个副本从旧主的 replica 列表中移除并在配置层面将其置为主:
./cluster_mgr.py --action=takeover --target_host=<replica_ip> --target_port=<replica_port>脚本会向集群推送修改后的配置(要求远端节点已启动并以--cluster_mode=yes初始化)。脚本的 help 文本明确列出了执行完这条命令后你还要做的事(cluster_mgr.py):
- 在新主上执行
REPLICAOF NO ONE; - 如果旧主还有其他副本,用
REPLICAOF把它们指向新主; - 旧主会被 detach 出集群,建议随后将其关闭。
注意两条路径的差别:REPLTAKEOVER命令走的是 in-band 交接——等请求副本的 LSN 追平后才完成;而cluster_mgr.py --action=takeover只做集群配置变更,数据与角色的切换靠其后继步骤(REPLICAOF NO ONE等)完成。
失败现象与边界
- 超时:
<seconds>到期仍未追平 LSN 时,命令返回Couldn't execute takeover: <原因>,节点角色保持不变。replication_resilience_test.py 用REPLTAKEOVER 0(零超时仅测试允许)构造了这一失败:报错前缀为Couldn't execute takeover,且事后role验证主仍是 master、副本仍是 replica。 - 并发 takeover:主侧全局状态翻转是原子的,同一时刻第二个 takeover 请求会在
ACTIVE -> TAKEN_OVER状态检查处失败。 - full sync 未完成:副本刚建立复制关系、尚未完成 full sync 就发起 takeover,会得到
Full sync not done。 - 其他副本尽力等待:主侧对其他副本的等待是 best-effort(不强制 PING,因为强制 ACK 会推进 LSN、反而破坏这些节点的 partial sync);若它们没追平,对新主可能缺数据或需要全量重同步,这也是交接后应尽快用
REPLICAOF检查并修复其余副本的原因。 - 相关机制的完整说明(握手、full/partial sync、ACK 与反压)在 docs/replication.md,集群配置层面的 takeover 用法可结合 cluster_mgr_test.py 中的
--action=takeover用例查看。
【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考