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++ { 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. // - 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) }