github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/restore/split_test.go (about) 1 // Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0. 2 3 package restore_test 4 5 import ( 6 "bytes" 7 "context" 8 "sync" 9 10 . "github.com/pingcap/check" 11 "github.com/pingcap/errors" 12 "github.com/pingcap/kvproto/pkg/import_sstpb" 13 "github.com/pingcap/kvproto/pkg/metapb" 14 "github.com/pingcap/kvproto/pkg/pdpb" 15 "github.com/pingcap/tidb/util/codec" 16 "github.com/tikv/pd/server/core" 17 "github.com/tikv/pd/server/schedule/placement" 18 "google.golang.org/grpc/codes" 19 "google.golang.org/grpc/status" 20 21 "github.com/pingcap/br/pkg/restore" 22 "github.com/pingcap/br/pkg/rtree" 23 ) 24 25 type TestClient struct { 26 mu sync.RWMutex 27 stores map[uint64]*metapb.Store 28 regions map[uint64]*restore.RegionInfo 29 regionsInfo *core.RegionsInfo // For now it's only used in ScanRegions 30 nextRegionID uint64 31 32 scattered map[uint64]bool 33 } 34 35 func NewTestClient( 36 stores map[uint64]*metapb.Store, 37 regions map[uint64]*restore.RegionInfo, 38 nextRegionID uint64, 39 ) *TestClient { 40 regionsInfo := core.NewRegionsInfo() 41 for _, regionInfo := range regions { 42 regionsInfo.SetRegion(core.NewRegionInfo(regionInfo.Region, regionInfo.Leader)) 43 } 44 return &TestClient{ 45 stores: stores, 46 regions: regions, 47 regionsInfo: regionsInfo, 48 nextRegionID: nextRegionID, 49 scattered: map[uint64]bool{}, 50 } 51 } 52 53 func (c *TestClient) GetAllRegions() map[uint64]*restore.RegionInfo { 54 c.mu.RLock() 55 defer c.mu.RUnlock() 56 return c.regions 57 } 58 59 func (c *TestClient) GetStore(ctx context.Context, storeID uint64) (*metapb.Store, error) { 60 c.mu.RLock() 61 defer c.mu.RUnlock() 62 store, ok := c.stores[storeID] 63 if !ok { 64 return nil, errors.Errorf("store not found") 65 } 66 return store, nil 67 } 68 69 func (c *TestClient) GetRegion(ctx context.Context, key []byte) (*restore.RegionInfo, error) { 70 c.mu.RLock() 71 defer c.mu.RUnlock() 72 for _, region := range c.regions { 73 if bytes.Compare(key, region.Region.StartKey) >= 0 && 74 (len(region.Region.EndKey) == 0 || bytes.Compare(key, region.Region.EndKey) < 0) { 75 return region, nil 76 } 77 } 78 return nil, errors.Errorf("region not found: key=%s", string(key)) 79 } 80 81 func (c *TestClient) GetRegionByID(ctx context.Context, regionID uint64) (*restore.RegionInfo, error) { 82 c.mu.RLock() 83 defer c.mu.RUnlock() 84 region, ok := c.regions[regionID] 85 if !ok { 86 return nil, errors.Errorf("region not found: id=%d", regionID) 87 } 88 return region, nil 89 } 90 91 func (c *TestClient) SplitRegion( 92 ctx context.Context, 93 regionInfo *restore.RegionInfo, 94 key []byte, 95 ) (*restore.RegionInfo, error) { 96 c.mu.Lock() 97 defer c.mu.Unlock() 98 var target *restore.RegionInfo 99 splitKey := codec.EncodeBytes([]byte{}, key) 100 for _, region := range c.regions { 101 if bytes.Compare(splitKey, region.Region.StartKey) >= 0 && 102 (len(region.Region.EndKey) == 0 || bytes.Compare(splitKey, region.Region.EndKey) < 0) { 103 target = region 104 } 105 } 106 if target == nil { 107 return nil, errors.Errorf("region not found: key=%s", string(key)) 108 } 109 newRegion := &restore.RegionInfo{ 110 Region: &metapb.Region{ 111 Peers: target.Region.Peers, 112 Id: c.nextRegionID, 113 StartKey: target.Region.StartKey, 114 EndKey: splitKey, 115 }, 116 } 117 c.regions[c.nextRegionID] = newRegion 118 c.nextRegionID++ 119 target.Region.StartKey = splitKey 120 c.regions[target.Region.Id] = target 121 return newRegion, nil 122 } 123 124 func (c *TestClient) BatchSplitRegionsWithOrigin( 125 ctx context.Context, regionInfo *restore.RegionInfo, keys [][]byte, 126 ) (*restore.RegionInfo, []*restore.RegionInfo, error) { 127 c.mu.Lock() 128 defer c.mu.Unlock() 129 newRegions := make([]*restore.RegionInfo, 0) 130 var region *restore.RegionInfo 131 for _, key := range keys { 132 var target *restore.RegionInfo 133 splitKey := codec.EncodeBytes([]byte{}, key) 134 for _, region := range c.regions { 135 if region.ContainsInterior(splitKey) { 136 target = region 137 } 138 } 139 if target == nil { 140 continue 141 } 142 newRegion := &restore.RegionInfo{ 143 Region: &metapb.Region{ 144 Peers: target.Region.Peers, 145 Id: c.nextRegionID, 146 StartKey: target.Region.StartKey, 147 EndKey: splitKey, 148 }, 149 } 150 c.regions[c.nextRegionID] = newRegion 151 c.nextRegionID++ 152 target.Region.StartKey = splitKey 153 c.regions[target.Region.Id] = target 154 region = target 155 newRegions = append(newRegions, newRegion) 156 } 157 return region, newRegions, nil 158 } 159 160 func (c *TestClient) BatchSplitRegions( 161 ctx context.Context, regionInfo *restore.RegionInfo, keys [][]byte, 162 ) ([]*restore.RegionInfo, error) { 163 _, newRegions, err := c.BatchSplitRegionsWithOrigin(ctx, regionInfo, keys) 164 return newRegions, err 165 } 166 167 func (c *TestClient) ScatterRegion(ctx context.Context, regionInfo *restore.RegionInfo) error { 168 if _, ok := c.scattered[regionInfo.Region.Id]; !ok { 169 c.scattered[regionInfo.Region.Id] = false 170 return status.Errorf(codes.Unknown, "region %d is not fully replicated", regionInfo.Region.Id) 171 } 172 c.scattered[regionInfo.Region.Id] = true 173 return nil 174 } 175 176 func (c *TestClient) GetOperator(ctx context.Context, regionID uint64) (*pdpb.GetOperatorResponse, error) { 177 return &pdpb.GetOperatorResponse{ 178 Header: new(pdpb.ResponseHeader), 179 }, nil 180 } 181 182 func (c *TestClient) ScanRegions(ctx context.Context, key, endKey []byte, limit int) ([]*restore.RegionInfo, error) { 183 infos := c.regionsInfo.ScanRange(key, endKey, limit) 184 regions := make([]*restore.RegionInfo, 0, len(infos)) 185 for _, info := range infos { 186 regions = append(regions, &restore.RegionInfo{ 187 Region: info.GetMeta(), 188 Leader: info.GetLeader(), 189 }) 190 } 191 return regions, nil 192 } 193 194 func (c *TestClient) GetPlacementRule(ctx context.Context, groupID, ruleID string) (r placement.Rule, err error) { 195 return 196 } 197 198 func (c *TestClient) SetPlacementRule(ctx context.Context, rule placement.Rule) error { 199 return nil 200 } 201 202 func (c *TestClient) DeletePlacementRule(ctx context.Context, groupID, ruleID string) error { 203 return nil 204 } 205 206 func (c *TestClient) SetStoresLabel(ctx context.Context, stores []uint64, labelKey, labelValue string) error { 207 return nil 208 } 209 210 func (c *TestClient) checkScatter(check *C) { 211 regions := c.GetAllRegions() 212 for key := range regions { 213 if !c.scattered[key] { 214 check.Fatalf("region %d has not been scattered: %#v", key, regions[key]) 215 } 216 } 217 } 218 219 // region: [, aay), [aay, bba), [bba, bbh), [bbh, cca), [cca, ) 220 // range: [aaa, aae), [aae, aaz), [ccd, ccf), [ccf, ccj) 221 // rewrite rules: aa -> xx, cc -> bb 222 // expected regions after split: 223 // [, aay), [aay, bb), [bb, bba), [bba, bbf), [bbf, bbh), [bbh, bbj), 224 // [bbj, cca), [cca, xx), [xx, xxe), [xxe, xxz), [xxz, ) 225 func (s *testRangeSuite) TestSplitAndScatter(c *C) { 226 client := initTestClient() 227 ranges := initRanges() 228 rewriteRules := initRewriteRules() 229 regionSplitter := restore.NewRegionSplitter(client) 230 231 ctx := context.Background() 232 err := regionSplitter.Split(ctx, ranges, rewriteRules, func(key [][]byte) {}) 233 if err != nil { 234 c.Assert(err, IsNil, Commentf("split regions failed: %v", err)) 235 } 236 regions := client.GetAllRegions() 237 if !validateRegions(regions) { 238 for _, region := range regions { 239 c.Logf("region: %v\n", region.Region) 240 } 241 c.Log("get wrong result") 242 c.Fail() 243 } 244 regionInfos := make([]*restore.RegionInfo, 0, len(regions)) 245 for _, info := range regions { 246 regionInfos = append(regionInfos, info) 247 } 248 regionSplitter.ScatterRegions(ctx, regionInfos) 249 client.checkScatter(c) 250 } 251 252 // region: [, aay), [aay, bba), [bba, bbh), [bbh, cca), [cca, ) 253 func initTestClient() *TestClient { 254 peers := make([]*metapb.Peer, 1) 255 peers[0] = &metapb.Peer{ 256 Id: 1, 257 StoreId: 1, 258 } 259 keys := [6]string{"", "aay", "bba", "bbh", "cca", ""} 260 regions := make(map[uint64]*restore.RegionInfo) 261 for i := uint64(1); i < 6; i++ { 262 startKey := []byte(keys[i-1]) 263 if len(startKey) != 0 { 264 startKey = codec.EncodeBytes([]byte{}, startKey) 265 } 266 endKey := []byte(keys[i]) 267 if len(endKey) != 0 { 268 endKey = codec.EncodeBytes([]byte{}, endKey) 269 } 270 regions[i] = &restore.RegionInfo{ 271 Region: &metapb.Region{ 272 Id: i, 273 Peers: peers, 274 StartKey: startKey, 275 EndKey: endKey, 276 }, 277 } 278 } 279 stores := make(map[uint64]*metapb.Store) 280 stores[1] = &metapb.Store{ 281 Id: 1, 282 } 283 return NewTestClient(stores, regions, 6) 284 } 285 286 // range: [aaa, aae), [aae, aaz), [ccd, ccf), [ccf, ccj) 287 func initRanges() []rtree.Range { 288 var ranges [4]rtree.Range 289 ranges[0] = rtree.Range{ 290 StartKey: []byte("aaa"), 291 EndKey: []byte("aae"), 292 } 293 ranges[1] = rtree.Range{ 294 StartKey: []byte("aae"), 295 EndKey: []byte("aaz"), 296 } 297 ranges[2] = rtree.Range{ 298 StartKey: []byte("ccd"), 299 EndKey: []byte("ccf"), 300 } 301 ranges[3] = rtree.Range{ 302 StartKey: []byte("ccf"), 303 EndKey: []byte("ccj"), 304 } 305 return ranges[:] 306 } 307 308 func initRewriteRules() *restore.RewriteRules { 309 var rules [2]*import_sstpb.RewriteRule 310 rules[0] = &import_sstpb.RewriteRule{ 311 OldKeyPrefix: []byte("aa"), 312 NewKeyPrefix: []byte("xx"), 313 } 314 rules[1] = &import_sstpb.RewriteRule{ 315 OldKeyPrefix: []byte("cc"), 316 NewKeyPrefix: []byte("bb"), 317 } 318 return &restore.RewriteRules{ 319 Data: rules[:], 320 } 321 } 322 323 // expected regions after split: 324 // [, aay), [aay, bb), [bb, bba), [bba, bbf), [bbf, bbh), [bbh, bbj), 325 // [bbj, cca), [cca, xx), [xx, xxe), [xxe, xxz), [xxz, ) 326 func validateRegions(regions map[uint64]*restore.RegionInfo) bool { 327 keys := [12]string{"", "aay", "bb", "bba", "bbf", "bbh", "bbj", "cca", "xx", "xxe", "xxz", ""} 328 if len(regions) != 11 { 329 return false 330 } 331 FindRegion: 332 for i := 1; i < len(keys); i++ { 333 for _, region := range regions { 334 startKey := []byte(keys[i-1]) 335 if len(startKey) != 0 { 336 startKey = codec.EncodeBytes([]byte{}, startKey) 337 } 338 endKey := []byte(keys[i]) 339 if len(endKey) != 0 { 340 endKey = codec.EncodeBytes([]byte{}, endKey) 341 } 342 if bytes.Equal(region.Region.GetStartKey(), startKey) && 343 bytes.Equal(region.Region.GetEndKey(), endKey) { 344 continue FindRegion 345 } 346 } 347 return false 348 } 349 return true 350 } 351 352 func (s *testRangeSuite) TestNeedSplit(c *C) { 353 regions := []*restore.RegionInfo{ 354 { 355 Region: &metapb.Region{ 356 StartKey: codec.EncodeBytes([]byte{}, []byte("b")), 357 EndKey: codec.EncodeBytes([]byte{}, []byte("d")), 358 }, 359 }, 360 } 361 // Out of region 362 c.Assert(restore.NeedSplit([]byte("a"), regions), IsNil) 363 // Region start key 364 c.Assert(restore.NeedSplit([]byte("b"), regions), IsNil) 365 // In region 366 region := restore.NeedSplit([]byte("c"), regions) 367 c.Assert(bytes.Compare(region.Region.GetStartKey(), codec.EncodeBytes([]byte{}, []byte("b"))), Equals, 0) 368 c.Assert(bytes.Compare(region.Region.GetEndKey(), codec.EncodeBytes([]byte{}, []byte("d"))), Equals, 0) 369 // Region end key 370 c.Assert(restore.NeedSplit([]byte("d"), regions), IsNil) 371 // Out of region 372 c.Assert(restore.NeedSplit([]byte("e"), regions), IsNil) 373 }