Skip to content

Commit 8599fee

Browse files
crazycs520winkyao
authored andcommitted
*: implement IterReverse for tikvSnapshot and use desc scan to get latest N ddl history jobs. (#10152)
1 parent 03bb568 commit 8599fee

File tree

13 files changed

+236
-29
lines changed

13 files changed

+236
-29
lines changed

executor/executor_test.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"github.com/pingcap/tidb/executor"
4343
"github.com/pingcap/tidb/expression"
4444
"github.com/pingcap/tidb/kv"
45+
"github.com/pingcap/tidb/meta"
4546
"github.com/pingcap/tidb/meta/autoid"
4647
"github.com/pingcap/tidb/planner"
4748
plannercore "github.com/pingcap/tidb/planner/core"
@@ -226,9 +227,9 @@ func (s *testSuite) TestAdmin(c *C) {
226227
result.Check(testkit.Rows())
227228
result = tk.MustQuery(`admin show ddl job queries 1, 2, 3, 4`)
228229
result.Check(testkit.Rows())
229-
historyJob, err := admin.GetHistoryDDLJobs(txn, admin.DefNumHistoryJobs)
230-
result = tk.MustQuery(fmt.Sprintf("admin show ddl job queries %d", historyJob[0].ID))
231-
result.Check(testkit.Rows(historyJob[0].Query))
230+
historyJobs, err = admin.GetHistoryDDLJobs(txn, admin.DefNumHistoryJobs)
231+
result = tk.MustQuery(fmt.Sprintf("admin show ddl job queries %d", historyJobs[0].ID))
232+
result.Check(testkit.Rows(historyJobs[0].Query))
232233
c.Assert(err, IsNil)
233234

234235
// check table test
@@ -282,6 +283,22 @@ func (s *testSuite) TestAdmin(c *C) {
282283
tk.MustExec("ALTER TABLE t1 ADD COLUMN c4 bit(10) default 127;")
283284
tk.MustExec("ALTER TABLE t1 ADD INDEX idx3 (c4);")
284285
tk.MustExec("admin check table t1;")
286+
287+
// Test for reverse scan get history ddl jobs when ddl history jobs queue has multiple regions.
288+
txn, err = s.store.Begin()
289+
c.Assert(err, IsNil)
290+
historyJobs, err = admin.GetHistoryDDLJobs(txn, 20)
291+
c.Assert(err, IsNil)
292+
293+
// Split region for history ddl job queues.
294+
m := meta.NewMeta(txn)
295+
startKey := meta.DDLJobHistoryKey(m, 0)
296+
endKey := meta.DDLJobHistoryKey(m, historyJobs[0].ID)
297+
s.cluster.SplitKeys(s.mvccStore, startKey, endKey, int(historyJobs[0].ID/5))
298+
299+
historyJobs2, err := admin.GetHistoryDDLJobs(txn, 20)
300+
c.Assert(err, IsNil)
301+
c.Assert(historyJobs, DeepEquals, historyJobs2)
285302
}
286303

287304
func (s *testSuite) fillData(tk *testkit.TestKit, table string) {

meta/meta.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ func (m *Meta) tableKey(tableID int64) []byte {
135135
return []byte(fmt.Sprintf("%s:%d", mTablePrefix, tableID))
136136
}
137137

138+
// DDLJobHistoryKey is only used for testing.
139+
func DDLJobHistoryKey(m *Meta, jobID int64) []byte {
140+
return m.txn.EncodeHashDataKey(mDDLJobHistoryKey, m.jobIDKey(jobID))
141+
}
142+
138143
// GenAutoTableIDKeyValue generates meta key by dbID, tableID and corresponding value by autoID.
139144
func (m *Meta) GenAutoTableIDKeyValue(dbID, tableID, autoID int64) (key, value []byte) {
140145
dbKey := m.dbKey(dbID)
@@ -637,10 +642,23 @@ func (m *Meta) GetAllHistoryDDLJobs() ([]*model.Job, error) {
637642
if err != nil {
638643
return nil, errors.Trace(err)
639644
}
640-
jobs := make([]*model.Job, 0, len(pairs))
641-
for _, pair := range pairs {
645+
return decodeAndSortJob(pairs)
646+
}
647+
648+
// GetLastNHistoryDDLJobs gets latest N history ddl jobs.
649+
func (m *Meta) GetLastNHistoryDDLJobs(num int) ([]*model.Job, error) {
650+
pairs, err := m.txn.HGetLastN(mDDLJobHistoryKey, num)
651+
if err != nil {
652+
return nil, errors.Trace(err)
653+
}
654+
return decodeAndSortJob(pairs)
655+
}
656+
657+
func decodeAndSortJob(jobPairs []structure.HashPair) ([]*model.Job, error) {
658+
jobs := make([]*model.Job, 0, len(jobPairs))
659+
for _, pair := range jobPairs {
642660
job := &model.Job{}
643-
err = job.Decode(pair.Value)
661+
err := job.Decode(pair.Value)
644662
if err != nil {
645663
return nil, errors.Trace(err)
646664
}

meta/meta_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ func (s *testSuite) TestMeta(c *C) {
237237

238238
err = txn.Commit(context.Background())
239239
c.Assert(err, IsNil)
240+
241+
// Test for DDLJobHistoryKey.
242+
key = meta.DDLJobHistoryKey(t, 888)
243+
c.Assert(key, DeepEquals, []byte{0x6d, 0x44, 0x44, 0x4c, 0x4a, 0x6f, 0x62, 0x48, 0x69, 0xff, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x78, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf7})
240244
}
241245

242246
func (s *testSuite) TestSnapshot(c *C) {
@@ -356,6 +360,13 @@ func (s *testSuite) TestDDL(c *C) {
356360
lastID = job.ID
357361
}
358362

363+
// Test for get last N history ddl jobs.
364+
historyJobs, err := t.GetLastNHistoryDDLJobs(2)
365+
c.Assert(err, IsNil)
366+
c.Assert(len(historyJobs), Equals, 2)
367+
c.Assert(historyJobs[0].ID == 123, IsTrue)
368+
c.Assert(historyJobs[1].ID == 1234, IsTrue)
369+
359370
// Test GetAllDDLJobsInQueue.
360371
err = t.EnQueueDDLJob(job)
361372
c.Assert(err, IsNil)

store/mockstore/mocktikv/cluster.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/golang/protobuf/proto"
2323
"github.com/pingcap/kvproto/pkg/kvrpcpb"
2424
"github.com/pingcap/kvproto/pkg/metapb"
25+
"github.com/pingcap/tidb/kv"
2526
"github.com/pingcap/tidb/tablecodec"
2627
)
2728

@@ -340,6 +341,12 @@ func (c *Cluster) SplitIndex(mvccStore MVCCStore, tableID, indexID int64, count
340341
c.splitRange(mvccStore, NewMvccKey(indexStart), NewMvccKey(indexEnd), count)
341342
}
342343

344+
// SplitKeys evenly splits the start, end key into "count" regions.
345+
// Only works for single store.
346+
func (c *Cluster) SplitKeys(mvccStore MVCCStore, start, end kv.Key, count int) {
347+
c.splitRange(mvccStore, NewMvccKey(start), NewMvccKey(end), count)
348+
}
349+
343350
func (c *Cluster) splitRange(mvccStore MVCCStore, start, end MvccKey, count int) {
344351
c.Lock()
345352
defer c.Unlock()

store/mockstore/mocktikv/rpc.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,14 +245,32 @@ func (h *rpcHandler) handleKvGet(req *kvrpcpb.GetRequest) *kvrpcpb.GetResponse {
245245
}
246246

247247
func (h *rpcHandler) handleKvScan(req *kvrpcpb.ScanRequest) *kvrpcpb.ScanResponse {
248-
if !h.checkKeyInRegion(req.GetStartKey()) {
249-
panic("KvScan: startKey not in region")
250-
}
251248
endKey := MvccKey(h.endKey).Raw()
252-
if len(req.EndKey) > 0 && (len(endKey) == 0 || bytes.Compare(NewMvccKey(req.EndKey), h.endKey) < 0) {
253-
endKey = req.EndKey
249+
var pairs []Pair
250+
if !req.Reverse {
251+
if !h.checkKeyInRegion(req.GetStartKey()) {
252+
panic("KvScan: startKey not in region")
253+
}
254+
if len(req.EndKey) > 0 && (len(endKey) == 0 || bytes.Compare(NewMvccKey(req.EndKey), h.endKey) < 0) {
255+
endKey = req.EndKey
256+
}
257+
pairs = h.mvccStore.Scan(req.GetStartKey(), endKey, int(req.GetLimit()), req.GetVersion(), h.isolationLevel)
258+
} else {
259+
// TiKV use range [end_key, start_key) for reverse scan.
260+
// Should use the req.EndKey to check in region.
261+
if !h.checkKeyInRegion(req.GetEndKey()) {
262+
panic("KvScan: startKey not in region")
263+
}
264+
265+
// TiKV use range [end_key, start_key) for reverse scan.
266+
// So the req.StartKey actually is the end_key.
267+
if len(req.StartKey) > 0 && (len(endKey) == 0 || bytes.Compare(NewMvccKey(req.StartKey), h.endKey) < 0) {
268+
endKey = req.StartKey
269+
}
270+
271+
pairs = h.mvccStore.ReverseScan(req.EndKey, endKey, int(req.GetLimit()), req.GetVersion(), h.isolationLevel)
254272
}
255-
pairs := h.mvccStore.Scan(req.GetStartKey(), endKey, int(req.GetLimit()), req.GetVersion(), h.isolationLevel)
273+
256274
return &kvrpcpb.ScanResponse{
257275
Pairs: convertToPbPairs(pairs),
258276
}

store/tikv/scan.go

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ type Scanner struct {
3535
nextStartKey []byte
3636
endKey []byte
3737
eof bool
38+
39+
// Use for reverse scan.
40+
reverse bool
41+
nextEndKey []byte
3842
}
3943

40-
func newScanner(snapshot *tikvSnapshot, startKey []byte, endKey []byte, batchSize int) (*Scanner, error) {
44+
func newScanner(snapshot *tikvSnapshot, startKey []byte, endKey []byte, batchSize int, reverse bool) (*Scanner, error) {
4145
// It must be > 1. Otherwise scanner won't skipFirst.
4246
if batchSize <= 1 {
4347
batchSize = scanBatchSize
@@ -48,6 +52,8 @@ func newScanner(snapshot *tikvSnapshot, startKey []byte, endKey []byte, batchSiz
4852
valid: true,
4953
nextStartKey: startKey,
5054
endKey: endKey,
55+
reverse: reverse,
56+
nextEndKey: endKey,
5157
}
5258
err := scanner.Next()
5359
if kv.IsErrNotFound(err) {
@@ -83,14 +89,15 @@ func (s *Scanner) Next() error {
8389
if !s.valid {
8490
return errors.New("scanner iterator is invalid")
8591
}
92+
var err error
8693
for {
8794
s.idx++
8895
if s.idx >= len(s.cache) {
8996
if s.eof {
9097
s.Close()
9198
return nil
9299
}
93-
err := s.getData(bo)
100+
err = s.getData(bo)
94101
if err != nil {
95102
s.Close()
96103
return errors.Trace(err)
@@ -101,7 +108,8 @@ func (s *Scanner) Next() error {
101108
}
102109

103110
current := s.cache[s.idx]
104-
if len(s.endKey) > 0 && kv.Key(current.Key).Cmp(kv.Key(s.endKey)) >= 0 {
111+
if (!s.reverse && (len(s.endKey) > 0 && kv.Key(current.Key).Cmp(kv.Key(s.endKey)) >= 0)) ||
112+
(s.reverse && len(s.nextStartKey) > 0 && kv.Key(current.Key).Cmp(kv.Key(s.nextStartKey)) < 0) {
105113
s.eof = true
106114
s.Close()
107115
return nil
@@ -147,18 +155,34 @@ func (s *Scanner) resolveCurrentLock(bo *Backoffer, current *pb.KvPair) error {
147155
func (s *Scanner) getData(bo *Backoffer) error {
148156
logutil.Logger(context.Background()).Debug("txn getData",
149157
zap.Binary("nextStartKey", s.nextStartKey),
158+
zap.Binary("nextEndKey", s.nextEndKey),
159+
zap.Bool("reverse", s.reverse),
150160
zap.Uint64("txnStartTS", s.startTS()))
151161
sender := NewRegionRequestSender(s.snapshot.store.regionCache, s.snapshot.store.client)
152-
162+
var reqEndKey, reqStartKey []byte
163+
var loc *KeyLocation
164+
var err error
153165
for {
154-
loc, err := s.snapshot.store.regionCache.LocateKey(bo, s.nextStartKey)
166+
if !s.reverse {
167+
loc, err = s.snapshot.store.regionCache.LocateKey(bo, s.nextStartKey)
168+
} else {
169+
loc, err = s.snapshot.store.regionCache.LocateEndKey(bo, s.nextEndKey)
170+
}
155171
if err != nil {
156172
return errors.Trace(err)
157173
}
158174

159-
reqEndKey := s.endKey
160-
if len(reqEndKey) > 0 && len(loc.EndKey) > 0 && bytes.Compare(loc.EndKey, reqEndKey) < 0 {
161-
reqEndKey = loc.EndKey
175+
if !s.reverse {
176+
reqEndKey = s.endKey
177+
if len(reqEndKey) > 0 && len(loc.EndKey) > 0 && bytes.Compare(loc.EndKey, reqEndKey) < 0 {
178+
reqEndKey = loc.EndKey
179+
}
180+
} else {
181+
reqStartKey = s.nextStartKey
182+
if len(reqStartKey) == 0 ||
183+
(len(loc.StartKey) > 0 && bytes.Compare(loc.StartKey, reqStartKey) > 0) {
184+
reqStartKey = loc.StartKey
185+
}
162186
}
163187

164188
req := &tikvrpc.Request{
@@ -175,6 +199,11 @@ func (s *Scanner) getData(bo *Backoffer) error {
175199
NotFillCache: s.snapshot.notFillCache,
176200
},
177201
}
202+
if s.reverse {
203+
req.Scan.StartKey = s.nextEndKey
204+
req.Scan.EndKey = reqStartKey
205+
req.Scan.Reverse = true
206+
}
178207
resp, err := sender.SendReq(bo, req, loc.Region, ReadTimeoutMedium)
179208
if err != nil {
180209
return errors.Trace(err)
@@ -218,8 +247,13 @@ func (s *Scanner) getData(bo *Backoffer) error {
218247
if len(kvPairs) < s.batchSize {
219248
// No more data in current Region. Next getData() starts
220249
// from current Region's endKey.
221-
s.nextStartKey = loc.EndKey
222-
if len(loc.EndKey) == 0 || (len(s.endKey) > 0 && kv.Key(s.nextStartKey).Cmp(kv.Key(s.endKey)) >= 0) {
250+
if !s.reverse {
251+
s.nextStartKey = loc.EndKey
252+
} else {
253+
s.nextEndKey = reqStartKey
254+
}
255+
if (!s.reverse && (len(loc.EndKey) == 0 || (len(s.endKey) > 0 && kv.Key(s.nextStartKey).Cmp(kv.Key(s.endKey)) >= 0))) ||
256+
(s.reverse && (len(loc.StartKey) == 0 || (len(s.nextStartKey) > 0 && kv.Key(s.nextStartKey).Cmp(kv.Key(s.nextEndKey)) >= 0))) {
223257
// Current Region is the last one.
224258
s.eof = true
225259
}
@@ -230,7 +264,11 @@ func (s *Scanner) getData(bo *Backoffer) error {
230264
// may get an empty response if the Region in fact does not have
231265
// more data.
232266
lastKey := kvPairs[len(kvPairs)-1].GetKey()
233-
s.nextStartKey = kv.Key(lastKey).Next()
267+
if !s.reverse {
268+
s.nextStartKey = kv.Key(lastKey).Next()
269+
} else {
270+
s.nextEndKey = kv.Key(lastKey)
271+
}
234272
return nil
235273
}
236274
}

store/tikv/scan_mock_test.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,52 @@ func (s *testScanMockSuite) TestScanMultipleRegions(c *C) {
4242
txn, err = store.Begin()
4343
c.Assert(err, IsNil)
4444
snapshot := newTiKVSnapshot(store, kv.Version{Ver: txn.StartTS()})
45-
scanner, err := newScanner(snapshot, []byte("a"), nil, 10)
45+
scanner, err := newScanner(snapshot, []byte("a"), nil, 10, false)
4646
c.Assert(err, IsNil)
4747
for ch := byte('a'); ch <= byte('z'); ch++ {
4848
c.Assert([]byte{ch}, BytesEquals, []byte(scanner.Key()))
4949
c.Assert(scanner.Next(), IsNil)
5050
}
5151
c.Assert(scanner.Valid(), IsFalse)
5252

53-
scanner, err = newScanner(snapshot, []byte("a"), []byte("i"), 10)
53+
scanner, err = newScanner(snapshot, []byte("a"), []byte("i"), 10, false)
5454
c.Assert(err, IsNil)
5555
for ch := byte('a'); ch <= byte('h'); ch++ {
5656
c.Assert([]byte{ch}, BytesEquals, []byte(scanner.Key()))
5757
c.Assert(scanner.Next(), IsNil)
5858
}
5959
c.Assert(scanner.Valid(), IsFalse)
6060
}
61+
62+
func (s *testScanMockSuite) TestReverseScan(c *C) {
63+
store := NewTestStore(c).(*tikvStore)
64+
defer store.Close()
65+
66+
txn, err := store.Begin()
67+
c.Assert(err, IsNil)
68+
for ch := byte('a'); ch <= byte('z'); ch++ {
69+
err = txn.Set([]byte{ch}, []byte{ch})
70+
c.Assert(err, IsNil)
71+
}
72+
err = txn.Commit(context.Background())
73+
c.Assert(err, IsNil)
74+
75+
txn, err = store.Begin()
76+
c.Assert(err, IsNil)
77+
snapshot := newTiKVSnapshot(store, kv.Version{Ver: txn.StartTS()})
78+
scanner, err := newScanner(snapshot, nil, []byte("z"), 10, true)
79+
c.Assert(err, IsNil)
80+
for ch := byte('y'); ch >= byte('a'); ch-- {
81+
c.Assert(string([]byte{ch}), Equals, string([]byte(scanner.Key())))
82+
c.Assert(scanner.Next(), IsNil)
83+
}
84+
c.Assert(scanner.Valid(), IsFalse)
85+
86+
scanner, err = newScanner(snapshot, []byte("a"), []byte("i"), 10, true)
87+
c.Assert(err, IsNil)
88+
for ch := byte('h'); ch >= byte('a'); ch-- {
89+
c.Assert(string([]byte{ch}), Equals, string([]byte(scanner.Key())))
90+
c.Assert(scanner.Next(), IsNil)
91+
}
92+
c.Assert(scanner.Valid(), IsFalse)
93+
}

store/tikv/snapshot.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,14 @@ func (s *tikvSnapshot) get(bo *Backoffer, k kv.Key) ([]byte, error) {
295295

296296
// Iter return a list of key-value pair after `k`.
297297
func (s *tikvSnapshot) Iter(k kv.Key, upperBound kv.Key) (kv.Iterator, error) {
298-
scanner, err := newScanner(s, k, upperBound, scanBatchSize)
298+
scanner, err := newScanner(s, k, upperBound, scanBatchSize, false)
299299
return scanner, errors.Trace(err)
300300
}
301301

302302
// IterReverse creates a reversed Iterator positioned on the first entry which key is less than k.
303303
func (s *tikvSnapshot) IterReverse(k kv.Key) (kv.Iterator, error) {
304-
return nil, kv.ErrNotImplemented
304+
scanner, err := newScanner(s, nil, k, scanBatchSize, true)
305+
return scanner, errors.Trace(err)
305306
}
306307

307308
func extractLockFromKeyErr(keyErr *pb.KeyError) (*Lock, error) {

store/tikv/split_region.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (s *tikvStore) WaitScatterRegionFinish(regionID uint64) error {
115115
if logFreq%10 == 0 {
116116
logutil.Logger(context.Background()).Info("wait scatter region",
117117
zap.Uint64("regionID", regionID),
118-
zap.String("desc", string(resp.Desc)),
118+
zap.String("reverse", string(resp.Desc)),
119119
zap.String("status", pdpb.OperatorStatus_name[int32(resp.Status)]))
120120
}
121121
logFreq++

0 commit comments

Comments
 (0)