diff --git a/store/mockstore/mocktikv/rpc.go b/store/mockstore/mocktikv/rpc.go index 9275cce60613..c56948266425 100644 --- a/store/mockstore/mocktikv/rpc.go +++ b/store/mockstore/mocktikv/rpc.go @@ -476,22 +476,25 @@ func (h *rpcHandler) handleKvRawScan(req *kvrpcpb.RawScanRequest) *kvrpcpb.RawSc } } - endKey := h.endKey - if len(req.EndKey) > 0 && (len(endKey) == 0 || bytes.Compare(req.EndKey, endKey) < 0) { - endKey = req.EndKey - } - var pairs []Pair if req.Reverse { + lowerBound := h.startKey + if len(req.EndKey) > 0 && (len(lowerBound) == 0 || bytes.Compare(req.EndKey, lowerBound) > 0) { + lowerBound = req.EndKey + } pairs = rawKV.RawReverseScan( - req.GetStartKey(), // region start position - h.startKey, // upper bound of scan + req.StartKey, + lowerBound, int(req.GetLimit()), ) } else { + upperBound := h.endKey + if len(req.EndKey) > 0 && (len(upperBound) == 0 || bytes.Compare(req.EndKey, upperBound) < 0) { + upperBound = req.EndKey + } pairs = rawKV.RawScan( - req.GetStartKey(), // region start position - endKey, // lower bound of scan + req.StartKey, + upperBound, int(req.GetLimit()), ) } diff --git a/store/tikv/rawkv.go b/store/tikv/rawkv.go index 4b27d24e026a..740db88a832b 100644 --- a/store/tikv/rawkv.go +++ b/store/tikv/rawkv.go @@ -310,9 +310,13 @@ func (c *RawKVClient) Scan(startKey, endKey []byte, limit int) (keys [][]byte, v return } -// ReverseScan queries continuous kv pairs, starts from startKey and goes backwards, up to limit pairs. -// startKey is the exclusive upper bound. If you want to include the startKey, append a '\0' to the key -func (c *RawKVClient) ReverseScan(startKey []byte, limit int) (keys [][]byte, values [][]byte, err error) { +// ReverseScan queries continuous kv pairs in range [endKey, startKey), up to limit pairs. +// Direction is different from Scan, upper to lower. +// If endKey is empty, it means unbounded. +// If you want to exclude the startKey or include the endKey, append a '\0' to the key. For example, to scan +// (endKey, startKey], you can write: +// `ReverseScan(append(startKey, '\0'), append(endKey, '\0'), limit)`. +func (c *RawKVClient) ReverseScan(startKey, endKey []byte, limit int) (keys [][]byte, values [][]byte, err error) { start := time.Now() defer func() { metrics.TiKVRawkvCmdHistogram.WithLabelValues("raw_reverse_scan").Observe(time.Since(start).Seconds()) @@ -327,6 +331,7 @@ func (c *RawKVClient) ReverseScan(startKey []byte, limit int) (keys [][]byte, va Type: tikvrpc.CmdRawScan, RawScan: &kvrpcpb.RawScanRequest{ StartKey: startKey, + EndKey: endKey, Limit: uint32(limit - len(keys)), Reverse: true, }, diff --git a/store/tikv/rawkv_test.go b/store/tikv/rawkv_test.go index aa591b4e1f2b..ffcc97346aa2 100644 --- a/store/tikv/rawkv_test.go +++ b/store/tikv/rawkv_test.go @@ -123,7 +123,17 @@ func (s *testRawKVSuite) mustScanRange(c *C, startKey string, endKey string, lim } func (s *testRawKVSuite) mustReverseScan(c *C, startKey []byte, limit int, expect ...string) { - keys, values, err := s.client.ReverseScan(startKey, limit) + keys, values, err := s.client.ReverseScan(startKey, nil, limit) + c.Assert(err, IsNil) + c.Assert(len(keys)*2, Equals, len(expect)) + for i := range keys { + c.Assert(string(keys[i]), Equals, expect[i*2]) + c.Assert(string(values[i]), Equals, expect[i*2+1]) + } +} + +func (s *testRawKVSuite) mustReverseScanRange(c *C, startKey, endKey []byte, limit int, expect ...string) { + keys, values, err := s.client.ReverseScan(startKey, endKey, limit) c.Assert(err, IsNil) c.Assert(len(keys)*2, Equals, len(expect)) for i := range keys { @@ -257,6 +267,9 @@ func (s *testRawKVSuite) TestReverseScan(c *C) { s.mustReverseScan(c, []byte("k5"), 1, "k3", "v3") s.mustReverseScan(c, append([]byte("k5"), 0), 1, "k5", "v5") s.mustReverseScan(c, []byte("k6"), 3, "k5", "v5", "k3", "v3", "k1", "v1") + + s.mustReverseScanRange(c, []byte("z"), []byte("k3"), 10, "k7", "v7", "k5", "v5", "k3", "v3") + s.mustReverseScanRange(c, []byte("k7"), append([]byte("k3"), 0), 10, "k5", "v5") } check()