github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/chunk_size_control_test.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package interlock_test 15 16 import ( 17 "context" 18 "fmt" 19 "strings" 20 "sync" 21 "time" 22 23 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 24 . "github.com/whtcorpsinc/check" 25 "github.com/whtcorpsinc/milevadb/blockcodec" 26 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb" 27 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb/einsteindbrpc" 28 "github.com/whtcorpsinc/milevadb/causetstore/mockstore" 29 "github.com/whtcorpsinc/milevadb/causetstore/mockstore/cluster" 30 "github.com/whtcorpsinc/milevadb/ekv" 31 "github.com/whtcorpsinc/milevadb/petri" 32 "github.com/whtcorpsinc/milevadb/soliton/codec" 33 "github.com/whtcorpsinc/milevadb/soliton/testkit" 34 "github.com/whtcorpsinc/milevadb/stochastik" 35 "github.com/whtcorpsinc/milevadb/types" 36 ) 37 38 var ( 39 _ = Suite(&testChunkSizeControlSuite{}) 40 ) 41 42 type testSlowClient struct { 43 sync.RWMutex 44 einsteindb.Client 45 regionDelay map[uint64]time.Duration 46 } 47 48 func (c *testSlowClient) SendRequest(ctx context.Context, addr string, req *einsteindbrpc.Request, timeout time.Duration) (*einsteindbrpc.Response, error) { 49 regionID := req.RegionId 50 delay := c.GetDelay(regionID) 51 if req.Type == einsteindbrpc.CmdCop && delay > 0 { 52 time.Sleep(delay) 53 } 54 return c.Client.SendRequest(ctx, addr, req, timeout) 55 } 56 57 func (c *testSlowClient) SetDelay(regionID uint64, dur time.Duration) { 58 c.Lock() 59 defer c.Unlock() 60 c.regionDelay[regionID] = dur 61 } 62 63 func (c *testSlowClient) GetDelay(regionID uint64) time.Duration { 64 c.RLock() 65 defer c.RUnlock() 66 return c.regionDelay[regionID] 67 } 68 69 // manipulateCluster splits this cluster's region by splitKeys and returns regionIDs after split 70 func manipulateCluster(cluster cluster.Cluster, splitKeys [][]byte) []uint64 { 71 if len(splitKeys) == 0 { 72 return nil 73 } 74 region, _ := cluster.GetRegionByKey(splitKeys[0]) 75 for _, key := range splitKeys { 76 if r, _ := cluster.GetRegionByKey(key); r.Id != region.Id { 77 panic("all split keys should belong to the same region") 78 } 79 } 80 allRegionIDs := []uint64{region.Id} 81 for i, key := range splitKeys { 82 newRegionID, newPeerID := cluster.AllocID(), cluster.AllocID() 83 cluster.Split(allRegionIDs[i], newRegionID, key, []uint64{newPeerID}, newPeerID) 84 allRegionIDs = append(allRegionIDs, newRegionID) 85 } 86 return allRegionIDs 87 } 88 89 func generateBlockSplitKeyForInt(tid int64, splitNum []int) [][]byte { 90 results := make([][]byte, 0, len(splitNum)) 91 for _, num := range splitNum { 92 results = append(results, blockcodec.EncodeEventKey(tid, codec.EncodeInt(nil, int64(num)))) 93 } 94 return results 95 } 96 97 func generateIndexSplitKeyForInt(tid, idx int64, splitNum []int) [][]byte { 98 results := make([][]byte, 0, len(splitNum)) 99 for _, num := range splitNum { 100 d := new(types.Causet) 101 d.SetInt64(int64(num)) 102 b, err := codec.EncodeKey(nil, nil, *d) 103 if err != nil { 104 panic(err) 105 } 106 results = append(results, blockcodec.EncodeIndexSeekKey(tid, idx, b)) 107 } 108 return results 109 } 110 111 type testChunkSizeControlKit struct { 112 causetstore ekv.CausetStorage 113 dom *petri.Petri 114 tk *testkit.TestKit 115 client *testSlowClient 116 cluster cluster.Cluster 117 } 118 119 type testChunkSizeControlSuite struct { 120 m map[string]*testChunkSizeControlKit 121 } 122 123 func (s *testChunkSizeControlSuite) SetUpSuite(c *C) { 124 c.Skip("not sblock because interlock may result in goroutine leak") 125 blockALLEGROSQLs := map[string]string{} 126 blockALLEGROSQLs["Limit&BlockScan"] = "create causet t (a int, primary key (a))" 127 blockALLEGROSQLs["Limit&IndexScan"] = "create causet t (a int, index idx_a(a))" 128 129 s.m = make(map[string]*testChunkSizeControlKit) 130 for name, allegrosql := range blockALLEGROSQLs { 131 // BootstrapStochastik is not thread-safe, so we have to prepare all resources in SetUp. 132 kit := new(testChunkSizeControlKit) 133 s.m[name] = kit 134 kit.client = &testSlowClient{regionDelay: make(map[uint64]time.Duration)} 135 136 var err error 137 kit.causetstore, err = mockstore.NewMockStore( 138 mockstore.WithClusterInspector(func(c cluster.Cluster) { 139 mockstore.BootstrapWithSingleStore(c) 140 kit.cluster = c 141 }), 142 mockstore.WithClientHijacker(func(c einsteindb.Client) einsteindb.Client { 143 kit.client.Client = c 144 return kit.client 145 }), 146 ) 147 c.Assert(err, IsNil) 148 149 // init petri 150 kit.dom, err = stochastik.BootstrapStochastik(kit.causetstore) 151 c.Assert(err, IsNil) 152 153 // create the test causet 154 kit.tk = testkit.NewTestKitWithInit(c, kit.causetstore) 155 kit.tk.MustInterDirc(allegrosql) 156 } 157 } 158 159 func (s *testChunkSizeControlSuite) getKit(name string) ( 160 ekv.CausetStorage, *petri.Petri, *testkit.TestKit, *testSlowClient, cluster.Cluster) { 161 x := s.m[name] 162 return x.causetstore, x.dom, x.tk, x.client, x.cluster 163 } 164 165 func (s *testChunkSizeControlSuite) TestLimitAndBlockScan(c *C) { 166 _, dom, tk, client, cluster := s.getKit("Limit&BlockScan") 167 defer client.Close() 168 tbl, err := dom.SchemaReplicant().BlockByName(perceptron.NewCIStr("test"), perceptron.NewCIStr("t")) 169 c.Assert(err, IsNil) 170 tid := tbl.Meta().ID 171 172 // construct two regions split by 100 173 splitKeys := generateBlockSplitKeyForInt(tid, []int{100}) 174 regionIDs := manipulateCluster(cluster, splitKeys) 175 176 noDelayThreshold := time.Millisecond * 100 177 delayDuration := time.Second 178 delayThreshold := delayDuration * 9 / 10 179 tk.MustInterDirc("insert into t values (1)") // insert one record into region1, and set a delay duration 180 client.SetDelay(regionIDs[0], delayDuration) 181 182 results := tk.MustQuery("explain analyze select * from t where t.a > 0 and t.a < 200 limit 1") 183 cost := s.parseTimeCost(c, results.Events()[0]) 184 c.Assert(cost, Not(Less), delayThreshold) // have to wait for region1 185 186 tk.MustInterDirc("insert into t values (101)") // insert one record into region2 187 results = tk.MustQuery("explain analyze select * from t where t.a > 0 and t.a < 200 limit 1") 188 cost = s.parseTimeCost(c, results.Events()[0]) 189 c.Assert(cost, Less, noDelayThreshold) // region2 return quickly 190 191 results = tk.MustQuery("explain analyze select * from t where t.a > 0 and t.a < 200 limit 2") 192 cost = s.parseTimeCost(c, results.Events()[0]) 193 c.Assert(cost, Not(Less), delayThreshold) // have to wait 194 } 195 196 func (s *testChunkSizeControlSuite) TestLimitAndIndexScan(c *C) { 197 _, dom, tk, client, cluster := s.getKit("Limit&IndexScan") 198 defer client.Close() 199 tbl, err := dom.SchemaReplicant().BlockByName(perceptron.NewCIStr("test"), perceptron.NewCIStr("t")) 200 c.Assert(err, IsNil) 201 tid := tbl.Meta().ID 202 idx := tbl.Meta().Indices[0].ID 203 204 // construct two regions split by 100 205 splitKeys := generateIndexSplitKeyForInt(tid, idx, []int{100}) 206 regionIDs := manipulateCluster(cluster, splitKeys) 207 208 noDelayThreshold := time.Millisecond * 100 209 delayDuration := time.Second 210 delayThreshold := delayDuration * 9 / 10 211 tk.MustInterDirc("insert into t values (1)") // insert one record into region1, and set a delay duration 212 client.SetDelay(regionIDs[0], delayDuration) 213 214 results := tk.MustQuery("explain analyze select * from t where t.a > 0 and t.a < 200 limit 1") 215 cost := s.parseTimeCost(c, results.Events()[0]) 216 c.Assert(cost, Not(Less), delayThreshold) // have to wait for region1 217 218 tk.MustInterDirc("insert into t values (101)") // insert one record into region2 219 results = tk.MustQuery("explain analyze select * from t where t.a > 0 and t.a < 200 limit 1") 220 cost = s.parseTimeCost(c, results.Events()[0]) 221 c.Assert(cost, Less, noDelayThreshold) // region2 return quickly 222 223 results = tk.MustQuery("explain analyze select * from t where t.a > 0 and t.a < 200 limit 2") 224 cost = s.parseTimeCost(c, results.Events()[0]) 225 c.Assert(cost, Not(Less), delayThreshold) // have to wait 226 } 227 228 func (s *testChunkSizeControlSuite) parseTimeCost(c *C, line []interface{}) time.Duration { 229 lineStr := fmt.Sprintf("%v", line) 230 idx := strings.Index(lineStr, "time:") 231 c.Assert(idx, Not(Equals), -1) 232 lineStr = lineStr[idx+len("time:"):] 233 idx = strings.Index(lineStr, ",") 234 c.Assert(idx, Not(Equals), -1) 235 timeStr := lineStr[:idx] 236 d, err := time.ParseDuration(timeStr) 237 c.Assert(err, IsNil) 238 return d 239 }