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 }