github.com/pingcap/tidb-lightning@v5.0.0-rc.0.20210428090220-84b649866577+incompatible/lightning/backend/localhelper_test.go (about)

     1  // Copyright 2019 PingCAP, 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 backend
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"sync"
    20  	"time"
    21  
    22  	"github.com/pingcap/br/pkg/restore"
    23  	. "github.com/pingcap/check"
    24  	"github.com/pingcap/errors"
    25  	"github.com/pingcap/kvproto/pkg/metapb"
    26  	"github.com/pingcap/kvproto/pkg/pdpb"
    27  	"github.com/pingcap/tidb/kv"
    28  	"github.com/pingcap/tidb/sessionctx/stmtctx"
    29  	"github.com/pingcap/tidb/tablecodec"
    30  	"github.com/pingcap/tidb/types"
    31  	"github.com/pingcap/tidb/util/codec"
    32  	"github.com/tikv/pd/server/core"
    33  	"github.com/tikv/pd/server/schedule/placement"
    34  )
    35  
    36  type testClient struct {
    37  	mu           sync.RWMutex
    38  	stores       map[uint64]*metapb.Store
    39  	regions      map[uint64]*restore.RegionInfo
    40  	regionsInfo  *core.RegionsInfo // For now it's only used in ScanRegions
    41  	nextRegionID uint64
    42  	splitCount   int
    43  	hook         clientHook
    44  }
    45  
    46  func newTestClient(
    47  	stores map[uint64]*metapb.Store,
    48  	regions map[uint64]*restore.RegionInfo,
    49  	nextRegionID uint64,
    50  	hook clientHook,
    51  ) *testClient {
    52  	regionsInfo := core.NewRegionsInfo()
    53  	for _, regionInfo := range regions {
    54  		regionsInfo.AddRegion(core.NewRegionInfo(regionInfo.Region, regionInfo.Leader))
    55  	}
    56  	return &testClient{
    57  		stores:       stores,
    58  		regions:      regions,
    59  		regionsInfo:  regionsInfo,
    60  		nextRegionID: nextRegionID,
    61  		hook:         hook,
    62  	}
    63  }
    64  
    65  func (c *testClient) GetAllRegions() map[uint64]*restore.RegionInfo {
    66  	c.mu.RLock()
    67  	defer c.mu.RUnlock()
    68  	return c.regions
    69  }
    70  
    71  func (c *testClient) GetStore(ctx context.Context, storeID uint64) (*metapb.Store, error) {
    72  	c.mu.RLock()
    73  	defer c.mu.RUnlock()
    74  	store, ok := c.stores[storeID]
    75  	if !ok {
    76  		return nil, errors.Errorf("store not found")
    77  	}
    78  	return store, nil
    79  }
    80  
    81  func (c *testClient) GetRegion(ctx context.Context, key []byte) (*restore.RegionInfo, error) {
    82  	c.mu.RLock()
    83  	defer c.mu.RUnlock()
    84  	for _, region := range c.regions {
    85  		if bytes.Compare(key, region.Region.StartKey) >= 0 &&
    86  			(len(region.Region.EndKey) == 0 || bytes.Compare(key, region.Region.EndKey) < 0) {
    87  			return region, nil
    88  		}
    89  	}
    90  	return nil, errors.Errorf("region not found: key=%s", string(key))
    91  }
    92  
    93  func (c *testClient) GetRegionByID(ctx context.Context, regionID uint64) (*restore.RegionInfo, error) {
    94  	c.mu.RLock()
    95  	defer c.mu.RUnlock()
    96  	region, ok := c.regions[regionID]
    97  	if !ok {
    98  		return nil, errors.Errorf("region not found: id=%d", regionID)
    99  	}
   100  	return region, nil
   101  }
   102  
   103  func (c *testClient) SplitRegion(
   104  	ctx context.Context,
   105  	regionInfo *restore.RegionInfo,
   106  	key []byte,
   107  ) (*restore.RegionInfo, error) {
   108  	c.mu.Lock()
   109  	defer c.mu.Unlock()
   110  	var target *restore.RegionInfo
   111  	splitKey := codec.EncodeBytes([]byte{}, key)
   112  	for _, region := range c.regions {
   113  		if bytes.Compare(splitKey, region.Region.StartKey) >= 0 &&
   114  			(len(region.Region.EndKey) == 0 || bytes.Compare(splitKey, region.Region.EndKey) < 0) {
   115  			target = region
   116  		}
   117  	}
   118  	if target == nil {
   119  		return nil, errors.Errorf("region not found: key=%s", string(key))
   120  	}
   121  	newRegion := &restore.RegionInfo{
   122  		Region: &metapb.Region{
   123  			Peers:    target.Region.Peers,
   124  			Id:       c.nextRegionID,
   125  			StartKey: target.Region.StartKey,
   126  			EndKey:   splitKey,
   127  			RegionEpoch: &metapb.RegionEpoch{
   128  				Version: target.Region.RegionEpoch.Version,
   129  				ConfVer: target.Region.RegionEpoch.ConfVer + 1,
   130  			},
   131  		},
   132  	}
   133  	c.regions[c.nextRegionID] = newRegion
   134  	c.regionsInfo.SetRegion(core.NewRegionInfo(newRegion.Region, newRegion.Leader))
   135  	c.nextRegionID++
   136  	target.Region.StartKey = splitKey
   137  	target.Region.RegionEpoch.ConfVer++
   138  	c.regions[target.Region.Id] = target
   139  	c.regionsInfo.SetRegion(core.NewRegionInfo(target.Region, target.Leader))
   140  	return newRegion, nil
   141  }
   142  
   143  func (c *testClient) BatchSplitRegionsWithOrigin(
   144  	ctx context.Context, regionInfo *restore.RegionInfo, keys [][]byte,
   145  ) (*restore.RegionInfo, []*restore.RegionInfo, error) {
   146  	if c.hook != nil {
   147  		regionInfo, keys = c.hook.BeforeSplitRegion(ctx, regionInfo, keys)
   148  	}
   149  
   150  	c.splitCount++
   151  	c.mu.Lock()
   152  	defer c.mu.Unlock()
   153  	newRegions := make([]*restore.RegionInfo, 0)
   154  	var region *restore.RegionInfo
   155  	for _, key := range keys {
   156  		var target *restore.RegionInfo
   157  		splitKey := codec.EncodeBytes([]byte{}, key)
   158  		for _, region := range c.regions {
   159  			if region.ContainsInterior(splitKey) {
   160  				target = region
   161  			}
   162  		}
   163  		if target == nil {
   164  			continue
   165  		}
   166  		if target.Region.RegionEpoch.Version != regionInfo.Region.RegionEpoch.Version ||
   167  			target.Region.RegionEpoch.ConfVer != regionInfo.Region.RegionEpoch.ConfVer {
   168  			return regionInfo, nil, errors.New("epoch not match")
   169  		}
   170  		newRegion := &restore.RegionInfo{
   171  			Region: &metapb.Region{
   172  				Peers:    target.Region.Peers,
   173  				Id:       c.nextRegionID,
   174  				StartKey: target.Region.StartKey,
   175  				EndKey:   splitKey,
   176  			},
   177  		}
   178  		c.regions[c.nextRegionID] = newRegion
   179  		c.regionsInfo.SetRegion(core.NewRegionInfo(newRegion.Region, newRegion.Leader))
   180  		c.nextRegionID++
   181  		target.Region.StartKey = splitKey
   182  		c.regions[target.Region.Id] = target
   183  		region = target
   184  		newRegions = append(newRegions, newRegion)
   185  	}
   186  	if region != nil {
   187  		c.regionsInfo.SetRegion(core.NewRegionInfo(region.Region, region.Leader))
   188  	}
   189  	var err error
   190  	if c.hook != nil {
   191  		newRegions, err = c.hook.AfterSplitRegion(ctx, region, keys, newRegions, nil)
   192  	}
   193  
   194  	return region, newRegions, err
   195  }
   196  
   197  func (c *testClient) BatchSplitRegions(
   198  	ctx context.Context, regionInfo *restore.RegionInfo, keys [][]byte,
   199  ) ([]*restore.RegionInfo, error) {
   200  	_, newRegions, err := c.BatchSplitRegionsWithOrigin(ctx, regionInfo, keys)
   201  	return newRegions, err
   202  }
   203  
   204  func (c *testClient) ScatterRegion(ctx context.Context, regionInfo *restore.RegionInfo) error {
   205  	return nil
   206  }
   207  
   208  func (c *testClient) GetOperator(ctx context.Context, regionID uint64) (*pdpb.GetOperatorResponse, error) {
   209  	return &pdpb.GetOperatorResponse{
   210  		Header: new(pdpb.ResponseHeader),
   211  	}, nil
   212  }
   213  
   214  func (c *testClient) ScanRegions(ctx context.Context, key, endKey []byte, limit int) ([]*restore.RegionInfo, error) {
   215  	if c.hook != nil {
   216  		key, endKey, limit = c.hook.BeforeScanRegions(ctx, key, endKey, limit)
   217  	}
   218  
   219  	infos := c.regionsInfo.ScanRange(key, endKey, limit)
   220  	regions := make([]*restore.RegionInfo, 0, len(infos))
   221  	for _, info := range infos {
   222  		regions = append(regions, &restore.RegionInfo{
   223  			Region: info.GetMeta(),
   224  			Leader: info.GetLeader(),
   225  		})
   226  	}
   227  
   228  	var err error
   229  	if c.hook != nil {
   230  		regions, err = c.hook.AfterScanRegions(regions, nil)
   231  	}
   232  	return regions, err
   233  }
   234  
   235  func (c *testClient) GetPlacementRule(ctx context.Context, groupID, ruleID string) (r placement.Rule, err error) {
   236  	return
   237  }
   238  
   239  func (c *testClient) SetPlacementRule(ctx context.Context, rule placement.Rule) error {
   240  	return nil
   241  }
   242  
   243  func (c *testClient) DeletePlacementRule(ctx context.Context, groupID, ruleID string) error {
   244  	return nil
   245  }
   246  
   247  func (c *testClient) SetStoresLabel(ctx context.Context, stores []uint64, labelKey, labelValue string) error {
   248  	return nil
   249  }
   250  
   251  func cloneRegion(region *restore.RegionInfo) *restore.RegionInfo {
   252  	r := &metapb.Region{}
   253  	if region.Region != nil {
   254  		b, _ := region.Region.Marshal()
   255  		_ = r.Unmarshal(b)
   256  	}
   257  
   258  	l := &metapb.Peer{}
   259  	if region.Region != nil {
   260  		b, _ := region.Region.Marshal()
   261  		_ = l.Unmarshal(b)
   262  	}
   263  	return &restore.RegionInfo{Region: r, Leader: l}
   264  }
   265  
   266  // region: [, aay), [aay, bba), [bba, bbh), [bbh, cca), [cca, )
   267  func initTestClient(keys [][]byte, hook clientHook) *testClient {
   268  	peers := make([]*metapb.Peer, 1)
   269  	peers[0] = &metapb.Peer{
   270  		Id:      1,
   271  		StoreId: 1,
   272  	}
   273  	regions := make(map[uint64]*restore.RegionInfo)
   274  	for i := uint64(1); i < uint64(len(keys)); i++ {
   275  		startKey := keys[i-1]
   276  		if len(startKey) != 0 {
   277  			startKey = codec.EncodeBytes([]byte{}, startKey)
   278  		}
   279  		endKey := keys[i]
   280  		if len(endKey) != 0 {
   281  			endKey = codec.EncodeBytes([]byte{}, endKey)
   282  		}
   283  		regions[i] = &restore.RegionInfo{
   284  			Region: &metapb.Region{
   285  				Id:          i,
   286  				Peers:       peers,
   287  				StartKey:    startKey,
   288  				EndKey:      endKey,
   289  				RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1},
   290  			},
   291  		}
   292  	}
   293  	stores := make(map[uint64]*metapb.Store)
   294  	stores[1] = &metapb.Store{
   295  		Id: 1,
   296  	}
   297  	return newTestClient(stores, regions, uint64(len(keys)), hook)
   298  }
   299  
   300  func checkRegionRanges(c *C, regions []*restore.RegionInfo, keys [][]byte) {
   301  	//c.Assert(len(regions)+1, Equals, len(keys))
   302  	for i, r := range regions {
   303  		_, regionStart, _ := codec.DecodeBytes(r.Region.StartKey, []byte{})
   304  		_, regionEnd, _ := codec.DecodeBytes(r.Region.EndKey, []byte{})
   305  		c.Assert(regionStart, DeepEquals, keys[i])
   306  		c.Assert(regionEnd, DeepEquals, keys[i+1])
   307  	}
   308  }
   309  
   310  type clientHook interface {
   311  	BeforeSplitRegion(ctx context.Context, regionInfo *restore.RegionInfo, keys [][]byte) (*restore.RegionInfo, [][]byte)
   312  	AfterSplitRegion(context.Context, *restore.RegionInfo, [][]byte, []*restore.RegionInfo, error) ([]*restore.RegionInfo, error)
   313  	BeforeScanRegions(ctx context.Context, key, endKey []byte, limit int) ([]byte, []byte, int)
   314  	AfterScanRegions([]*restore.RegionInfo, error) ([]*restore.RegionInfo, error)
   315  }
   316  
   317  type noopHook struct{}
   318  
   319  func (h *noopHook) BeforeSplitRegion(ctx context.Context, regionInfo *restore.RegionInfo, keys [][]byte) (*restore.RegionInfo, [][]byte) {
   320  	return regionInfo, keys
   321  }
   322  func (h *noopHook) AfterSplitRegion(c context.Context, r *restore.RegionInfo, keys [][]byte, res []*restore.RegionInfo, err error) ([]*restore.RegionInfo, error) {
   323  	return res, err
   324  }
   325  func (h *noopHook) BeforeScanRegions(ctx context.Context, key, endKey []byte, limit int) ([]byte, []byte, int) {
   326  	return key, endKey, limit
   327  }
   328  func (h *noopHook) AfterScanRegions(res []*restore.RegionInfo, err error) ([]*restore.RegionInfo, error) {
   329  	return res, err
   330  }
   331  
   332  func (s *localSuite) doTestBatchSplitRegionByRanges(c *C, hook clientHook, errPat string) {
   333  	oldLimit := maxBatchSplitKeys
   334  	oldSplitBackoffTime := splitRegionBaseBackOffTime
   335  	maxBatchSplitKeys = 4
   336  	splitRegionBaseBackOffTime = time.Millisecond
   337  	defer func() {
   338  		maxBatchSplitKeys = oldLimit
   339  		splitRegionBaseBackOffTime = oldSplitBackoffTime
   340  	}()
   341  
   342  	keys := [][]byte{[]byte(""), []byte("aay"), []byte("bba"), []byte("bbh"), []byte("cca"), []byte("")}
   343  	client := initTestClient(keys, hook)
   344  	local := &local{
   345  		splitCli: client,
   346  	}
   347  	ctx := context.Background()
   348  
   349  	// current region ranges: [, aay), [aay, bba), [bba, bbh), [bbh, cca), [cca, )
   350  	rangeStart := codec.EncodeBytes([]byte{}, []byte("b"))
   351  	rangeEnd := codec.EncodeBytes([]byte{}, []byte("c"))
   352  	regions, err := paginateScanRegion(ctx, client, rangeStart, rangeEnd, 5)
   353  	c.Assert(err, IsNil)
   354  	// regions is: [aay, bba), [bba, bbh), [bbh, cca)
   355  	checkRegionRanges(c, regions, [][]byte{[]byte("aay"), []byte("bba"), []byte("bbh"), []byte("cca")})
   356  
   357  	// generate:  ranges [b, ba), [ba, bb), [bb, bc), ... [by, bz)
   358  	ranges := make([]Range, 0)
   359  	start := []byte{'b'}
   360  	for i := byte('a'); i <= 'z'; i++ {
   361  		end := []byte{'b', i}
   362  		ranges = append(ranges, Range{start: start, end: end})
   363  		start = end
   364  	}
   365  
   366  	err = local.SplitAndScatterRegionByRanges(ctx, ranges, true)
   367  	if len(errPat) == 0 {
   368  		c.Assert(err, IsNil)
   369  	} else {
   370  		c.Assert(err, ErrorMatches, errPat)
   371  		return
   372  	}
   373  
   374  	// so with a batch split size of 4, there will be 7 time batch split
   375  	// 1. region: [aay, bba), keys: [b, ba, bb]
   376  	// 2. region: [bbh, cca), keys: [bc, bd, be, bf]
   377  	// 3. region: [bf, cca), keys: [bg, bh, bi, bj]
   378  	// 4. region: [bj, cca), keys: [bk, bl, bm, bn]
   379  	// 5. region: [bn, cca), keys: [bo, bp, bq, br]
   380  	// 6. region: [br, cca), keys: [bs, bt, bu, bv]
   381  	// 7. region: [bv, cca), keys: [bw, bx, by, bz]
   382  
   383  	// since it may encounter error retries, here only check the lower threshold.
   384  	c.Assert(client.splitCount >= 7, IsTrue)
   385  
   386  	// check split ranges
   387  	regions, err = paginateScanRegion(ctx, client, rangeStart, rangeEnd, 5)
   388  	c.Assert(err, IsNil)
   389  	result := [][]byte{[]byte("b"), []byte("ba"), []byte("bb"), []byte("bba"), []byte("bbh"), []byte("bc"),
   390  		[]byte("bd"), []byte("be"), []byte("bf"), []byte("bg"), []byte("bh"), []byte("bi"), []byte("bj"),
   391  		[]byte("bk"), []byte("bl"), []byte("bm"), []byte("bn"), []byte("bo"), []byte("bp"), []byte("bq"),
   392  		[]byte("br"), []byte("bs"), []byte("bt"), []byte("bu"), []byte("bv"), []byte("bw"), []byte("bx"),
   393  		[]byte("by"), []byte("bz"), []byte("cca")}
   394  	checkRegionRanges(c, regions, result)
   395  }
   396  
   397  func (s *localSuite) TestBatchSplitRegionByRanges(c *C) {
   398  	s.doTestBatchSplitRegionByRanges(c, nil, "")
   399  }
   400  
   401  type scanRegionEmptyHook struct {
   402  	noopHook
   403  	cnt int
   404  }
   405  
   406  func (h *scanRegionEmptyHook) AfterScanRegions(res []*restore.RegionInfo, err error) ([]*restore.RegionInfo, error) {
   407  	h.cnt++
   408  	// skip the first call
   409  	if h.cnt == 1 {
   410  		return res, err
   411  	}
   412  	return nil, err
   413  }
   414  
   415  func (s *localSuite) TestBatchSplitRegionByRangesScanFailed(c *C) {
   416  	s.doTestBatchSplitRegionByRanges(c, &scanRegionEmptyHook{}, "paginate scan region returns empty result")
   417  }
   418  
   419  type splitRegionEpochNotMatchHook struct {
   420  	noopHook
   421  }
   422  
   423  func (h *splitRegionEpochNotMatchHook) BeforeSplitRegion(ctx context.Context, regionInfo *restore.RegionInfo, keys [][]byte) (*restore.RegionInfo, [][]byte) {
   424  	regionInfo = cloneRegion(regionInfo)
   425  	// decrease the region epoch, so split region will fail
   426  	regionInfo.Region.RegionEpoch.Version--
   427  	return regionInfo, keys
   428  }
   429  
   430  func (s *localSuite) TestBatchSplitByRangesEpochNotMatch(c *C) {
   431  	s.doTestBatchSplitRegionByRanges(c, &splitRegionEpochNotMatchHook{}, "batch split regions failed: epoch not match")
   432  }
   433  
   434  // return epoch not match error in every other call
   435  type splitRegionEpochNotMatchHookRandom struct {
   436  	noopHook
   437  	cnt int
   438  }
   439  
   440  func (h *splitRegionEpochNotMatchHookRandom) BeforeSplitRegion(ctx context.Context, regionInfo *restore.RegionInfo, keys [][]byte) (*restore.RegionInfo, [][]byte) {
   441  	h.cnt++
   442  	if h.cnt%2 != 0 {
   443  		return regionInfo, keys
   444  	}
   445  	regionInfo = cloneRegion(regionInfo)
   446  	// decrease the region epoch, so split region will fail
   447  	regionInfo.Region.RegionEpoch.Version--
   448  	return regionInfo, keys
   449  }
   450  
   451  func (s *localSuite) TestBatchSplitByRangesEpochNotMatchOnce(c *C) {
   452  	s.doTestBatchSplitRegionByRanges(c, &splitRegionEpochNotMatchHookRandom{}, "")
   453  }
   454  
   455  func (s *localSuite) doTestBatchSplitByRangesWithClusteredIndex(c *C, hook clientHook) {
   456  	oldLimit := maxBatchSplitKeys
   457  	oldSplitBackoffTime := splitRegionBaseBackOffTime
   458  	maxBatchSplitKeys = 10
   459  	splitRegionBaseBackOffTime = time.Millisecond
   460  	defer func() {
   461  		maxBatchSplitKeys = oldLimit
   462  		splitRegionBaseBackOffTime = oldSplitBackoffTime
   463  	}()
   464  
   465  	stmtCtx := new(stmtctx.StatementContext)
   466  
   467  	tableId := int64(1)
   468  	tableStartKey := tablecodec.EncodeTablePrefix(tableId)
   469  	tableEndKey := tablecodec.EncodeTablePrefix(tableId + 1)
   470  	keys := [][]byte{[]byte(""), tableStartKey}
   471  	// pre split 2 regions
   472  	for i := int64(0); i < 2; i++ {
   473  		keyBytes, err := codec.EncodeKey(stmtCtx, nil, types.NewIntDatum(i))
   474  		c.Assert(err, IsNil)
   475  		h, err := kv.NewCommonHandle(keyBytes)
   476  		c.Assert(err, IsNil)
   477  		key := tablecodec.EncodeRowKeyWithHandle(tableId, h)
   478  		keys = append(keys, key)
   479  	}
   480  	keys = append(keys, tableEndKey, []byte(""))
   481  	client := initTestClient(keys, hook)
   482  	local := &local{
   483  		splitCli: client,
   484  	}
   485  	ctx := context.Background()
   486  
   487  	// we batch generate a batch of row keys for table 1 with common handle
   488  	rangeKeys := make([][]byte, 0, 20+1)
   489  	for i := int64(0); i < 2; i++ {
   490  		for j := int64(0); j < 10; j++ {
   491  			keyBytes, err := codec.EncodeKey(stmtCtx, nil, types.NewIntDatum(i), types.NewIntDatum(j*10000))
   492  			c.Assert(err, IsNil)
   493  			h, err := kv.NewCommonHandle(keyBytes)
   494  			c.Assert(err, IsNil)
   495  			key := tablecodec.EncodeRowKeyWithHandle(tableId, h)
   496  			rangeKeys = append(rangeKeys, key)
   497  		}
   498  	}
   499  
   500  	start := rangeKeys[0]
   501  	ranges := make([]Range, 0, len(rangeKeys)-1)
   502  	for _, e := range rangeKeys[1:] {
   503  		ranges = append(ranges, Range{start: start, end: e})
   504  		start = e
   505  	}
   506  
   507  	err := local.SplitAndScatterRegionByRanges(ctx, ranges, true)
   508  	c.Assert(err, IsNil)
   509  
   510  	startKey := codec.EncodeBytes([]byte{}, rangeKeys[0])
   511  	endKey := codec.EncodeBytes([]byte{}, rangeKeys[len(rangeKeys)-1])
   512  	// check split ranges
   513  	regions, err := paginateScanRegion(ctx, client, startKey, endKey, 5)
   514  	c.Assert(err, IsNil)
   515  	c.Assert(len(regions), Equals, len(ranges)+1)
   516  
   517  	checkKeys := append([][]byte{}, rangeKeys[:10]...)
   518  	checkKeys = append(checkKeys, keys[3])
   519  	checkKeys = append(checkKeys, rangeKeys[10:]...)
   520  	checkRegionRanges(c, regions, checkKeys)
   521  }
   522  
   523  func (s *localSuite) TestBatchSplitByRangesWithClusteredIndex(c *C) {
   524  	s.doTestBatchSplitByRangesWithClusteredIndex(c, nil)
   525  }
   526  
   527  func (s *localSuite) TestBatchSplitByRangesWithClusteredIndexEpochNotMatch(c *C) {
   528  	s.doTestBatchSplitByRangesWithClusteredIndex(c, &splitRegionEpochNotMatchHookRandom{})
   529  }