github.com/KinWaiYuen/client-go/v2@v2.5.4/txnkv/txnsnapshot/scan.go (about) 1 // Copyright 2021 TiKV Authors 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // NOTE: The code in this file is based on code from the 16 // TiDB project, licensed under the Apache License v 2.0 17 // 18 // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/scan.go 19 // 20 21 // Copyright 2016 PingCAP, Inc. 22 // 23 // Licensed under the Apache License, Version 2.0 (the "License"); 24 // you may not use this file except in compliance with the License. 25 // You may obtain a copy of the License at 26 // 27 // http://www.apache.org/licenses/LICENSE-2.0 28 // 29 // Unless required by applicable law or agreed to in writing, software 30 // distributed under the License is distributed on an "AS IS" BASIS, 31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package txnsnapshot 36 37 import ( 38 "bytes" 39 "context" 40 41 tikverr "github.com/KinWaiYuen/client-go/v2/error" 42 "github.com/KinWaiYuen/client-go/v2/internal/client" 43 "github.com/KinWaiYuen/client-go/v2/internal/locate" 44 "github.com/KinWaiYuen/client-go/v2/internal/logutil" 45 "github.com/KinWaiYuen/client-go/v2/internal/retry" 46 "github.com/KinWaiYuen/client-go/v2/kv" 47 "github.com/KinWaiYuen/client-go/v2/tikvrpc" 48 "github.com/KinWaiYuen/client-go/v2/txnkv/txnlock" 49 "github.com/pingcap/errors" 50 "github.com/pingcap/kvproto/pkg/kvrpcpb" 51 "go.uber.org/zap" 52 ) 53 54 // Scanner support tikv scan 55 type Scanner struct { 56 snapshot *KVSnapshot 57 batchSize int 58 cache []*kvrpcpb.KvPair 59 idx int 60 nextStartKey []byte 61 endKey []byte 62 63 // Use for reverse scan. 64 nextEndKey []byte 65 reverse bool 66 67 valid bool 68 eof bool 69 } 70 71 func newScanner(snapshot *KVSnapshot, startKey []byte, endKey []byte, batchSize int, reverse bool) (*Scanner, error) { 72 // It must be > 1. Otherwise scanner won't skipFirst. 73 if batchSize <= 1 { 74 batchSize = defaultScanBatchSize 75 } 76 scanner := &Scanner{ 77 snapshot: snapshot, 78 batchSize: batchSize, 79 valid: true, 80 nextStartKey: startKey, 81 endKey: endKey, 82 reverse: reverse, 83 nextEndKey: endKey, 84 } 85 err := scanner.Next() 86 if tikverr.IsErrNotFound(err) { 87 return scanner, nil 88 } 89 return scanner, errors.Trace(err) 90 } 91 92 // Valid return valid. 93 func (s *Scanner) Valid() bool { 94 return s.valid 95 } 96 97 // Key return key. 98 func (s *Scanner) Key() []byte { 99 if s.valid { 100 return s.cache[s.idx].Key 101 } 102 return nil 103 } 104 105 // Value return value. 106 func (s *Scanner) Value() []byte { 107 if s.valid { 108 return s.cache[s.idx].Value 109 } 110 return nil 111 } 112 113 const scannerNextMaxBackoff = 600000 // 10 minutes 114 115 // Next return next element. 116 func (s *Scanner) Next() error { 117 bo := retry.NewBackofferWithVars(context.WithValue(context.Background(), retry.TxnStartKey, s.snapshot.version), scannerNextMaxBackoff, s.snapshot.vars) 118 if !s.valid { 119 return errors.New("scanner iterator is invalid") 120 } 121 var err error 122 for { 123 s.idx++ 124 if s.idx >= len(s.cache) { 125 if s.eof { 126 s.Close() 127 return nil 128 } 129 err = s.getData(bo) 130 if err != nil { 131 s.Close() 132 return errors.Trace(err) 133 } 134 if s.idx >= len(s.cache) { 135 continue 136 } 137 } 138 139 current := s.cache[s.idx] 140 if (!s.reverse && (len(s.endKey) > 0 && kv.CmpKey(current.Key, s.endKey) >= 0)) || 141 (s.reverse && len(s.nextStartKey) > 0 && kv.CmpKey(current.Key, s.nextStartKey) < 0) { 142 s.eof = true 143 s.Close() 144 return nil 145 } 146 // Try to resolve the lock 147 if current.GetError() != nil { 148 // 'current' would be modified if the lock being resolved 149 if err := s.resolveCurrentLock(bo, current); err != nil { 150 s.Close() 151 return errors.Trace(err) 152 } 153 154 // The check here does not violate the KeyOnly semantic, because current's value 155 // is filled by resolveCurrentLock which fetches the value by snapshot.get, so an empty 156 // value stands for NotExist 157 if len(current.Value) == 0 { 158 continue 159 } 160 } 161 return nil 162 } 163 } 164 165 // Close close iterator. 166 func (s *Scanner) Close() { 167 s.valid = false 168 } 169 170 func (s *Scanner) startTS() uint64 { 171 return s.snapshot.version 172 } 173 174 func (s *Scanner) resolveCurrentLock(bo *retry.Backoffer, current *kvrpcpb.KvPair) error { 175 ctx := context.Background() 176 val, err := s.snapshot.get(ctx, bo, current.Key) 177 if err != nil { 178 return errors.Trace(err) 179 } 180 current.Error = nil 181 current.Value = val 182 return nil 183 } 184 185 func (s *Scanner) getData(bo *retry.Backoffer) error { 186 logutil.BgLogger().Debug("txn getData", 187 zap.String("nextStartKey", kv.StrKey(s.nextStartKey)), 188 zap.String("nextEndKey", kv.StrKey(s.nextEndKey)), 189 zap.Bool("reverse", s.reverse), 190 zap.Uint64("txnStartTS", s.startTS())) 191 sender := locate.NewRegionRequestSender(s.snapshot.store.GetRegionCache(), s.snapshot.store.GetTiKVClient()) 192 var reqEndKey, reqStartKey []byte 193 var loc *locate.KeyLocation 194 var err error 195 for { 196 if !s.reverse { 197 loc, err = s.snapshot.store.GetRegionCache().LocateKey(bo, s.nextStartKey) 198 } else { 199 loc, err = s.snapshot.store.GetRegionCache().LocateEndKey(bo, s.nextEndKey) 200 } 201 if err != nil { 202 return errors.Trace(err) 203 } 204 205 if !s.reverse { 206 reqEndKey = s.endKey 207 if len(reqEndKey) > 0 && len(loc.EndKey) > 0 && bytes.Compare(loc.EndKey, reqEndKey) < 0 { 208 reqEndKey = loc.EndKey 209 } 210 } else { 211 reqStartKey = s.nextStartKey 212 if len(reqStartKey) == 0 || 213 (len(loc.StartKey) > 0 && bytes.Compare(loc.StartKey, reqStartKey) > 0) { 214 reqStartKey = loc.StartKey 215 } 216 } 217 sreq := &kvrpcpb.ScanRequest{ 218 Context: &kvrpcpb.Context{ 219 Priority: s.snapshot.priority.ToPB(), 220 NotFillCache: s.snapshot.notFillCache, 221 IsolationLevel: s.snapshot.isolationLevel.ToPB(), 222 ResourceGroupTag: s.snapshot.resourceGroupTag, 223 }, 224 StartKey: s.nextStartKey, 225 EndKey: reqEndKey, 226 Limit: uint32(s.batchSize), 227 Version: s.startTS(), 228 KeyOnly: s.snapshot.keyOnly, 229 SampleStep: s.snapshot.sampleStep, 230 } 231 if s.reverse { 232 sreq.StartKey = s.nextEndKey 233 sreq.EndKey = reqStartKey 234 sreq.Reverse = true 235 } 236 s.snapshot.mu.RLock() 237 req := tikvrpc.NewReplicaReadRequest(tikvrpc.CmdScan, sreq, s.snapshot.mu.replicaRead, &s.snapshot.replicaReadSeed, kvrpcpb.Context{ 238 Priority: s.snapshot.priority.ToPB(), 239 NotFillCache: s.snapshot.notFillCache, 240 TaskId: s.snapshot.mu.taskID, 241 ResourceGroupTag: s.snapshot.resourceGroupTag, 242 }) 243 s.snapshot.mu.RUnlock() 244 resp, err := sender.SendReq(bo, req, loc.Region, client.ReadTimeoutMedium) 245 if err != nil { 246 return errors.Trace(err) 247 } 248 regionErr, err := resp.GetRegionError() 249 if err != nil { 250 return errors.Trace(err) 251 } 252 if regionErr != nil { 253 logutil.BgLogger().Debug("scanner getData failed", 254 zap.Stringer("regionErr", regionErr)) 255 // For other region error and the fake region error, backoff because 256 // there's something wrong. 257 // For the real EpochNotMatch error, don't backoff. 258 if regionErr.GetEpochNotMatch() == nil || locate.IsFakeRegionError(regionErr) { 259 err = bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String())) 260 if err != nil { 261 return errors.Trace(err) 262 } 263 } 264 continue 265 } 266 if resp.Resp == nil { 267 return errors.Trace(tikverr.ErrBodyMissing) 268 } 269 cmdScanResp := resp.Resp.(*kvrpcpb.ScanResponse) 270 271 err = s.snapshot.store.CheckVisibility(s.startTS()) 272 if err != nil { 273 return errors.Trace(err) 274 } 275 276 // When there is a response-level key error, the returned pairs are incomplete. 277 // We should resolve the lock first and then retry the same request. 278 if keyErr := cmdScanResp.GetError(); keyErr != nil { 279 lock, err := txnlock.ExtractLockFromKeyErr(keyErr) 280 if err != nil { 281 return errors.Trace(err) 282 } 283 msBeforeExpired, _, err := txnlock.NewLockResolver(s.snapshot.store).ResolveLocks(bo, s.snapshot.version, []*txnlock.Lock{lock}) 284 if err != nil { 285 return errors.Trace(err) 286 } 287 if msBeforeExpired > 0 { 288 err = bo.BackoffWithMaxSleepTxnLockFast(int(msBeforeExpired), errors.Errorf("key is locked during scanning")) 289 if err != nil { 290 return errors.Trace(err) 291 } 292 } 293 continue 294 } 295 296 kvPairs := cmdScanResp.Pairs 297 // Check if kvPair contains error, it should be a Lock. 298 for _, pair := range kvPairs { 299 if keyErr := pair.GetError(); keyErr != nil && len(pair.Key) == 0 { 300 lock, err := txnlock.ExtractLockFromKeyErr(keyErr) 301 if err != nil { 302 return errors.Trace(err) 303 } 304 pair.Key = lock.Key 305 } 306 } 307 308 s.cache, s.idx = kvPairs, 0 309 if len(kvPairs) < s.batchSize { 310 // No more data in current Region. Next getData() starts 311 // from current Region's endKey. 312 if !s.reverse { 313 s.nextStartKey = loc.EndKey 314 } else { 315 s.nextEndKey = reqStartKey 316 } 317 if (!s.reverse && (len(loc.EndKey) == 0 || (len(s.endKey) > 0 && kv.CmpKey(s.nextStartKey, s.endKey) >= 0))) || 318 (s.reverse && (len(loc.StartKey) == 0 || (len(s.nextStartKey) > 0 && kv.CmpKey(s.nextStartKey, s.nextEndKey) >= 0))) { 319 // Current Region is the last one. 320 s.eof = true 321 } 322 return nil 323 } 324 // next getData() starts from the last key in kvPairs (but skip 325 // it by appending a '\x00' to the key). Note that next getData() 326 // may get an empty response if the Region in fact does not have 327 // more data. 328 lastKey := kvPairs[len(kvPairs)-1].GetKey() 329 if !s.reverse { 330 s.nextStartKey = kv.NextKey(lastKey) 331 } else { 332 s.nextEndKey = lastKey 333 } 334 return nil 335 } 336 }