Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add keyonly support for seek #7419

Merged
merged 9 commits into from
Aug 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions kv/kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ const (
// BypassLatch option tells 2PC commit to bypass latches, it would be true when the
// transaction is not conflict-retryable, for example: 'select for update', 'load data'.
BypassLatch
// KeyOnly retrieve only keys, it can be used in scan now.
KeyOnly
)

// Priority value for transaction priority.
Expand Down
16 changes: 16 additions & 0 deletions store/tikv/lock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,22 @@ func (s *testLockSuite) TestScanLockResolveWithSeek(c *C) {
}
}

func (s *testLockSuite) TestScanLockResolveWithSeekKeyOnly(c *C) {
s.putAlphabets(c)
s.prepareAlphabetLocks(c)

txn, err := s.store.Begin()
c.Assert(err, IsNil)
txn.SetOption(kv.KeyOnly, true)
iter, err := txn.Seek([]byte("a"))
c.Assert(err, IsNil)
for ch := byte('a'); ch <= byte('z'); ch++ {
c.Assert(iter.Valid(), IsTrue)
c.Assert([]byte(iter.Key()), BytesEquals, []byte{ch})
shafreeck marked this conversation as resolved.
Show resolved Hide resolved
c.Assert(iter.Next(), IsNil)
}
}

func (s *testLockSuite) TestScanLockResolveWithBatchGet(c *C) {
s.putAlphabets(c)
s.prepareAlphabetLocks(c)
Expand Down
30 changes: 18 additions & 12 deletions store/tikv/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,22 @@ func (s *Scanner) Next() error {
continue
}
}
if err := s.resolveCurrentLock(bo); err != nil {
s.Close()
return errors.Trace(err)
}
if len(s.Value()) == 0 {
// nil stands for NotExist, go to next KV pair.
continue

current := s.cache[s.idx]
// Try to resolve the lock
if current.GetError() != nil {
// 'current' would be modified if the lock being resolved
if err := s.resolveCurrentLock(bo, current); err != nil {
s.Close()
return errors.Trace(err)
}

// The check here does not violate the KeyOnly semantic, because current's value
// is filled by resolveCurrentLock which fetches the value by snapshot.get, so an empty
// value stands for NotExist
if len(current.Value) == 0 {
continue
}
}
return nil
}
Expand All @@ -115,11 +124,7 @@ func (s *Scanner) startTS() uint64 {
return s.snapshot.version.Ver
}

func (s *Scanner) resolveCurrentLock(bo *Backoffer) error {
current := s.cache[s.idx]
if current.GetError() == nil {
return nil
}
func (s *Scanner) resolveCurrentLock(bo *Backoffer, current *pb.KvPair) error {
val, err := s.snapshot.get(bo, kv.Key(current.Key))
if err != nil {
return errors.Trace(err)
Expand All @@ -144,6 +149,7 @@ func (s *Scanner) getData(bo *Backoffer) error {
StartKey: s.nextStartKey,
Limit: uint32(s.batchSize),
Version: s.startTS(),
KeyOnly: s.snapshot.keyOnly,
},
Context: pb.Context{
Priority: s.snapshot.priority,
Expand Down
37 changes: 37 additions & 0 deletions store/tikv/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"time"

. "github.com/pingcap/check"
"github.com/pingcap/tidb/kv"
"golang.org/x/net/context"
)

Expand Down Expand Up @@ -91,5 +92,41 @@ func (s *testScanSuite) TestSeek(c *C) {
}
scan.Next()
c.Assert(scan.Valid(), IsFalse)

txn3 := s.beginTxn(c)
txn3.SetOption(kv.KeyOnly, true)
scan, err = txn3.Seek(encodeKey(s.prefix, ""))
c.Assert(err, IsNil)

for i := 0; i < rowNum; i++ {
k := scan.Key()
c.Assert([]byte(k), BytesEquals, encodeKey(s.prefix, s08d("key", i)))
// Because newScan return first item without calling scan.Next() just like go-hbase,
// for-loop count will decrease 1.
if i < rowNum-1 {
scan.Next()
}
}
scan.Next()
c.Assert(scan.Valid(), IsFalse)

// Restore KeyOnly to false
txn3.SetOption(kv.KeyOnly, false)
scan, err = txn3.Seek(encodeKey(s.prefix, ""))
c.Assert(err, IsNil)

for i := 0; i < rowNum; i++ {
k := scan.Key()
c.Assert([]byte(k), BytesEquals, encodeKey(s.prefix, s08d("key", i)))
v := scan.Value()
c.Assert(v, BytesEquals, valueBytes(i))
// Because newScan return first item without calling scan.Next() just like go-hbase,
// for-loop count will decrease 1.
if i < rowNum-1 {
scan.Next()
}
}
scan.Next()
c.Assert(scan.Valid(), IsFalse)
}
}
1 change: 1 addition & 0 deletions store/tikv/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type tikvSnapshot struct {
priority pb.CommandPri
notFillCache bool
syncLog bool
keyOnly bool
vars *kv.Variables
}

Expand Down
2 changes: 2 additions & 0 deletions store/tikv/txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ func (txn *tikvTxn) SetOption(opt kv.Option, val interface{}) {
txn.snapshot.notFillCache = val.(bool)
case kv.SyncLog:
txn.snapshot.syncLog = val.(bool)
case kv.KeyOnly:
txn.snapshot.keyOnly = val.(bool)
}
}

Expand Down