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  }