package threadTest import ( "fmt" "os" "simple-kv-store/internal/nodes" "strconv" "testing" "time" "github.com/syndtr/goleveldb/leveldb" ) func ExecuteStaticNodeI(id string, isRestart bool, peerIds []string, threadTransport *nodes.ThreadTransport) (*nodes.Node, chan struct{}) { if !isRestart { os.RemoveAll("storage/node" + id) } os.RemoveAll("leveldb/simple-kv-store" + id) db, err := leveldb.OpenFile("leveldb/simple-kv-store" + id, nil) if err != nil { fmt.Println("Failed to open database: ", err) } // 打开或创建节点数据持久化文件 storage := nodes.NewRaftStorage("storage/node" + id) var otherIds []string for _, ids := range peerIds { if ids != id { otherIds = append(otherIds, ids) // 删除目标元素 } } // 初始化 node, quitChan := nodes.InitThreadNode(id, otherIds, db, storage, isRestart, threadTransport) // 开启 raft // go nodes.Start(node, quitChan) return node, quitChan } func StopElectionReset(nodeCollections [] *nodes.Node, quitCollections []chan struct{}) { for i := 0; i < len(quitCollections); i++ { node := nodeCollections[i] quitChan := quitCollections[i] go func(node *nodes.Node, quitChan chan struct{}) { ticker := time.NewTicker(400 * time.Millisecond) defer ticker.Stop() for { select { case <-quitChan: return // 退出 goroutine case <-ticker.C: node.ResetElectionTimer() // 不主动触发选举 } } }(node, quitChan) } } func TestInitElection(t *testing.T) { n := 5 var peerIds []string for i := 0; i < n; i++ { peerIds = append(peerIds, strconv.Itoa(i + 1)) } // 结点启动 var quitCollections []chan struct{} var nodeCollections []*nodes.Node threadTransport := nodes.NewThreadTransport() for i := 0; i < n; i++ { n, quitChan := ExecuteStaticNodeI(strconv.Itoa(i + 1), false, peerIds, threadTransport) quitCollections = append(quitCollections, quitChan) nodeCollections = append(nodeCollections, n) } StopElectionReset(nodeCollections, quitCollections) // 通知所有node结束 defer func(){ for _, quitChan := range quitCollections { close(quitChan) } }() for i := 0; i < n; i++ { nodeCollections[i].State = nodes.Follower } nodeCollections[0].StartElection() time.Sleep(time.Second) CheckOneLeader(t, nodeCollections) CheckIsLeader(t, nodeCollections[0]) CheckTerm(t, nodeCollections[0], 2) } func TestRepeatElection(t *testing.T) { n := 5 var peerIds []string for i := 0; i < n; i++ { peerIds = append(peerIds, strconv.Itoa(i + 1)) } // 结点启动 var quitCollections []chan struct{} var nodeCollections []*nodes.Node threadTransport := nodes.NewThreadTransport() for i := 0; i < n; i++ { n, quitChan := ExecuteStaticNodeI(strconv.Itoa(i + 1), false, peerIds, threadTransport) quitCollections = append(quitCollections, quitChan) nodeCollections = append(nodeCollections, n) } StopElectionReset(nodeCollections, quitCollections) // 通知所有node结束 defer func(){ for _, quitChan := range quitCollections { close(quitChan) } }() for i := 0; i < n; i++ { nodeCollections[i].State = nodes.Follower } go nodeCollections[0].StartElection() go nodeCollections[0].StartElection() time.Sleep(time.Second) CheckOneLeader(t, nodeCollections) CheckIsLeader(t, nodeCollections[0]) CheckTerm(t, nodeCollections[0], 3) } func TestBelowHalfCandidateElection(t *testing.T) { n := 5 var peerIds []string for i := 0; i < n; i++ { peerIds = append(peerIds, strconv.Itoa(i + 1)) } // 结点启动 var quitCollections []chan struct{} var nodeCollections []*nodes.Node threadTransport := nodes.NewThreadTransport() for i := 0; i < n; i++ { n, quitChan := ExecuteStaticNodeI(strconv.Itoa(i + 1), false, peerIds, threadTransport) quitCollections = append(quitCollections, quitChan) nodeCollections = append(nodeCollections, n) } StopElectionReset(nodeCollections, quitCollections) // 通知所有node结束 defer func(){ for _, quitChan := range quitCollections { close(quitChan) } }() for i := 0; i < n; i++ { nodeCollections[i].State = nodes.Follower } go nodeCollections[0].StartElection() go nodeCollections[1].StartElection() time.Sleep(time.Second) CheckOneLeader(t, nodeCollections) for i := 0; i < n; i++ { CheckTerm(t, nodeCollections[i], 2) } } func TestOverHalfCandidateElection(t *testing.T) { n := 5 var peerIds []string for i := 0; i < n; i++ { peerIds = append(peerIds, strconv.Itoa(i + 1)) } // 结点启动 var quitCollections []chan struct{} var nodeCollections []*nodes.Node threadTransport := nodes.NewThreadTransport() for i := 0; i < n; i++ { n, quitChan := ExecuteStaticNodeI(strconv.Itoa(i + 1), false, peerIds, threadTransport) quitCollections = append(quitCollections, quitChan) nodeCollections = append(nodeCollections, n) } StopElectionReset(nodeCollections, quitCollections) // 通知所有node结束 defer func(){ for _, quitChan := range quitCollections { close(quitChan) } }() for i := 0; i < n; i++ { nodeCollections[i].State = nodes.Follower } go nodeCollections[0].StartElection() go nodeCollections[1].StartElection() go nodeCollections[2].StartElection() time.Sleep(time.Second) CheckZeroOrOneLeader(t, nodeCollections) for i := 0; i < n; i++ { CheckTerm(t, nodeCollections[i], 2) } }