mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-17 19:48:35 +02:00
fix(master): skip redundant DoJoinCommand on resumeState to prevent deadlock (#8998)
* fix(master): skip redundant DoJoinCommand on resumeState to prevent deadlock When fastResume is active (single-master + resumeState + non-empty log), the raft server becomes leader within ~1ms. DoJoinCommand then enters the leaderLoop's processCommand path, which calls setCommitIndex to commit all pending entries. The goraft setCommitIndex implementation returns early when it encounters a JoinCommand entry (to recalculate quorum), which can prevent the new entry's event channel from being notified — leaving DoJoinCommand blocked forever. Each restart appends a new raft:join entry to the log, while the conf file's commitIndex (only persisted on AddPeer) lags behind. After 3-4 restarts the uncommitted range contains old JoinCommand entries that trigger the early return before the new entry is reached. Fix: skip DoJoinCommand when the raft log already has entries (the server was already joined in a previous run). The fastResume mechanism handles leader election independently. * fix(master): handle Hashicorp Raft in HasExistingState Add Hashicorp Raft support to HasExistingState by checking AppliedIndex, consistent with how other RaftServer methods handle both raft implementations. * fix(master): use LastIndex() instead of AppliedIndex() for Hashicorp Raft AppliedIndex() reflects in-memory FSM state which starts at 0 before log replay completes. LastIndex() reads from persisted stable storage, correctly mirroring the non-Hashicorp IsLogEmpty() check.
This commit is contained in:
1 parent
3c2e0e3e26
commit
6f036c7015
2 files changed
+20
-2
No files matched your search
@@ -224,8 +224,13 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) {
|
||||
if raftServer == nil {
|
||||
glog.Fatalf("please verify %s is writable, see https://github.com/seaweedfs/seaweedfs/issues/717: %s", *masterOption.metaFolder, err)
|
||||
}
|
||||
// For single-master mode, initialize cluster immediately without waiting
|
||||
if isSingleMaster {
|
||||
// For single-master mode with a fresh log, initialize cluster immediately.
|
||||
// When resuming with existing state, the server is already a member and
|
||||
// will self-elect via fastResume — sending another JoinCommand would block
|
||||
// because goraft's setCommitIndex returns early on JoinCommand entries,
|
||||
// preventing the new entry's event from being notified when old uncommitted
|
||||
// JoinCommands exist in the log.
|
||||
if isSingleMaster && !raftServer.HasExistingState() {
|
||||
glog.V(0).Infof("Single-master mode: initializing cluster immediately")
|
||||
raftServer.DoJoinCommand()
|
||||
}
|
||||
|
||||
@@ -290,6 +290,19 @@ func recoverTopologyIdFromSnapshot(dataDir string, topo *topology.Topology) {
|
||||
recoverTopologyIdFromState(snap.State, topo)
|
||||
}
|
||||
|
||||
// HasExistingState returns true when the raft log already contains entries,
|
||||
// indicating this server was previously joined and does not need a new
|
||||
// JoinCommand on startup.
|
||||
func (s *RaftServer) HasExistingState() bool {
|
||||
if s.raftServer != nil {
|
||||
return !s.raftServer.IsLogEmpty()
|
||||
}
|
||||
if s.RaftHashicorp != nil {
|
||||
return s.RaftHashicorp.LastIndex() > 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *RaftServer) DoJoinCommand() {
|
||||
|
||||
glog.V(0).Infoln("Initializing new cluster")
|
||||
|
||||
Reference in new issue
Block a user