diff --git a/br/pkg/restore/split.go b/br/pkg/restore/split.go index 27e14c5bc83a5..c962a2109aac6 100644 --- a/br/pkg/restore/split.go +++ b/br/pkg/restore/split.go @@ -339,10 +339,10 @@ func (rs *RegionSplitter) ScatterRegions(ctx context.Context, newRegions []*Regi }) } -func checkRegionConsistency(startKey, endKey []byte, regions []*RegionInfo) error { +func CheckRegionConsistency(startKey, endKey []byte, regions []*RegionInfo) error { // current pd can't guarantee the consistency of returned regions if len(regions) == 0 { - return errors.Annotatef(berrors.ErrPDBatchScanRegion, "scan region return empty result, startKey: %s, endkey: %s", + return errors.Annotatef(berrors.ErrPDBatchScanRegion, "scan region return empty result, startKey: %s, endKey: %s", redact.Key(startKey), redact.Key(endKey)) } @@ -350,7 +350,7 @@ func checkRegionConsistency(startKey, endKey []byte, regions []*RegionInfo) erro return errors.Annotatef(berrors.ErrPDBatchScanRegion, "first region's startKey > startKey, startKey: %s, regionStartKey: %s", redact.Key(startKey), redact.Key(regions[0].Region.StartKey)) } else if len(regions[len(regions)-1].Region.EndKey) != 0 && bytes.Compare(regions[len(regions)-1].Region.EndKey, endKey) < 0 { - return errors.Annotatef(berrors.ErrPDBatchScanRegion, "last region's endKey < startKey, startKey: %s, regionStartKey: %s", + return errors.Annotatef(berrors.ErrPDBatchScanRegion, "last region's endKey < endKey, endKey: %s, regionEndKey: %s", redact.Key(endKey), redact.Key(regions[len(regions)-1].Region.EndKey)) } @@ -398,7 +398,7 @@ func PaginateScanRegion( break } } - if err := checkRegionConsistency(startKey, endKey, regions); err != nil { + if err := CheckRegionConsistency(startKey, endKey, regions); err != nil { log.Warn("failed to scan region, retrying", logutil.ShortError(err)) return err } diff --git a/br/pkg/restore/split_test.go b/br/pkg/restore/split_test.go index a4bd794f1a0a1..5e43d3378e579 100644 --- a/br/pkg/restore/split_test.go +++ b/br/pkg/restore/split_test.go @@ -445,3 +445,70 @@ func (s *testRangeSuite) TestNeedSplit(c *C) { // Out of region c.Assert(restore.NeedSplit([]byte("e"), regions), IsNil) } + +func (s *testRangeSuite) TestRegionConsistency(c *C) { + cases := []struct { + startKey []byte + endKey []byte + err string + regions []*restore.RegionInfo + }{ + { + codec.EncodeBytes([]byte{}, []byte("a")), + codec.EncodeBytes([]byte{}, []byte("a")), + "scan region return empty result, startKey: (.*?), endKey: (.*?)", + []*restore.RegionInfo{}, + }, + { + codec.EncodeBytes([]byte{}, []byte("a")), + codec.EncodeBytes([]byte{}, []byte("a")), + "first region's startKey > startKey, startKey: (.*?), regionStartKey: (.*?)", + []*restore.RegionInfo{ + { + Region: &metapb.Region{ + StartKey: codec.EncodeBytes([]byte{}, []byte("b")), + EndKey: codec.EncodeBytes([]byte{}, []byte("d")), + }, + }, + }, + }, + { + codec.EncodeBytes([]byte{}, []byte("b")), + codec.EncodeBytes([]byte{}, []byte("e")), + "last region's endKey < endKey, endKey: (.*?), regionEndKey: (.*?)", + []*restore.RegionInfo{ + { + Region: &metapb.Region{ + StartKey: codec.EncodeBytes([]byte{}, []byte("b")), + EndKey: codec.EncodeBytes([]byte{}, []byte("d")), + }, + }, + }, + }, + { + codec.EncodeBytes([]byte{}, []byte("c")), + codec.EncodeBytes([]byte{}, []byte("e")), + "region endKey not equal to next region startKey(.*?)", + []*restore.RegionInfo{ + { + Region: &metapb.Region{ + StartKey: codec.EncodeBytes([]byte{}, []byte("b")), + EndKey: codec.EncodeBytes([]byte{}, []byte("d")), + }, + }, + { + Region: &metapb.Region{ + StartKey: codec.EncodeBytes([]byte{}, []byte("e")), + EndKey: codec.EncodeBytes([]byte{}, []byte("f")), + }, + }, + }, + }, + } + for _, ca := range cases { + c.Assert( + restore.CheckRegionConsistency(ca.startKey, ca.endKey, ca.regions), + ErrorMatches, + ca.err) + } +}