talent-plan-tinykv/kv/test_raftstore/test_test.go

742 lines
23 KiB
Go
Raw Normal View History

package test_raftstore
import (
"bytes"
"fmt"
"math/rand"
_ "net/http/pprof"
"strconv"
"strings"
"sync/atomic"
"testing"
"time"
"github.com/Connor1996/badger"
"github.com/pingcap-incubator/tinykv/kv/config"
"github.com/pingcap-incubator/tinykv/kv/raftstore/meta"
"github.com/pingcap-incubator/tinykv/kv/util/engine_util"
"github.com/pingcap-incubator/tinykv/log"
"github.com/pingcap-incubator/tinykv/proto/pkg/raft_cmdpb"
"github.com/stretchr/testify/assert"
)
// a client runs the function f and then signals it is done
func runClient(t *testing.T, me int, ca chan bool, fn func(me int, t *testing.T)) {
ok := false
defer func() { ca <- ok }()
fn(me, t)
ok = true
}
// spawn ncli clients and wait until they are all done
func SpawnClientsAndWait(t *testing.T, ch chan bool, ncli int, fn func(me int, t *testing.T)) {
defer func() { ch <- true }()
ca := make([]chan bool, ncli)
for cli := 0; cli < ncli; cli++ {
ca[cli] = make(chan bool)
go runClient(t, cli, ca[cli], fn)
}
// log.Printf("SpawnClientsAndWait: waiting for clients")
for cli := 0; cli < ncli; cli++ {
ok := <-ca[cli]
// log.Infof("SpawnClientsAndWait: client %d is done\n", cli)
if ok == false {
t.Fatalf("failure")
}
}
}
// predict effect of Append(k, val) if old value is prev.
func NextValue(prev string, val string) string {
return prev + val
}
// check that for a specific client all known appends are present in a value,
// and in order
func checkClntAppends(t *testing.T, clnt int, v string, count int) {
lastoff := -1
for j := 0; j < count; j++ {
wanted := "x " + strconv.Itoa(clnt) + " " + strconv.Itoa(j) + " y"
off := strings.Index(v, wanted)
if off < 0 {
t.Fatalf("%v missing element %v in Append result %v", clnt, wanted, v)
}
off1 := strings.LastIndex(v, wanted)
if off1 != off {
t.Fatalf("duplicate element %v in Append result", wanted)
}
if off <= lastoff {
t.Fatalf("wrong order for element %v in Append result", wanted)
}
lastoff = off
}
}
// check that all known appends are present in a value,
// and are in order for each concurrent client.
func checkConcurrentAppends(t *testing.T, v string, counts []int) {
nclients := len(counts)
for i := 0; i < nclients; i++ {
2022-01-11 17:38:37 +08:00
checkClntAppends(t, i, v, counts[i])
}
}
// make network chaos among servers
func networkchaos(t *testing.T, cluster *Cluster, ch chan bool, done *int32, unreliable bool, partitions bool, electionTimeout time.Duration) {
defer func() { ch <- true }()
for atomic.LoadInt32(done) == 0 {
if partitions {
a := make([]int, cluster.count)
for i := 0; i < cluster.count; i++ {
a[i] = (rand.Int() % 2)
}
pa := make([][]uint64, 2)
for i := 0; i < 2; i++ {
pa[i] = make([]uint64, 0)
for j := 1; j <= cluster.count; j++ {
if a[j-1] == i {
pa[i] = append(pa[i], uint64(j))
}
}
}
cluster.ClearFilters()
log.Infof("partition: %v, %v", pa[0], pa[1])
cluster.AddFilter(&PartitionFilter{
s1: pa[0],
s2: pa[1],
})
}
if unreliable {
cluster.AddFilter(&DropFilter{})
}
time.Sleep(electionTimeout + time.Duration(rand.Int63()%200)*time.Millisecond)
}
}
func confchanger(t *testing.T, cluster *Cluster, ch chan bool, done *int32) {
defer func() { ch <- true }()
count := uint64(cluster.count)
for atomic.LoadInt32(done) == 0 {
region := cluster.GetRandomRegion()
store := rand.Uint64()%count + 1
if p := FindPeer(region, store); p != nil {
if len(region.GetPeers()) > 1 {
cluster.MustRemovePeer(region.GetId(), p)
}
} else {
cluster.MustAddPeer(region.GetId(), cluster.AllocPeer(store))
}
time.Sleep(time.Duration(rand.Int63()%200) * time.Millisecond)
}
}
// Basic test is as follows: one or more clients submitting Put/Scan
// operations to set of servers for some period of time. After the period is
// over, test checks that all sequential values are present and in order for a
// particular key and perform Delete to clean up.
// - If unreliable is set, RPCs may fail.
// - If crash is set, the servers restart after the period is over.
// - If partitions is set, the test repartitions the network concurrently between the servers.
// - If maxraftlog is a positive number, the count of the persistent log for Raft shouldn't exceed 2*maxraftlog.
2022-01-11 17:38:37 +08:00
// - If confchange is set, the cluster will schedule random conf change concurrently.
// - If split is set, split region when size exceed 1024 bytes.
func GenericTest(t *testing.T, part string, nclients int, unreliable bool, crash bool, partitions bool, maxraftlog int, confchange bool, split bool) {
title := "Test: "
if unreliable {
// the network drops RPC requests and replies.
title = title + "unreliable net, "
}
if crash {
// peers re-start, and thus persistence must work.
title = title + "restarts, "
}
if partitions {
// the network may partition
title = title + "partitions, "
}
if maxraftlog != -1 {
title = title + "snapshots, "
}
if nclients > 1 {
title = title + "many clients"
} else {
title = title + "one client"
}
title = title + " (" + part + ")" // 3A or 3B
nservers := 5
cfg := config.NewTestConfig()
if maxraftlog != -1 {
cfg.RaftLogGcCountLimit = uint64(maxraftlog)
}
if split {
cfg.RegionMaxSize = 300
cfg.RegionSplitSize = 200
}
cluster := NewTestCluster(nservers, cfg)
cluster.Start()
defer cluster.Shutdown()
electionTimeout := cfg.RaftBaseTickInterval * time.Duration(cfg.RaftElectionTimeoutTicks)
// Wait for leader election
time.Sleep(2 * electionTimeout)
done_partitioner := int32(0)
done_confchanger := int32(0)
done_clients := int32(0)
ch_partitioner := make(chan bool)
ch_confchange := make(chan bool)
ch_clients := make(chan bool)
clnts := make([]chan int, nclients)
for i := 0; i < nclients; i++ {
clnts[i] = make(chan int, 1)
}
for i := 0; i < 3; i++ {
// log.Printf("Iteration %v\n", i)
atomic.StoreInt32(&done_clients, 0)
atomic.StoreInt32(&done_partitioner, 0)
go SpawnClientsAndWait(t, ch_clients, nclients, func(cli int, t *testing.T) {
j := 0
defer func() {
clnts[cli] <- j
}()
last := ""
for atomic.LoadInt32(&done_clients) == 0 {
if (rand.Int() % 1000) < 500 {
key := strconv.Itoa(cli) + " " + fmt.Sprintf("%08d", j)
value := "x " + strconv.Itoa(cli) + " " + strconv.Itoa(j) + " y"
// log.Infof("%d: client new put %v,%v\n", cli, key, value)
cluster.MustPut([]byte(key), []byte(value))
last = NextValue(last, value)
j++
} else {
start := strconv.Itoa(cli) + " " + fmt.Sprintf("%08d", 0)
end := strconv.Itoa(cli) + " " + fmt.Sprintf("%08d", j)
// log.Infof("%d: client new scan %v-%v\n", cli, start, end)
values := cluster.Scan([]byte(start), []byte(end))
v := string(bytes.Join(values, []byte("")))
if v != last {
log.Fatalf("get wrong value, client %v\nwant:%v\ngot: %v\n", cli, last, v)
}
}
}
})
if unreliable || partitions {
// Allow the clients to perform some operations without interruption
time.Sleep(300 * time.Millisecond)
go networkchaos(t, cluster, ch_partitioner, &done_partitioner, unreliable, partitions, electionTimeout)
}
if confchange {
// Allow the clients to perfrom some operations without interruption
time.Sleep(100 * time.Millisecond)
go confchanger(t, cluster, ch_confchange, &done_confchanger)
}
time.Sleep(5 * time.Second)
atomic.StoreInt32(&done_clients, 1) // tell clients to quit
atomic.StoreInt32(&done_partitioner, 1) // tell partitioner to quit
atomic.StoreInt32(&done_confchanger, 1) // tell confchanger to quit
if unreliable || partitions {
// log.Printf("wait for partitioner\n")
<-ch_partitioner
// reconnect network and submit a request. A client may
// have submitted a request in a minority. That request
// won't return until that server discovers a new term
// has started.
cluster.ClearFilters()
// wait for a while so that we have a new term
time.Sleep(electionTimeout)
}
// log.Printf("wait for clients\n")
<-ch_clients
if crash {
log.Warnf("shutdown servers\n")
for i := 1; i <= nservers; i++ {
cluster.StopServer(uint64(i))
}
// Wait for a while for servers to shutdown, since
// shutdown isn't a real crash and isn't instantaneous
time.Sleep(electionTimeout)
log.Warnf("restart servers\n")
// crash and re-start all
for i := 1; i <= nservers; i++ {
cluster.StartServer(uint64(i))
}
}
for cli := 0; cli < nclients; cli++ {
// log.Printf("read from clients %d\n", cli)
j := <-clnts[cli]
// if j < 10 {
// log.Printf("Warning: client %d managed to perform only %d put operations in 1 sec?\n", i, j)
// }
start := strconv.Itoa(cli) + " " + fmt.Sprintf("%08d", 0)
end := strconv.Itoa(cli) + " " + fmt.Sprintf("%08d", j)
values := cluster.Scan([]byte(start), []byte(end))
v := string(bytes.Join(values, []byte("")))
checkClntAppends(t, cli, v, j)
for k := 0; k < j; k++ {
key := strconv.Itoa(cli) + " " + fmt.Sprintf("%08d", k)
cluster.MustDelete([]byte(key))
}
}
if maxraftlog > 0 {
time.Sleep(1 * time.Second)
// Check maximum after the servers have processed all client
// requests and had time to checkpoint.
key := []byte("")
for {
region := cluster.GetRegion(key)
if region == nil {
panic("region is not found")
}
for _, engine := range cluster.engines {
state, err := meta.GetApplyState(engine.Kv, region.GetId())
if err == badger.ErrKeyNotFound {
continue
}
if err != nil {
panic(err)
}
truncatedIdx := state.TruncatedState.Index
appliedIdx := state.AppliedIndex
if appliedIdx-truncatedIdx > 2*uint64(maxraftlog) {
t.Fatalf("logs were not trimmed (%v - %v > 2*%v)", appliedIdx, truncatedIdx, maxraftlog)
}
}
key = region.EndKey
if len(key) == 0 {
break
}
}
}
if split {
r := cluster.GetRegion([]byte(""))
if len(r.GetEndKey()) == 0 {
t.Fatalf("region is not split")
}
}
}
}
func TestBasic2B(t *testing.T) {
// Test: one client (2B) ...
GenericTest(t, "2B", 1, false, false, false, -1, false, false)
}
func TestConcurrent2B(t *testing.T) {
// Test: many clients (2B) ...
GenericTest(t, "2B", 5, false, false, false, -1, false, false)
}
func TestUnreliable2B(t *testing.T) {
// Test: unreliable net, many clients (2B) ...
GenericTest(t, "2B", 5, true, false, false, -1, false, false)
}
// Submit a request in the minority partition and check that the requests
// doesn't go through until the partition heals. The leader in the original
// network ends up in the minority partition.
func TestOnePartition2B(t *testing.T) {
cfg := config.NewTestConfig()
cluster := NewTestCluster(5, cfg)
cluster.Start()
defer cluster.Shutdown()
region := cluster.GetRegion([]byte(""))
leader := cluster.LeaderOfRegion(region.GetId())
s1 := []uint64{leader.GetStoreId()}
s2 := []uint64{}
for _, p := range region.GetPeers() {
if p.GetId() == leader.GetId() {
continue
}
if len(s1) < 3 {
s1 = append(s1, p.GetStoreId())
} else {
s2 = append(s2, p.GetStoreId())
}
}
// leader in majority, partition doesn't affect write/read
cluster.AddFilter(&PartitionFilter{
s1: s1,
s2: s2,
})
cluster.MustPut([]byte("k1"), []byte("v1"))
cluster.MustGet([]byte("k1"), []byte("v1"))
MustGetNone(cluster.engines[s2[0]], []byte("k1"))
MustGetNone(cluster.engines[s2[1]], []byte("k1"))
cluster.ClearFilters()
// old leader in minority, new leader should be elected
s2 = append(s2, s1[2])
s1 = s1[:2]
cluster.AddFilter(&PartitionFilter{
s1: s1,
s2: s2,
})
cluster.MustGet([]byte("k1"), []byte("v1"))
cluster.MustPut([]byte("k1"), []byte("changed"))
MustGetEqual(cluster.engines[s1[0]], []byte("k1"), []byte("v1"))
MustGetEqual(cluster.engines[s1[1]], []byte("k1"), []byte("v1"))
cluster.ClearFilters()
// when partition heals, old leader should sync data
cluster.MustPut([]byte("k2"), []byte("v2"))
MustGetEqual(cluster.engines[s1[0]], []byte("k2"), []byte("v2"))
MustGetEqual(cluster.engines[s1[0]], []byte("k1"), []byte("changed"))
}
func TestManyPartitionsOneClient2B(t *testing.T) {
// Test: partitions, one client (2B) ...
GenericTest(t, "2B", 1, false, false, true, -1, false, false)
}
func TestManyPartitionsManyClients2B(t *testing.T) {
// Test: partitions, many clients (2B) ...
GenericTest(t, "2B", 5, false, false, true, -1, false, false)
}
func TestPersistOneClient2B(t *testing.T) {
// Test: restarts, one client (2B) ...
GenericTest(t, "2B", 1, false, true, false, -1, false, false)
}
func TestPersistConcurrent2B(t *testing.T) {
// Test: restarts, many clients (2B) ...
GenericTest(t, "2B", 5, false, true, false, -1, false, false)
}
func TestPersistConcurrentUnreliable2B(t *testing.T) {
// Test: unreliable net, restarts, many clients (2B) ...
GenericTest(t, "2B", 5, true, true, false, -1, false, false)
}
func TestPersistPartition2B(t *testing.T) {
// Test: restarts, partitions, many clients (2B) ...
GenericTest(t, "2B", 5, false, true, true, -1, false, false)
}
func TestPersistPartitionUnreliable2B(t *testing.T) {
// Test: unreliable net, restarts, partitions, many clients (2B) ...
GenericTest(t, "2B", 5, true, true, true, -1, false, false)
}
func TestOneSnapshot2C(t *testing.T) {
cfg := config.NewTestConfig()
cfg.RaftLogGcCountLimit = 10
cluster := NewTestCluster(3, cfg)
cluster.Start()
defer cluster.Shutdown()
cf := engine_util.CfLock
cluster.MustPutCF(cf, []byte("k1"), []byte("v1"))
cluster.MustPutCF(cf, []byte("k2"), []byte("v2"))
MustGetCfEqual(cluster.engines[1], cf, []byte("k1"), []byte("v1"))
MustGetCfEqual(cluster.engines[1], cf, []byte("k2"), []byte("v2"))
for _, engine := range cluster.engines {
state, err := meta.GetApplyState(engine.Kv, 1)
if err != nil {
t.Fatal(err)
}
if state.TruncatedState.Index != meta.RaftInitLogIndex ||
state.TruncatedState.Term != meta.RaftInitLogTerm {
t.Fatalf("unexpected truncated state %v", state.TruncatedState)
}
}
cluster.AddFilter(
&PartitionFilter{
s1: []uint64{1},
s2: []uint64{2, 3},
},
)
// write some data to trigger snapshot
for i := 100; i < 115; i++ {
cluster.MustPutCF(cf, []byte(fmt.Sprintf("k%d", i)), []byte(fmt.Sprintf("v%d", i)))
}
cluster.MustDeleteCF(cf, []byte("k2"))
time.Sleep(500 * time.Millisecond)
MustGetCfNone(cluster.engines[1], cf, []byte("k100"))
cluster.ClearFilters()
// Now snapshot must applied on
MustGetCfEqual(cluster.engines[1], cf, []byte("k1"), []byte("v1"))
MustGetCfEqual(cluster.engines[1], cf, []byte("k100"), []byte("v100"))
MustGetCfNone(cluster.engines[1], cf, []byte("k2"))
cluster.StopServer(1)
cluster.StartServer(1)
MustGetCfEqual(cluster.engines[1], cf, []byte("k1"), []byte("v1"))
for _, engine := range cluster.engines {
state, err := meta.GetApplyState(engine.Kv, 1)
if err != nil {
t.Fatal(err)
}
truncatedIdx := state.TruncatedState.Index
appliedIdx := state.AppliedIndex
if appliedIdx-truncatedIdx > 2*uint64(cfg.RaftLogGcCountLimit) {
t.Fatalf("logs were not trimmed (%v - %v > 2*%v)", appliedIdx, truncatedIdx, cfg.RaftLogGcCountLimit)
}
}
}
func TestSnapshotRecover2C(t *testing.T) {
// Test: restarts, snapshots, one client (2C) ...
GenericTest(t, "2C", 1, false, true, false, 100, false, false)
}
func TestSnapshotRecoverManyClients2C(t *testing.T) {
// Test: restarts, snapshots, many clients (2C) ...
GenericTest(t, "2C", 20, false, true, false, 100, false, false)
}
func TestSnapshotUnreliable2C(t *testing.T) {
// Test: unreliable net, snapshots, many clients (2C) ...
GenericTest(t, "2C", 5, true, false, false, 100, false, false)
}
func TestSnapshotUnreliableRecover2C(t *testing.T) {
// Test: unreliable net, restarts, snapshots, many clients (2C) ...
GenericTest(t, "2C", 5, true, true, false, 100, false, false)
}
func TestSnapshotUnreliableRecoverConcurrentPartition2C(t *testing.T) {
// Test: unreliable net, restarts, partitions, snapshots, many clients (2C) ...
GenericTest(t, "2C", 5, true, true, true, 100, false, false)
}
func TestTransferLeader3B(t *testing.T) {
cfg := config.NewTestConfig()
cluster := NewTestCluster(5, cfg)
cluster.Start()
defer cluster.Shutdown()
regionID := cluster.GetRegion([]byte("")).GetId()
cluster.MustTransferLeader(regionID, NewPeer(1, 1))
cluster.MustTransferLeader(regionID, NewPeer(2, 2))
cluster.MustTransferLeader(regionID, NewPeer(3, 3))
cluster.MustTransferLeader(regionID, NewPeer(4, 4))
cluster.MustTransferLeader(regionID, NewPeer(5, 5))
}
func TestBasicConfChange3B(t *testing.T) {
cfg := config.NewTestConfig()
cluster := NewTestCluster(5, cfg)
cluster.Start()
defer cluster.Shutdown()
cluster.MustTransferLeader(1, NewPeer(1, 1))
cluster.MustRemovePeer(1, NewPeer(2, 2))
cluster.MustRemovePeer(1, NewPeer(3, 3))
cluster.MustRemovePeer(1, NewPeer(4, 4))
cluster.MustRemovePeer(1, NewPeer(5, 5))
// now region 1 only has peer: (1, 1)
cluster.MustPut([]byte("k1"), []byte("v1"))
MustGetNone(cluster.engines[2], []byte("k1"))
// add peer (2, 2) to region 1
cluster.MustAddPeer(1, NewPeer(2, 2))
cluster.MustPut([]byte("k2"), []byte("v2"))
cluster.MustGet([]byte("k2"), []byte("v2"))
MustGetEqual(cluster.engines[2], []byte("k1"), []byte("v1"))
MustGetEqual(cluster.engines[2], []byte("k2"), []byte("v2"))
epoch := cluster.GetRegion([]byte("k1")).GetRegionEpoch()
assert.True(t, epoch.GetConfVer() > 1)
// peer 5 must not exist
MustGetNone(cluster.engines[5], []byte("k1"))
// add peer (3, 3) to region 1
cluster.MustAddPeer(1, NewPeer(3, 3))
cluster.MustRemovePeer(1, NewPeer(2, 2))
cluster.MustPut([]byte("k3"), []byte("v3"))
cluster.MustGet([]byte("k3"), []byte("v3"))
MustGetEqual(cluster.engines[3], []byte("k1"), []byte("v1"))
MustGetEqual(cluster.engines[3], []byte("k2"), []byte("v2"))
MustGetEqual(cluster.engines[3], []byte("k3"), []byte("v3"))
// peer 2 has nothing
MustGetNone(cluster.engines[2], []byte("k1"))
MustGetNone(cluster.engines[2], []byte("k2"))
cluster.MustAddPeer(1, NewPeer(2, 2))
MustGetEqual(cluster.engines[2], []byte("k1"), []byte("v1"))
MustGetEqual(cluster.engines[2], []byte("k2"), []byte("v2"))
MustGetEqual(cluster.engines[2], []byte("k3"), []byte("v3"))
// remove peer (2, 2) from region 1
cluster.MustRemovePeer(1, NewPeer(2, 2))
// add peer (2, 4) to region 1
cluster.MustAddPeer(1, NewPeer(2, 4))
// remove peer (3, 3) from region 1
cluster.MustRemovePeer(1, NewPeer(3, 3))
cluster.MustPut([]byte("k4"), []byte("v4"))
MustGetEqual(cluster.engines[2], []byte("k1"), []byte("v1"))
MustGetEqual(cluster.engines[2], []byte("k4"), []byte("v4"))
MustGetNone(cluster.engines[3], []byte("k1"))
MustGetNone(cluster.engines[3], []byte("k4"))
}
func TestConfChangeRemoveLeader3B(t *testing.T) {
cfg := config.NewTestConfig()
cluster := NewTestCluster(5, cfg)
cluster.Start()
defer cluster.Shutdown()
cluster.MustTransferLeader(1, NewPeer(1, 1))
// remove (1,1) and put (k0,v0), store 1 can not see it
cluster.MustRemovePeer(1, NewPeer(1, 1))
cluster.MustPut([]byte("k0"), []byte("v0"))
MustGetNone(cluster.engines[1], []byte("k0"))
// rejoin and become leader, now store 1 can see it
cluster.MustAddPeer(1, NewPeer(1, 1))
cluster.MustTransferLeader(1, NewPeer(1, 1))
cluster.MustPut([]byte("k1"), []byte("v1"))
MustGetEqual(cluster.engines[1], []byte("k0"), []byte("v0"))
MustGetEqual(cluster.engines[1], []byte("k1"), []byte("v1"))
cluster.MustRemovePeer(1, NewPeer(2, 2))
cluster.MustRemovePeer(1, NewPeer(3, 3))
cluster.MustRemovePeer(1, NewPeer(4, 4))
// now only have (1,1) and (5,5), try to remove (1,1)
cluster.MustRemovePeer(1, NewPeer(1, 1))
// now region 1 only has peer: (5, 5)
cluster.MustPut([]byte("k2"), []byte("v2"))
MustGetNone(cluster.engines[1], []byte("k2"))
// now have (1,1) and (5,5)
cluster.MustAddPeer(1, NewPeer(1, 1))
cluster.MustPut([]byte("k3"), []byte("v3"))
// 3 can not see it
MustGetNone(cluster.engines[3], []byte("k3"))
MustGetEqual(cluster.engines[1], []byte("k3"), []byte("v3"))
}
func TestConfChangeRecover3B(t *testing.T) {
// Test: restarts, snapshots, conf change, one client (3B) ...
GenericTest(t, "3B", 1, false, true, false, -1, true, false)
}
func TestConfChangeRecoverManyClients3B(t *testing.T) {
// Test: restarts, snapshots, conf change, many clients (3B) ...
GenericTest(t, "3B", 20, false, true, false, -1, true, false)
}
func TestConfChangeUnreliable3B(t *testing.T) {
// Test: unreliable net, snapshots, conf change, many clients (3B) ...
GenericTest(t, "3B", 5, true, false, false, -1, true, false)
}
func TestConfChangeUnreliableRecover3B(t *testing.T) {
// Test: unreliable net, restarts, snapshots, conf change, many clients (3B) ...
GenericTest(t, "3B", 5, true, true, false, -1, true, false)
}
func TestConfChangeSnapshotUnreliableRecover3B(t *testing.T) {
// Test: unreliable net, restarts, snapshots, conf change, many clients (3B) ...
GenericTest(t, "3B", 5, true, true, false, 100, true, false)
}
func TestConfChangeSnapshotUnreliableRecoverConcurrentPartition3B(t *testing.T) {
// Test: unreliable net, restarts, partitions, snapshots, conf change, many clients (3B) ...
GenericTest(t, "3B", 5, true, true, true, 100, true, false)
}
func TestOneSplit3B(t *testing.T) {
cfg := config.NewTestConfig()
cfg.RegionMaxSize = 800
cfg.RegionSplitSize = 500
cluster := NewTestCluster(5, cfg)
cluster.Start()
defer cluster.Shutdown()
cluster.MustPut([]byte("k1"), []byte("v1"))
cluster.MustPut([]byte("k2"), []byte("v2"))
region := cluster.GetRegion([]byte("k1"))
region1 := cluster.GetRegion([]byte("k2"))
assert.Equal(t, region.GetId(), region1.GetId())
cluster.AddFilter(
&PartitionFilter{
s1: []uint64{1, 2, 3, 4},
s2: []uint64{5},
},
)
// write some data to trigger split
for i := 100; i < 200; i++ {
cluster.MustPut([]byte(fmt.Sprintf("k%d", i)), []byte(fmt.Sprintf("v%d", i)))
}
time.Sleep(200 * time.Millisecond)
cluster.ClearFilters()
left := cluster.GetRegion([]byte("k1"))
right := cluster.GetRegion([]byte("k2"))
assert.NotEqual(t, left.GetId(), right.GetId())
assert.True(t, bytes.Equal(region.GetStartKey(), left.GetStartKey()))
assert.True(t, bytes.Equal(left.GetEndKey(), right.GetStartKey()))
assert.True(t, bytes.Equal(right.GetEndKey(), region.GetEndKey()))
req := NewRequest(left.GetId(), left.GetRegionEpoch(), []*raft_cmdpb.Request{NewGetCfCmd(engine_util.CfDefault, []byte("k2"))})
resp, _ := cluster.CallCommandOnLeader(&req, time.Second)
assert.NotNil(t, resp.GetHeader().GetError())
assert.NotNil(t, resp.GetHeader().GetError().GetKeyNotInRegion())
MustGetEqual(cluster.engines[5], []byte("k100"), []byte("v100"))
}
func TestSplitRecover3B(t *testing.T) {
// Test: restarts, snapshots, conf change, one client (3B) ...
GenericTest(t, "3B", 1, false, true, false, -1, false, true)
}
func TestSplitRecoverManyClients3B(t *testing.T) {
// Test: restarts, snapshots, conf change, many clients (3B) ...
GenericTest(t, "3B", 20, false, true, false, -1, false, true)
}
func TestSplitUnreliable3B(t *testing.T) {
// Test: unreliable net, snapshots, conf change, many clients (3B) ...
GenericTest(t, "3B", 5, true, false, false, -1, false, true)
}
func TestSplitUnreliableRecover3B(t *testing.T) {
// Test: unreliable net, restarts, snapshots, conf change, many clients (3B) ...
GenericTest(t, "3B", 5, true, true, false, -1, false, true)
}
func TestSplitConfChangeSnapshotUnreliableRecover3B(t *testing.T) {
// Test: unreliable net, restarts, snapshots, conf change, many clients (3B) ...
GenericTest(t, "3B", 5, true, true, false, 100, true, true)
}
func TestSplitConfChangeSnapshotUnreliableRecoverConcurrentPartition3B(t *testing.T) {
// Test: unreliable net, restarts, partitions, snapshots, conf change, many clients (3B) ...
GenericTest(t, "3B", 5, true, true, true, 100, true, true)
}