diff --git a/src/cli/cmd/mysql.go b/src/cli/cmd/mysql.go index 9adf66f..d036443 100644 --- a/src/cli/cmd/mysql.go +++ b/src/cli/cmd/mysql.go @@ -328,17 +328,11 @@ func mysqlRebuildMeCommandFn(cmd *cobra.Command, args []string) { // 16. enable raft { - // check whether the state is IDLE or not - if conf.Raft.StartAsIDLE { - log.Warning("S16-->enable.raft.skiped.since.StartAsIDLE=true...") - log.Warning("S16-->run.as.IDLE...") - } else { - log.Warning("S16-->enable.raft.begin...") - if _, err := callx.EnableRaftRPC(self); err != nil { - log.Error("enbleRaftRPC.error[%v]", err) - } - log.Warning("S16-->enable.raft.done...") + log.Warning("S16-->enable.raft.begin...") + if _, err := callx.EnableRaftRPC(self); err != nil { + log.Error("enbleRaftRPC.error[%v]", err) } + log.Warning("S16-->enable.raft.done...") } // 17. wait change to master diff --git a/src/config/config.go b/src/config/config.go index 2eec471..b32d4a4 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -57,8 +57,8 @@ type RaftConfig struct { // purge binlog interval (ms) PurgeBinlogInterval int `json:"purge-binlog-interval"` - // start as idle - StartAsIDLE bool `json:"start-as-idle"` + // Super IDLE cant' change to FOLLOWER. + SuperIDLE bool `json:"super-idle"` // MUST: set in init // the shell command when leader start diff --git a/src/raft/raft.go b/src/raft/raft.go index f1acbaf..0198bcf 100644 --- a/src/raft/raft.go +++ b/src/raft/raft.go @@ -108,8 +108,9 @@ func (r *Raft) Start() error { r.c = make(chan *ev) // state - if r.conf.StartAsIDLE { + if r.conf.SuperIDLE { r.setState(IDLE) + r.WARNING("start.as.super.IDLE") } else { r.setState(FOLLOWER) } diff --git a/src/raft/raft_test.go b/src/raft/raft_test.go index 604a247..3ada7b2 100644 --- a/src/raft/raft_test.go +++ b/src/raft/raft_test.go @@ -836,12 +836,12 @@ func TestRaftElectionUnderIDLEInMajority(t *testing.T) { // test run as IDLE with config.StartAsIDLE=true configuration // // TEST PROCESSES: -// 1. Start 1 raft with StartAsIDLE=true +// 1. Start 1 raft with SuperIDLE=true // 2. check the IDLE func TestRaftStartAsIDLE(t *testing.T) { log := xlog.NewStdLog(xlog.Level(xlog.PANIC)) conf := config.DefaultRaftConfig() - conf.StartAsIDLE = true + conf.SuperIDLE = true port := common.RandomPort(8100, 8200) _, rafts, cleanup := MockRaftsWithConfig(log, conf, port, 1) defer cleanup() diff --git a/src/raft/rpc_ha.go b/src/raft/rpc_ha.go index 100b69e..0450205 100644 --- a/src/raft/rpc_ha.go +++ b/src/raft/rpc_ha.go @@ -45,8 +45,13 @@ func (h *HARPC) HAEnable(req *model.HARPCRequest, rsp *model.HARPCResponse) erro state := h.raft.getState() switch state { case IDLE: - h.raft.setState(FOLLOWER) - h.raft.loopFired() + if h.raft.conf.SuperIDLE { + // Set SuperIDLE to noLeader to fire the 'change master to'. + h.raft.setLeader(noLeader) + } else { + h.raft.setState(FOLLOWER) + h.raft.loopFired() + } rsp.RetCode = model.OK return nil case STOPPED: diff --git a/src/raft/rpc_ha_test.go b/src/raft/rpc_ha_test.go index 16d68d7..61cf017 100644 --- a/src/raft/rpc_ha_test.go +++ b/src/raft/rpc_ha_test.go @@ -485,3 +485,81 @@ func TestRaftRPCHATryToLeaderFail_MySQLUnpromotble(t *testing.T) { assert.Equal(t, whoisleader, GTIDCIDX) } } + +func TestRaftSuperIDLEEnableHA(t *testing.T) { + var testName = "TestRaftSuperIDLEEnableHA" + var want, got State + var whoisleader int + var leader *Raft + var idler *Raft + + log := xlog.NewStdLog(xlog.Level(xlog.PANIC)) + port := common.RandomPort(8000, 9000) + names, rafts, cleanup := MockRafts(log, port, 3) + defer cleanup() + + // 1. Start 3 rafts. + { + for i, raft := range rafts { + if i == 2 { + raft.conf.SuperIDLE = true + idler = raft + } + raft.Start() + } + } + + // 2. wait leader election. + { + MockWaitLeaderEggs(rafts, 1) + whoisleader = 0 + got = 0 + want = (LEADER + FOLLOWER + IDLE) + for i, raft := range rafts { + got += raft.getState() + if raft.getState() == LEADER { + whoisleader = i + } + } + // [LEADER, FOLLOWER, IDLE] + assert.Equal(t, want, got) + } + idlerLeader1 := idler.getLeader() + + // 3. set leader handlers to mock + { + leader = rafts[whoisleader] + log.Warning("%v.leader[%v].set.mock.functions", testName, rafts[whoisleader].getID()) + leader.L.setProcessHeartbeatRequestHandler(leader.mockLeaderProcessHeartbeatRequest) + leader.L.setProcessRequestVoteRequestHandler(leader.mockLeaderProcessRequestVoteRequest) + } + + // 4. Stop leader hearbeat + { + log.Warning("%v.leader[%v].Stop.heartbeat", testName, leader.getID()) + leader.L.setSendHeartbeatHandler(leader.mockLeaderSendHeartbeat) + } + + // 5. Wait new leader. + { + MockWaitLeaderEggs(rafts, 2) + } + + { + c, cleanup := MockGetClient(t, names[2]) + defer cleanup() + + method := model.RPCHAEnable + req := model.NewHARPCRequest() + rsp := model.NewHARPCResponse(model.OK) + err := c.Call(method, req, rsp) + assert.Nil(t, err) + + want := model.OK + got := rsp.RetCode + assert.Equal(t, want, got) + } + + idlerLeader2 := idler.getLeader() + assert.NotEqual(t, idlerLeader1, idlerLeader2) +}