|
|
@ -0,0 +1,217 @@ |
|
|
|
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) |
|
|
|
} |
|
|
|
} |