From bf04ddcae80dc337ec63d6ddd59ca7271617f606 Mon Sep 17 00:00:00 2001 From: chrislu Date: Thu, 18 Dec 2025 22:33:19 -0800 Subject: [PATCH] master: sync hashicorp raft leadership with seaweedfs/raft leader When seaweedfs/raft elects a new leader, transfer hashicorp raft leadership to the same node. This ensures both raft systems always have the same leader, allowing dual-write to work correctly. Changes: - Add SyncHashicorpRaftLeadership() to transfer hashicorp raft leadership - Call sync on seaweedfs/raft leader change event - Only the current hashicorp raft leader can initiate transfer --- weed/server/master_server.go | 2 ++ weed/server/raft_server.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/weed/server/master_server.go b/weed/server/master_server.go index 496f226fa..3c9bc88e8 100644 --- a/weed/server/master_server.go +++ b/weed/server/master_server.go @@ -209,6 +209,8 @@ func (ms *MasterServer) SetRaftServer(raftServer *RaftServer) { if ms.Topo.RaftServer.Leader() != "" { glog.V(0).Infof("[%s] %s becomes leader.", ms.Topo.RaftServer.Name(), ms.Topo.RaftServer.Leader()) ms.Topo.LastLeaderChangeTime = time.Now() + // Sync hashicorp raft leadership to match seaweedfs/raft leader for dual-write + raftServer.SyncHashicorpRaftLeadership(ms.Topo.RaftServer.Leader()) } }) raftServerName = fmt.Sprintf("[%s]", ms.Topo.RaftServer.Name()) diff --git a/weed/server/raft_server.go b/weed/server/raft_server.go index 0222b728f..7c697d31c 100644 --- a/weed/server/raft_server.go +++ b/weed/server/raft_server.go @@ -279,3 +279,38 @@ func (s *RaftServer) initHashicorpRaftForDualWrite(option *RaftServerOption) err return nil } + +// SyncHashicorpRaftLeadership transfers hashicorp raft leadership to match seaweedfs/raft leader. +// This ensures both raft systems have the same leader for dual-write to work correctly. +func (s *RaftServer) SyncHashicorpRaftLeadership(seaweedfsRaftLeader string) { + if s.RaftHashicorp == nil || seaweedfsRaftLeader == "" { + return + } + + hashicorpLeader, _ := s.RaftHashicorp.LeaderWithID() + targetLeader := pb.ServerAddress(seaweedfsRaftLeader) + + // If hashicorp raft already has the same leader, nothing to do + if string(hashicorpLeader) == targetLeader.ToGrpcAddress() { + glog.V(1).Infof("Dual-write: hashicorp raft leader already matches seaweedfs/raft leader: %s", seaweedfsRaftLeader) + return + } + + // Only the current hashicorp raft leader can transfer leadership + if s.RaftHashicorp.State() != hashicorpRaft.Leader { + glog.V(1).Infof("Dual-write: not hashicorp raft leader, cannot transfer leadership to %s", seaweedfsRaftLeader) + return + } + + // Transfer leadership to the seaweedfs/raft leader + glog.V(0).Infof("Dual-write: transferring hashicorp raft leadership to match seaweedfs/raft leader: %s", seaweedfsRaftLeader) + future := s.RaftHashicorp.LeadershipTransferToServer( + hashicorpRaft.ServerID(targetLeader), + hashicorpRaft.ServerAddress(targetLeader.ToGrpcAddress()), + ) + if err := future.Error(); err != nil { + glog.Warningf("Dual-write: failed to transfer hashicorp raft leadership to %s: %v", seaweedfsRaftLeader, err) + } else { + glog.V(0).Infof("Dual-write: hashicorp raft leadership transferred to %s", seaweedfsRaftLeader) + } +}