github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/store/tikv/scan.go (about) 1 // Copyright 2016 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package tikv 15 16 import ( 17 "github.com/insionng/yougam/libraries/golang/protobuf/proto" 18 "github.com/insionng/yougam/libraries/juju/errors" 19 "github.com/insionng/yougam/libraries/ngaut/log" 20 pb "github.com/insionng/yougam/libraries/pingcap/kvproto/pkg/kvrpcpb" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/terror" 23 ) 24 25 // Scanner support tikv scan 26 type Scanner struct { 27 snapshot *tikvSnapshot 28 batchSize int 29 valid bool 30 cache []*pb.KvPair 31 idx int 32 nextStartKey []byte 33 eof bool 34 } 35 36 func newScanner(snapshot *tikvSnapshot, startKey []byte, batchSize int) (*Scanner, error) { 37 // It must be > 1. Otherwise scanner won't skipFirst. 38 if batchSize <= 1 { 39 batchSize = scanBatchSize 40 } 41 scanner := &Scanner{ 42 snapshot: snapshot, 43 batchSize: batchSize, 44 valid: true, 45 nextStartKey: startKey, 46 } 47 err := scanner.Next() 48 if kv.IsErrNotFound(err) { 49 return scanner, nil 50 } 51 return scanner, errors.Trace(err) 52 } 53 54 // Valid return valid. 55 func (s *Scanner) Valid() bool { 56 return s.valid 57 } 58 59 // Key return key. 60 func (s *Scanner) Key() kv.Key { 61 if s.valid { 62 return s.cache[s.idx].Key 63 } 64 return nil 65 } 66 67 // Value return value. 68 func (s *Scanner) Value() []byte { 69 if s.valid { 70 return s.cache[s.idx].Value 71 } 72 return nil 73 } 74 75 // Next return next element. 76 func (s *Scanner) Next() error { 77 if !s.valid { 78 return errors.New("scanner iterator is invalid") 79 } 80 for { 81 s.idx++ 82 if s.idx >= len(s.cache) { 83 if s.eof { 84 s.Close() 85 return kv.ErrNotExist 86 } 87 err := s.getData() 88 if err != nil { 89 s.Close() 90 return errors.Trace(err) 91 } 92 if s.idx >= len(s.cache) { 93 continue 94 } 95 } 96 if err := s.resolveCurrentLock(); err != nil { 97 s.Close() 98 return errors.Trace(err) 99 } 100 if len(s.Value()) == 0 { 101 // nil stands for NotExist, go to next KV pair. 102 continue 103 } 104 return nil 105 } 106 } 107 108 // Close close iterator. 109 func (s *Scanner) Close() { 110 s.valid = false 111 } 112 113 func (s *Scanner) startTS() uint64 { 114 return s.snapshot.version.Ver 115 } 116 117 func (s *Scanner) resolveCurrentLock() error { 118 current := s.cache[s.idx] 119 if current.GetError() == nil { 120 return nil 121 } 122 var backoffErr error 123 for backoff := txnLockBackoff(); backoffErr == nil; backoffErr = backoff() { 124 val, err := s.snapshot.handleKeyError(current.GetError()) 125 if err != nil { 126 if terror.ErrorEqual(err, errInnerRetryable) { 127 continue 128 } 129 return errors.Trace(err) 130 } 131 current.Error = nil 132 current.Value = val 133 return nil 134 } 135 return errors.Annotate(backoffErr, txnRetryableMark) 136 } 137 138 func (s *Scanner) getData() error { 139 log.Debugf("txn getData nextStartKey[%q], txn %d", s.nextStartKey, s.startTS()) 140 141 var backoffErr error 142 for backoff := regionMissBackoff(); backoffErr == nil; backoffErr = backoff() { 143 region, err := s.snapshot.store.regionCache.GetRegion(s.nextStartKey) 144 if err != nil { 145 return errors.Trace(err) 146 } 147 req := &pb.Request{ 148 Type: pb.MessageType_CmdScan.Enum(), 149 CmdScanReq: &pb.CmdScanRequest{ 150 StartKey: []byte(s.nextStartKey), 151 Limit: proto.Uint32(uint32(s.batchSize)), 152 Version: proto.Uint64(s.startTS()), 153 }, 154 } 155 resp, err := s.snapshot.store.SendKVReq(req, region.VerID()) 156 if err != nil { 157 return errors.Trace(err) 158 } 159 if regionErr := resp.GetRegionError(); regionErr != nil { 160 log.Warnf("scanner getData failed: %s", regionErr) 161 continue 162 } 163 cmdScanResp := resp.GetCmdScanResp() 164 if cmdScanResp == nil { 165 return errors.Trace(errBodyMissing) 166 } 167 168 kvPairs := cmdScanResp.Pairs 169 // Check if kvPair contains error, it should be a Lock. 170 for _, pair := range kvPairs { 171 if keyErr := pair.GetError(); keyErr != nil { 172 lock, err := extractLockInfoFromKeyErr(keyErr) 173 if err != nil { 174 return errors.Trace(err) 175 } 176 pair.Key = lock.Key 177 } 178 } 179 180 s.cache, s.idx = kvPairs, 0 181 if len(kvPairs) < s.batchSize { 182 // No more data in current Region. Next getData() starts 183 // from current Region's endKey. 184 s.nextStartKey = region.EndKey() 185 if len(region.EndKey()) == 0 { 186 // Current Region is the last one. 187 s.eof = true 188 } 189 return nil 190 } 191 // next getData() starts from the last key in kvPairs (but skip 192 // it by appending a '\x00' to the key). Note that next getData() 193 // may get an empty response if the Region in fact does not have 194 // more data. 195 lastKey := kvPairs[len(kvPairs)-1].GetKey() 196 s.nextStartKey = kv.Key(lastKey).Next() 197 return nil 198 } 199 return errors.Annotate(backoffErr, txnRetryableMark) 200 }