diff --git a/store/tikv/coprocessor.go b/store/tikv/coprocessor.go index 546b42faeb..3115600626 100644 --- a/store/tikv/coprocessor.go +++ b/store/tikv/coprocessor.go @@ -278,10 +278,26 @@ func buildCopTasks(bo *Backoffer, cache *RegionCache, ranges *copRanges, desc bo }) } + err := splitRanges(bo, cache, ranges, appendTask) + if err != nil { + return nil, errors.Trace(err) + } + + if desc { + reverseTasks(tasks) + } + if elapsed := time.Since(start); elapsed > time.Millisecond*500 { + log.Warnf("buildCopTasks takes too much time (%v), range len %v, task len %v", elapsed, rangesLen, len(tasks)) + } + metrics.TiKVTxnRegionsNumHistogram.WithLabelValues("coprocessor").Observe(float64(len(tasks))) + return tasks, nil +} + +func splitRanges(bo *Backoffer, cache *RegionCache, ranges *copRanges, fn func(region RegionVerID, ranges *copRanges)) error { for ranges.len() > 0 { loc, err := cache.LocateKey(bo, ranges.at(0).StartKey) if err != nil { - return nil, errors.Trace(err) + return errors.Trace(err) } // Iterate to the first range that is not complete in the region. @@ -294,7 +310,7 @@ func buildCopTasks(bo *Backoffer, cache *RegionCache, ranges *copRanges, desc bo } // All rest ranges belong to the same region. if i == ranges.len() { - appendTask(loc.Region, ranges) + fn(loc.Region, ranges) break } @@ -306,7 +322,7 @@ func buildCopTasks(bo *Backoffer, cache *RegionCache, ranges *copRanges, desc bo StartKey: r.StartKey, EndKey: loc.EndKey, } - appendTask(loc.Region, taskRanges) + fn(loc.Region, taskRanges) ranges = ranges.slice(i+1, ranges.len()) ranges.first = &kv.KeyRange{ @@ -315,19 +331,31 @@ func buildCopTasks(bo *Backoffer, cache *RegionCache, ranges *copRanges, desc bo } } else { // rs[i] is not in the region. - appendTask(loc.Region, ranges.slice(0, i)) + taskRanges := ranges.slice(0, i) + fn(loc.Region, taskRanges) ranges = ranges.slice(i, ranges.len()) } } - if desc { - reverseTasks(tasks) + return nil +} + +// SplitRegionRanges get the split ranges from pd region. +func SplitRegionRanges(bo *Backoffer, cache *RegionCache, keyRanges []kv.KeyRange) ([]kv.KeyRange, error) { + ranges := copRanges{mid: keyRanges} + + var ret []kv.KeyRange + appendRange := func(region RegionVerID, ranges *copRanges) { + for i := 0; i < ranges.len(); i++ { + ret = append(ret, ranges.at(i)) + } } - if elapsed := time.Since(start); elapsed > time.Millisecond*500 { - log.Warnf("buildCopTasks takes too much time (%v), range len %v, task len %v", elapsed, rangesLen, len(tasks)) + + err := splitRanges(bo, cache, &ranges, appendRange) + if err != nil { + return nil, errors.Trace(err) } - metrics.TiKVTxnRegionsNumHistogram.WithLabelValues("coprocessor").Observe(float64(len(tasks))) - return tasks, nil + return ret, nil } func reverseTasks(tasks []*copTask) { diff --git a/store/tikv/coprocessor_test.go b/store/tikv/coprocessor_test.go index c53f1870d4..c400b759c5 100644 --- a/store/tikv/coprocessor_test.go +++ b/store/tikv/coprocessor_test.go @@ -35,28 +35,28 @@ func (s *testCoprocessorSuite) TestBuildTasks(c *C) { bo := NewBackoffer(context.Background(), 3000) - tasks, err := buildCopTasks(bo, cache, buildKeyRanges("a", "c"), false, false) + tasks, err := buildCopTasks(bo, cache, buildCopRanges("a", "c"), false, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 1) s.taskEqual(c, tasks[0], regionIDs[0], "a", "c") - tasks, err = buildCopTasks(bo, cache, buildKeyRanges("g", "n"), false, false) + tasks, err = buildCopTasks(bo, cache, buildCopRanges("g", "n"), false, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 1) s.taskEqual(c, tasks[0], regionIDs[1], "g", "n") - tasks, err = buildCopTasks(bo, cache, buildKeyRanges("m", "n"), false, false) + tasks, err = buildCopTasks(bo, cache, buildCopRanges("m", "n"), false, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 1) s.taskEqual(c, tasks[0], regionIDs[1], "m", "n") - tasks, err = buildCopTasks(bo, cache, buildKeyRanges("a", "k"), false, false) + tasks, err = buildCopTasks(bo, cache, buildCopRanges("a", "k"), false, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 2) s.taskEqual(c, tasks[0], regionIDs[0], "a", "g") s.taskEqual(c, tasks[1], regionIDs[1], "g", "k") - tasks, err = buildCopTasks(bo, cache, buildKeyRanges("a", "x"), false, false) + tasks, err = buildCopTasks(bo, cache, buildCopRanges("a", "x"), false, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 4) s.taskEqual(c, tasks[0], regionIDs[0], "a", "g") @@ -64,29 +64,81 @@ func (s *testCoprocessorSuite) TestBuildTasks(c *C) { s.taskEqual(c, tasks[2], regionIDs[2], "n", "t") s.taskEqual(c, tasks[3], regionIDs[3], "t", "x") - tasks, err = buildCopTasks(bo, cache, buildKeyRanges("a", "b", "b", "c"), false, false) + tasks, err = buildCopTasks(bo, cache, buildCopRanges("a", "b", "b", "c"), false, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 1) s.taskEqual(c, tasks[0], regionIDs[0], "a", "b", "b", "c") - tasks, err = buildCopTasks(bo, cache, buildKeyRanges("a", "b", "e", "f"), false, false) + tasks, err = buildCopTasks(bo, cache, buildCopRanges("a", "b", "e", "f"), false, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 1) s.taskEqual(c, tasks[0], regionIDs[0], "a", "b", "e", "f") - tasks, err = buildCopTasks(bo, cache, buildKeyRanges("g", "n", "o", "p"), false, false) + tasks, err = buildCopTasks(bo, cache, buildCopRanges("g", "n", "o", "p"), false, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 2) s.taskEqual(c, tasks[0], regionIDs[1], "g", "n") s.taskEqual(c, tasks[1], regionIDs[2], "o", "p") - tasks, err = buildCopTasks(bo, cache, buildKeyRanges("h", "k", "m", "p"), false, false) + tasks, err = buildCopTasks(bo, cache, buildCopRanges("h", "k", "m", "p"), false, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 2) s.taskEqual(c, tasks[0], regionIDs[1], "h", "k", "m", "n") s.taskEqual(c, tasks[1], regionIDs[2], "n", "p") } +func (s *testCoprocessorSuite) TestSplitRegionRanges(c *C) { + // nil --- 'g' --- 'n' --- 't' --- nil + // <- 0 -> <- 1 -> <- 2 -> <- 3 -> + cluster := mocktikv.NewCluster() + mocktikv.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) + pdCli := &codecPDClient{mocktikv.NewPDClient(cluster)} + cache := NewRegionCache(pdCli) + + bo := NewBackoffer(context.Background(), 3000) + + ranges, err := SplitRegionRanges(bo, cache, buildKeyRanges("a", "c")) + c.Assert(err, IsNil) + c.Assert(ranges, HasLen, 1) + s.rangeEqual(c, ranges, "a", "c") + + ranges, err = SplitRegionRanges(bo, cache, buildKeyRanges("h", "y")) + c.Assert(err, IsNil) + c.Assert(len(ranges), Equals, 3) + s.rangeEqual(c, ranges, "h", "n", "n", "t", "t", "y") + + ranges, err = SplitRegionRanges(bo, cache, buildKeyRanges("s", "z")) + c.Assert(err, IsNil) + c.Assert(len(ranges), Equals, 2) + s.rangeEqual(c, ranges, "s", "t", "t", "z") + + ranges, err = SplitRegionRanges(bo, cache, buildKeyRanges("s", "s")) + c.Assert(err, IsNil) + c.Assert(len(ranges), Equals, 1) + s.rangeEqual(c, ranges, "s", "s") + + ranges, err = SplitRegionRanges(bo, cache, buildKeyRanges("t", "t")) + c.Assert(err, IsNil) + c.Assert(len(ranges), Equals, 1) + s.rangeEqual(c, ranges, "t", "t") + + ranges, err = SplitRegionRanges(bo, cache, buildKeyRanges("t", "u")) + c.Assert(err, IsNil) + c.Assert(len(ranges), Equals, 1) + s.rangeEqual(c, ranges, "t", "u") + + ranges, err = SplitRegionRanges(bo, cache, buildKeyRanges("u", "z")) + c.Assert(err, IsNil) + c.Assert(len(ranges), Equals, 1) + s.rangeEqual(c, ranges, "u", "z") + + // min --> max + ranges, err = SplitRegionRanges(bo, cache, buildKeyRanges("a", "z")) + c.Assert(err, IsNil) + c.Assert(ranges, HasLen, 4) + s.rangeEqual(c, ranges, "a", "g", "g", "n", "n", "t", "t", "z") +} + func (s *testCoprocessorSuite) TestRebuild(c *C) { // nil --- 'm' --- nil // <- 0 -> <- 1 -> @@ -96,7 +148,7 @@ func (s *testCoprocessorSuite) TestRebuild(c *C) { cache := NewRegionCache(pdCli) bo := NewBackoffer(context.Background(), 3000) - tasks, err := buildCopTasks(bo, cache, buildKeyRanges("a", "z"), false, false) + tasks, err := buildCopTasks(bo, cache, buildCopRanges("a", "z"), false, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 2) s.taskEqual(c, tasks[0], regionIDs[0], "a", "m") @@ -109,7 +161,7 @@ func (s *testCoprocessorSuite) TestRebuild(c *C) { cluster.Split(regionIDs[1], regionIDs[2], []byte("q"), []uint64{peerIDs[2]}, storeID) cache.DropRegion(tasks[1].region) - tasks, err = buildCopTasks(bo, cache, buildKeyRanges("a", "z"), true, false) + tasks, err = buildCopTasks(bo, cache, buildCopRanges("a", "z"), true, false) c.Assert(err, IsNil) c.Assert(tasks, HasLen, 3) s.taskEqual(c, tasks[2], regionIDs[0], "a", "m") @@ -117,7 +169,7 @@ func (s *testCoprocessorSuite) TestRebuild(c *C) { s.taskEqual(c, tasks[0], regionIDs[2], "q", "z") } -func buildKeyRanges(keys ...string) *copRanges { +func buildKeyRanges(keys ...string) []kv.KeyRange { var ranges []kv.KeyRange for i := 0; i < len(keys); i += 2 { ranges = append(ranges, kv.KeyRange{ @@ -125,6 +177,11 @@ func buildKeyRanges(keys ...string) *copRanges { EndKey: []byte(keys[i+1]), }) } + return ranges +} + +func buildCopRanges(keys ...string) *copRanges { + ranges := buildKeyRanges(keys...) return &copRanges{mid: ranges} } @@ -137,6 +194,14 @@ func (s *testCoprocessorSuite) taskEqual(c *C, task *copTask, regionID uint64, k } } +func (s *testCoprocessorSuite) rangeEqual(c *C, ranges []kv.KeyRange, keys ...string) { + for i := 0; i < len(ranges); i++ { + r := ranges[i] + c.Assert(string(r.StartKey), Equals, keys[2*i]) + c.Assert(string(r.EndKey), Equals, keys[2*i+1]) + } +} + func (s *testCoprocessorSuite) TestCopRanges(c *C) { ranges := []kv.KeyRange{ {StartKey: []byte("a"), EndKey: []byte("b")}, @@ -178,49 +243,49 @@ func (s *testCoprocessorSuite) TestCopRangeSplit(c *C) { // input range: [c-d) [e-g) [l-o) ranges := &copRanges{mid: mid} s.testSplit(c, ranges, right, - splitCase{"c", buildKeyRanges("c", "d", "e", "g", "l", "o")}, - splitCase{"d", buildKeyRanges("e", "g", "l", "o")}, - splitCase{"f", buildKeyRanges("f", "g", "l", "o")}, + splitCase{"c", buildCopRanges("c", "d", "e", "g", "l", "o")}, + splitCase{"d", buildCopRanges("e", "g", "l", "o")}, + splitCase{"f", buildCopRanges("f", "g", "l", "o")}, ) // input range: [a-b) [c-d) [e-g) [l-o) ranges = &copRanges{first: first, mid: mid} s.testSplit(c, ranges, right, - splitCase{"a", buildKeyRanges("a", "b", "c", "d", "e", "g", "l", "o")}, - splitCase{"c", buildKeyRanges("c", "d", "e", "g", "l", "o")}, - splitCase{"m", buildKeyRanges("m", "o")}, + splitCase{"a", buildCopRanges("a", "b", "c", "d", "e", "g", "l", "o")}, + splitCase{"c", buildCopRanges("c", "d", "e", "g", "l", "o")}, + splitCase{"m", buildCopRanges("m", "o")}, ) // input range: [a-b) [c-d) [e-g) [l-o) [q-t) ranges = &copRanges{first: first, mid: mid, last: last} s.testSplit(c, ranges, right, - splitCase{"f", buildKeyRanges("f", "g", "l", "o", "q", "t")}, - splitCase{"h", buildKeyRanges("l", "o", "q", "t")}, - splitCase{"r", buildKeyRanges("r", "t")}, + splitCase{"f", buildCopRanges("f", "g", "l", "o", "q", "t")}, + splitCase{"h", buildCopRanges("l", "o", "q", "t")}, + splitCase{"r", buildCopRanges("r", "t")}, ) // input range: [c-d) [e-g) [l-o) ranges = &copRanges{mid: mid} s.testSplit(c, ranges, left, - splitCase{"m", buildKeyRanges("c", "d", "e", "g", "l", "m")}, - splitCase{"g", buildKeyRanges("c", "d", "e", "g")}, - splitCase{"g", buildKeyRanges("c", "d", "e", "g")}, + splitCase{"m", buildCopRanges("c", "d", "e", "g", "l", "m")}, + splitCase{"g", buildCopRanges("c", "d", "e", "g")}, + splitCase{"g", buildCopRanges("c", "d", "e", "g")}, ) // input range: [a-b) [c-d) [e-g) [l-o) ranges = &copRanges{first: first, mid: mid} s.testSplit(c, ranges, left, - splitCase{"d", buildKeyRanges("a", "b", "c", "d")}, - splitCase{"d", buildKeyRanges("a", "b", "c", "d")}, - splitCase{"o", buildKeyRanges("a", "b", "c", "d", "e", "g", "l", "o")}, + splitCase{"d", buildCopRanges("a", "b", "c", "d")}, + splitCase{"d", buildCopRanges("a", "b", "c", "d")}, + splitCase{"o", buildCopRanges("a", "b", "c", "d", "e", "g", "l", "o")}, ) // input range: [a-b) [c-d) [e-g) [l-o) [q-t) ranges = &copRanges{first: first, mid: mid, last: last} s.testSplit(c, ranges, left, - splitCase{"o", buildKeyRanges("a", "b", "c", "d", "e", "g", "l", "o")}, - splitCase{"p", buildKeyRanges("a", "b", "c", "d", "e", "g", "l", "o")}, - splitCase{"t", buildKeyRanges("a", "b", "c", "d", "e", "g", "l", "o", "q", "t")}, + splitCase{"o", buildCopRanges("a", "b", "c", "d", "e", "g", "l", "o")}, + splitCase{"p", buildCopRanges("a", "b", "c", "d", "e", "g", "l", "o")}, + splitCase{"t", buildCopRanges("a", "b", "c", "d", "e", "g", "l", "o", "q", "t")}, ) }