github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/restore/split.go (about)

     1  // Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.
     2  
     3  package restore
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"encoding/hex"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/opentracing/opentracing-go"
    13  	"github.com/pingcap/errors"
    14  	sst "github.com/pingcap/kvproto/pkg/import_sstpb"
    15  	"github.com/pingcap/kvproto/pkg/metapb"
    16  	"github.com/pingcap/kvproto/pkg/pdpb"
    17  	"github.com/pingcap/log"
    18  	"github.com/tikv/pd/pkg/codec"
    19  	"go.uber.org/zap"
    20  
    21  	berrors "github.com/pingcap/br/pkg/errors"
    22  	"github.com/pingcap/br/pkg/logutil"
    23  	"github.com/pingcap/br/pkg/rtree"
    24  	"github.com/pingcap/br/pkg/utils"
    25  )
    26  
    27  // Constants for split retry machinery.
    28  const (
    29  	SplitRetryTimes       = 32
    30  	SplitRetryInterval    = 50 * time.Millisecond
    31  	SplitMaxRetryInterval = time.Second
    32  
    33  	SplitCheckMaxRetryTimes = 64
    34  	SplitCheckInterval      = 8 * time.Millisecond
    35  	SplitMaxCheckInterval   = time.Second
    36  
    37  	ScatterWaitMaxRetryTimes = 64
    38  	ScatterWaitInterval      = 50 * time.Millisecond
    39  	ScatterMaxWaitInterval   = time.Second
    40  	ScatterWaitUpperInterval = 180 * time.Second
    41  
    42  	ScanRegionPaginationLimit = 128
    43  
    44  	RejectStoreCheckRetryTimes  = 64
    45  	RejectStoreCheckInterval    = 100 * time.Millisecond
    46  	RejectStoreMaxCheckInterval = 2 * time.Second
    47  )
    48  
    49  // RegionSplitter is a executor of region split by rules.
    50  type RegionSplitter struct {
    51  	client SplitClient
    52  }
    53  
    54  // NewRegionSplitter returns a new RegionSplitter.
    55  func NewRegionSplitter(client SplitClient) *RegionSplitter {
    56  	return &RegionSplitter{
    57  		client: client,
    58  	}
    59  }
    60  
    61  // OnSplitFunc is called before split a range.
    62  type OnSplitFunc func(key [][]byte)
    63  
    64  // Split executes a region split. It will split regions by the rewrite rules,
    65  // then it will split regions by the end key of each range.
    66  // tableRules includes the prefix of a table, since some ranges may have
    67  // a prefix with record sequence or index sequence.
    68  // note: all ranges and rewrite rules must have raw key.
    69  func (rs *RegionSplitter) Split(
    70  	ctx context.Context,
    71  	ranges []rtree.Range,
    72  	rewriteRules *RewriteRules,
    73  	onSplit OnSplitFunc,
    74  ) error {
    75  	if len(ranges) == 0 {
    76  		log.Info("skip split regions, no range")
    77  		return nil
    78  	}
    79  
    80  	if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil {
    81  		span1 := span.Tracer().StartSpan("RegionSplitter.Split", opentracing.ChildOf(span.Context()))
    82  		defer span1.Finish()
    83  		ctx = opentracing.ContextWithSpan(ctx, span1)
    84  	}
    85  
    86  	startTime := time.Now()
    87  	// Sort the range for getting the min and max key of the ranges
    88  	sortedRanges, errSplit := SortRanges(ranges, rewriteRules)
    89  	if errSplit != nil {
    90  		return errors.Trace(errSplit)
    91  	}
    92  	minKey := codec.EncodeBytes(sortedRanges[0].StartKey)
    93  	maxKey := codec.EncodeBytes(sortedRanges[len(sortedRanges)-1].EndKey)
    94  	for _, rule := range rewriteRules.Data {
    95  		if bytes.Compare(minKey, rule.GetNewKeyPrefix()) > 0 {
    96  			minKey = rule.GetNewKeyPrefix()
    97  		}
    98  		if bytes.Compare(maxKey, rule.GetNewKeyPrefix()) < 0 {
    99  			maxKey = rule.GetNewKeyPrefix()
   100  		}
   101  	}
   102  	for _, rule := range rewriteRules.Data {
   103  		if bytes.Compare(minKey, rule.GetNewKeyPrefix()) > 0 {
   104  			minKey = rule.GetNewKeyPrefix()
   105  		}
   106  		if bytes.Compare(maxKey, rule.GetNewKeyPrefix()) < 0 {
   107  			maxKey = rule.GetNewKeyPrefix()
   108  		}
   109  	}
   110  	interval := SplitRetryInterval
   111  	scatterRegions := make([]*RegionInfo, 0)
   112  SplitRegions:
   113  	for i := 0; i < SplitRetryTimes; i++ {
   114  		regions, errScan := PaginateScanRegion(ctx, rs.client, minKey, maxKey, ScanRegionPaginationLimit)
   115  		if errScan != nil {
   116  			return errors.Trace(errScan)
   117  		}
   118  		if len(regions) == 0 {
   119  			log.Warn("split regions cannot scan any region")
   120  			return nil
   121  		}
   122  		splitKeyMap := getSplitKeys(rewriteRules, sortedRanges, regions)
   123  		regionMap := make(map[uint64]*RegionInfo)
   124  		for _, region := range regions {
   125  			regionMap[region.Region.GetId()] = region
   126  		}
   127  		for regionID, keys := range splitKeyMap {
   128  			var newRegions []*RegionInfo
   129  			region := regionMap[regionID]
   130  			log.Info("split regions",
   131  				logutil.Region(region.Region), logutil.Keys(keys), rtree.ZapRanges(ranges))
   132  			newRegions, errSplit = rs.splitAndScatterRegions(ctx, region, keys)
   133  			if errSplit != nil {
   134  				if strings.Contains(errSplit.Error(), "no valid key") {
   135  					for _, key := range keys {
   136  						// Region start/end keys are encoded. split_region RPC
   137  						// requires raw keys (without encoding).
   138  						log.Error("split regions no valid key",
   139  							logutil.Key("startKey", region.Region.StartKey),
   140  							logutil.Key("endKey", region.Region.EndKey),
   141  							logutil.Key("key", codec.EncodeBytes(key)),
   142  							rtree.ZapRanges(ranges))
   143  					}
   144  					return errors.Trace(errSplit)
   145  				}
   146  				interval = 2 * interval
   147  				if interval > SplitMaxRetryInterval {
   148  					interval = SplitMaxRetryInterval
   149  				}
   150  				time.Sleep(interval)
   151  				log.Warn("split regions failed, retry",
   152  					zap.Error(errSplit),
   153  					logutil.Region(region.Region),
   154  					logutil.Leader(region.Leader),
   155  					logutil.Keys(keys), rtree.ZapRanges(ranges))
   156  				continue SplitRegions
   157  			}
   158  			if len(newRegions) != len(keys) {
   159  				log.Warn("split key count and new region count mismatch",
   160  					zap.Int("new region count", len(newRegions)),
   161  					zap.Int("split key count", len(keys)))
   162  			}
   163  			scatterRegions = append(scatterRegions, newRegions...)
   164  			onSplit(keys)
   165  		}
   166  		break
   167  	}
   168  	if errSplit != nil {
   169  		return errors.Trace(errSplit)
   170  	}
   171  	log.Info("start to wait for scattering regions",
   172  		zap.Int("regions", len(scatterRegions)), zap.Duration("take", time.Since(startTime)))
   173  	startTime = time.Now()
   174  	scatterCount := 0
   175  	for _, region := range scatterRegions {
   176  		rs.waitForScatterRegion(ctx, region)
   177  		if time.Since(startTime) > ScatterWaitUpperInterval {
   178  			break
   179  		}
   180  		scatterCount++
   181  	}
   182  	if scatterCount == len(scatterRegions) {
   183  		log.Info("waiting for scattering regions done",
   184  			zap.Int("regions", len(scatterRegions)), zap.Duration("take", time.Since(startTime)))
   185  	} else {
   186  		log.Warn("waiting for scattering regions timeout",
   187  			zap.Int("scatterCount", scatterCount),
   188  			zap.Int("regions", len(scatterRegions)),
   189  			zap.Duration("take", time.Since(startTime)))
   190  	}
   191  	return nil
   192  }
   193  
   194  func (rs *RegionSplitter) hasRegion(ctx context.Context, regionID uint64) (bool, error) {
   195  	regionInfo, err := rs.client.GetRegionByID(ctx, regionID)
   196  	if err != nil {
   197  		return false, errors.Trace(err)
   198  	}
   199  	return regionInfo != nil, nil
   200  }
   201  
   202  func (rs *RegionSplitter) isScatterRegionFinished(ctx context.Context, regionID uint64) (bool, error) {
   203  	resp, err := rs.client.GetOperator(ctx, regionID)
   204  	if err != nil {
   205  		return false, errors.Trace(err)
   206  	}
   207  	// Heartbeat may not be sent to PD
   208  	if respErr := resp.GetHeader().GetError(); respErr != nil {
   209  		if respErr.GetType() == pdpb.ErrorType_REGION_NOT_FOUND {
   210  			return true, nil
   211  		}
   212  		return false, errors.Annotatef(berrors.ErrPDInvalidResponse, "get operator error: %s", respErr.GetType())
   213  	}
   214  	retryTimes := ctx.Value(retryTimes).(int)
   215  	if retryTimes > 3 {
   216  		log.Info("get operator", zap.Uint64("regionID", regionID), zap.Stringer("resp", resp))
   217  	}
   218  	// If the current operator of the region is not 'scatter-region', we could assume
   219  	// that 'scatter-operator' has finished or timeout
   220  	ok := string(resp.GetDesc()) != "scatter-region" || resp.GetStatus() != pdpb.OperatorStatus_RUNNING
   221  	return ok, nil
   222  }
   223  
   224  func (rs *RegionSplitter) waitForSplit(ctx context.Context, regionID uint64) {
   225  	interval := SplitCheckInterval
   226  	for i := 0; i < SplitCheckMaxRetryTimes; i++ {
   227  		ok, err := rs.hasRegion(ctx, regionID)
   228  		if err != nil {
   229  			log.Warn("wait for split failed", zap.Error(err))
   230  			return
   231  		}
   232  		if ok {
   233  			break
   234  		}
   235  		interval = 2 * interval
   236  		if interval > SplitMaxCheckInterval {
   237  			interval = SplitMaxCheckInterval
   238  		}
   239  		time.Sleep(interval)
   240  	}
   241  }
   242  
   243  type retryTimeKey struct{}
   244  
   245  var retryTimes = new(retryTimeKey)
   246  
   247  func (rs *RegionSplitter) waitForScatterRegion(ctx context.Context, regionInfo *RegionInfo) {
   248  	interval := ScatterWaitInterval
   249  	regionID := regionInfo.Region.GetId()
   250  	for i := 0; i < ScatterWaitMaxRetryTimes; i++ {
   251  		ctx1 := context.WithValue(ctx, retryTimes, i)
   252  		ok, err := rs.isScatterRegionFinished(ctx1, regionID)
   253  		if err != nil {
   254  			log.Warn("scatter region failed: do not have the region",
   255  				logutil.Region(regionInfo.Region))
   256  			return
   257  		}
   258  		if ok {
   259  			break
   260  		}
   261  		interval = 2 * interval
   262  		if interval > ScatterMaxWaitInterval {
   263  			interval = ScatterMaxWaitInterval
   264  		}
   265  		time.Sleep(interval)
   266  	}
   267  }
   268  
   269  func (rs *RegionSplitter) splitAndScatterRegions(
   270  	ctx context.Context, regionInfo *RegionInfo, keys [][]byte,
   271  ) ([]*RegionInfo, error) {
   272  	newRegions, err := rs.client.BatchSplitRegions(ctx, regionInfo, keys)
   273  	if err != nil {
   274  		return nil, errors.Trace(err)
   275  	}
   276  	rs.ScatterRegions(ctx, newRegions)
   277  	return newRegions, nil
   278  }
   279  
   280  // ScatterRegions scatter the regions.
   281  func (rs *RegionSplitter) ScatterRegions(ctx context.Context, newRegions []*RegionInfo) {
   282  	for _, region := range newRegions {
   283  		// Wait for a while until the regions successfully split.
   284  		rs.waitForSplit(ctx, region.Region.Id)
   285  		if err := utils.WithRetry(ctx,
   286  			func() error { return rs.client.ScatterRegion(ctx, region) },
   287  			// backoff about 6s, or we give up scattering this region.
   288  			&scatterBackoffer{
   289  				attempt:     7,
   290  				baseBackoff: 100 * time.Millisecond,
   291  			},
   292  		); err != nil {
   293  			log.Warn("scatter region failed, stop retry", logutil.Region(region.Region), zap.Error(err))
   294  		}
   295  	}
   296  }
   297  
   298  // PaginateScanRegion scan regions with a limit pagination and
   299  // return all regions at once.
   300  // It reduces max gRPC message size.
   301  func PaginateScanRegion(
   302  	ctx context.Context, client SplitClient, startKey, endKey []byte, limit int,
   303  ) ([]*RegionInfo, error) {
   304  	if len(endKey) != 0 && bytes.Compare(startKey, endKey) >= 0 {
   305  		return nil, errors.Annotatef(berrors.ErrRestoreInvalidRange, "startKey >= endKey, startKey %s, endkey %s",
   306  			hex.EncodeToString(startKey), hex.EncodeToString(endKey))
   307  	}
   308  
   309  	regions := []*RegionInfo{}
   310  	for {
   311  		batch, err := client.ScanRegions(ctx, startKey, endKey, limit)
   312  		if err != nil {
   313  			return nil, errors.Trace(err)
   314  		}
   315  		regions = append(regions, batch...)
   316  		if len(batch) < limit {
   317  			// No more region
   318  			break
   319  		}
   320  		startKey = batch[len(batch)-1].Region.GetEndKey()
   321  		if len(startKey) == 0 ||
   322  			(len(endKey) > 0 && bytes.Compare(startKey, endKey) >= 0) {
   323  			// All key space have scanned
   324  			break
   325  		}
   326  	}
   327  	return regions, nil
   328  }
   329  
   330  // getSplitKeys checks if the regions should be split by the new prefix of the rewrites rule and the end key of
   331  // the ranges, groups the split keys by region id.
   332  func getSplitKeys(rewriteRules *RewriteRules, ranges []rtree.Range, regions []*RegionInfo) map[uint64][][]byte {
   333  	splitKeyMap := make(map[uint64][][]byte)
   334  	checkKeys := make([][]byte, 0)
   335  	for _, rule := range rewriteRules.Data {
   336  		checkKeys = append(checkKeys, rule.GetNewKeyPrefix())
   337  	}
   338  	for _, rg := range ranges {
   339  		checkKeys = append(checkKeys, rg.EndKey)
   340  	}
   341  	for _, key := range checkKeys {
   342  		if region := NeedSplit(key, regions); region != nil {
   343  			splitKeys, ok := splitKeyMap[region.Region.GetId()]
   344  			if !ok {
   345  				splitKeys = make([][]byte, 0, 1)
   346  			}
   347  			splitKeyMap[region.Region.GetId()] = append(splitKeys, key)
   348  			log.Debug("get key for split region",
   349  				logutil.Key("key", key),
   350  				logutil.Key("startKey", region.Region.StartKey),
   351  				logutil.Key("endKey", region.Region.EndKey))
   352  		}
   353  	}
   354  	return splitKeyMap
   355  }
   356  
   357  // NeedSplit checks whether a key is necessary to split, if true returns the split region.
   358  func NeedSplit(splitKey []byte, regions []*RegionInfo) *RegionInfo {
   359  	// If splitKey is the max key.
   360  	if len(splitKey) == 0 {
   361  		return nil
   362  	}
   363  	splitKey = codec.EncodeBytes(splitKey)
   364  	for _, region := range regions {
   365  		// If splitKey is the boundary of the region
   366  		if bytes.Equal(splitKey, region.Region.GetStartKey()) {
   367  			return nil
   368  		}
   369  		// If splitKey is in a region
   370  		if region.ContainsInterior(splitKey) {
   371  			return region
   372  		}
   373  	}
   374  	return nil
   375  }
   376  
   377  func replacePrefix(s []byte, rewriteRules *RewriteRules) ([]byte, *sst.RewriteRule) {
   378  	// We should search the dataRules firstly.
   379  	for _, rule := range rewriteRules.Data {
   380  		if bytes.HasPrefix(s, rule.GetOldKeyPrefix()) {
   381  			return append(append([]byte{}, rule.GetNewKeyPrefix()...), s[len(rule.GetOldKeyPrefix()):]...), rule
   382  		}
   383  	}
   384  
   385  	return s, nil
   386  }
   387  
   388  func beforeEnd(key []byte, end []byte) bool {
   389  	return bytes.Compare(key, end) < 0 || len(end) == 0
   390  }
   391  
   392  func intersectRange(region *metapb.Region, rg Range) Range {
   393  	var startKey, endKey []byte
   394  	if len(region.StartKey) > 0 {
   395  		_, startKey, _ = codec.DecodeBytes(region.StartKey)
   396  	}
   397  	if bytes.Compare(startKey, rg.Start) < 0 {
   398  		startKey = rg.Start
   399  	}
   400  	if len(region.EndKey) > 0 {
   401  		_, endKey, _ = codec.DecodeBytes(region.EndKey)
   402  	}
   403  	if beforeEnd(rg.End, endKey) {
   404  		endKey = rg.End
   405  	}
   406  
   407  	return Range{Start: startKey, End: endKey}
   408  }
   409  
   410  func insideRegion(region *metapb.Region, meta *sst.SSTMeta) bool {
   411  	rg := meta.GetRange()
   412  	return keyInsideRegion(region, rg.GetStart()) && keyInsideRegion(region, rg.GetEnd())
   413  }
   414  
   415  func keyInsideRegion(region *metapb.Region, key []byte) bool {
   416  	return bytes.Compare(key, region.GetStartKey()) >= 0 && (beforeEnd(key, region.GetEndKey()))
   417  }