package threadTest import ( "fmt" "os" "simple-kv-store/internal/client" "simple-kv-store/internal/nodes" "strconv" "testing" "time" "github.com/syndtr/goleveldb/leveldb" ) func ExecuteNodeI(id string, isRestart bool, peerIds []string, threadTransport *nodes.ThreadTransport) (*nodes.Node, chan struct{}) { if !isRestart { os.RemoveAll("storage/node" + id) } // 创建临时目录用于 leveldb dbPath, err := os.MkdirTemp("", "simple-kv-store-"+id+"-") if err != nil { panic(fmt.Sprintf("无法创建临时数据库目录: %s", err)) } // 创建临时目录用于 storage storagePath, err := os.MkdirTemp("", "raft-storage-"+id+"-") if err != nil { panic(fmt.Sprintf("无法创建临时存储目录: %s", err)) } db, err := leveldb.OpenFile(dbPath, nil) if err != nil { panic(fmt.Sprintf("Failed to open database: %s", err)) } // 初始化 Raft 存储 storage := nodes.NewRaftStorage(storagePath) 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 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) { for i := 0; i < len(nodeCollections); i++ { node := nodeCollections[i] go func(node *nodes.Node) { ticker := time.NewTicker(400 * time.Millisecond) defer ticker.Stop() for { <-ticker.C node.ResetElectionTimer() // 不主动触发选举 } }(node) } } func SendKvCall(kvCall *nodes.LogEntryCall, node *nodes.Node) { node.Mu.Lock() defer node.Mu.Unlock() node.MaxLogId++ logId := node.MaxLogId rLogE := nodes.RaftLogEntry{LogE: kvCall.LogE,LogId: logId, Term: node.CurrTerm} node.Log = append(node.Log, rLogE) node.Storage.AppendLog(rLogE) // 广播给其它节点 node.BroadCastKV() } func ClientWriteLog(t *testing.T, startLogid int, endLogid int, cWrite *clientPkg.Client) { var s clientPkg.Status for i := startLogid; i < endLogid; i++ { key := strconv.Itoa(i) newlog := nodes.LogEntry{Key: key, Value: "hello"} s = cWrite.Write(newlog) if s != clientPkg.Ok { t.Errorf("write test fail") } } } func FindLeader(t *testing.T, nodeCollections []* nodes.Node) (i int) { for i, node := range nodeCollections { if node.State == nodes.Leader { return i } } t.Errorf("系统目前没有leader") t.FailNow() return 0 } func CheckOneLeader(t *testing.T, nodeCollections []* nodes.Node) { cnt := 0 for _, node := range nodeCollections { node.Mu.Lock() if node.State == nodes.Leader { cnt++ } node.Mu.Unlock() } if cnt != 1 { t.Errorf("实际有%d个leader(!=1)", cnt) t.FailNow() } } func CheckNoLeader(t *testing.T, nodeCollections []* nodes.Node) { cnt := 0 for _, node := range nodeCollections { node.Mu.Lock() if node.State == nodes.Leader { cnt++ } node.Mu.Unlock() } if cnt != 0 { t.Errorf("实际有%d个leader(!=0)", cnt) t.FailNow() } } func CheckZeroOrOneLeader(t *testing.T, nodeCollections []* nodes.Node) { cnt := 0 for _, node := range nodeCollections { node.Mu.Lock() if node.State == nodes.Leader { cnt++ } node.Mu.Unlock() } if cnt > 1 { errmsg := fmt.Sprintf("实际有%d个leader(>1)", cnt) WriteFailLog(nodeCollections[0].SelfId, errmsg) t.Error(errmsg) t.FailNow() } } func CheckIsLeader(t *testing.T, node *nodes.Node) { node.Mu.Lock() defer node.Mu.Unlock() if node.State != nodes.Leader { t.Errorf("[%s]不是leader", node.SelfId) t.FailNow() } } func CheckTerm(t *testing.T, node *nodes.Node, targetTerm int) { node.Mu.Lock() defer node.Mu.Unlock() if node.CurrTerm != targetTerm { t.Errorf("[%s]实际term=%d (!=%d)", node.SelfId, node.CurrTerm, targetTerm) t.FailNow() } } func CheckLogNum(t *testing.T, node *nodes.Node, targetnum int) { node.Mu.Lock() defer node.Mu.Unlock() if len(node.Log) != targetnum { t.Errorf("[%s]实际logNum=%d (!=%d)", node.SelfId, len(node.Log), targetnum) t.FailNow() } } func CheckSameLog(t *testing.T, nodeCollections []* nodes.Node) { nodeCollections[0].Mu.Lock() defer nodeCollections[0].Mu.Unlock() standard_node := nodeCollections[0] for i, node := range nodeCollections { if i != 0 { node.Mu.Lock() if len(node.Log) != len(standard_node.Log) { errmsg := fmt.Sprintf("[1]和[%s]日志数量不一致", node.SelfId) WriteFailLog(node.SelfId, errmsg) t.Error(errmsg) t.FailNow() } for idx, log := range node.Log { standard_log := standard_node.Log[idx] if log.Term != standard_log.Term || log.LogE.Key != standard_log.LogE.Key || log.LogE.Value != standard_log.LogE.Value { errmsg := fmt.Sprintf("[1]和[%s]日志id%d不一致", node.SelfId, idx) WriteFailLog(node.SelfId, errmsg) t.Error(errmsg) t.FailNow() } } node.Mu.Unlock() } } } func WriteFailLog(name string, errmsg string) { f, _ := os.Create(name + ".log") fmt.Fprint(f, errmsg) f.Close() }