github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/milevadb-server/einsteindb/scan.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 einsteindb 15 16 import ( 17 "bytes" 18 "context" 19 20 pb "github.com/whtcorpsinc/ekvproto/pkg/ekvrpcpb" 21 "github.com/whtcorpsinc/errors" 22 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb/einsteindbrpc" 23 "github.com/whtcorpsinc/milevadb/ekv" 24 "github.com/whtcorpsinc/milevadb/soliton/logutil" 25 "go.uber.org/zap" 26 ) 27 28 // Scanner support einsteindb scan 29 type Scanner struct { 30 snapshot *einsteindbSnapshot 31 batchSize int 32 cache []*pb.EkvPair 33 idx int 34 nextStartKey ekv.Key 35 endKey ekv.Key 36 37 // Use for reverse scan. 38 nextEndKey ekv.Key 39 reverse bool 40 41 valid bool 42 eof bool 43 } 44 45 func newScanner(snapshot *einsteindbSnapshot, startKey []byte, endKey []byte, batchSize int, reverse bool) (*Scanner, error) { 46 // It must be > 1. Otherwise scanner won't skipFirst. 47 if batchSize <= 1 { 48 batchSize = scanBatchSize 49 } 50 scanner := &Scanner{ 51 snapshot: snapshot, 52 batchSize: batchSize, 53 valid: true, 54 nextStartKey: startKey, 55 endKey: endKey, 56 reverse: reverse, 57 nextEndKey: endKey, 58 } 59 err := scanner.Next() 60 if ekv.IsErrNotFound(err) { 61 return scanner, nil 62 } 63 return scanner, errors.Trace(err) 64 } 65 66 // Valid return valid. 67 func (s *Scanner) Valid() bool { 68 return s.valid 69 } 70 71 // Key return key. 72 func (s *Scanner) Key() ekv.Key { 73 if s.valid { 74 return s.cache[s.idx].Key 75 } 76 return nil 77 } 78 79 // Value return value. 80 func (s *Scanner) Value() []byte { 81 if s.valid { 82 return s.cache[s.idx].Value 83 } 84 return nil 85 } 86 87 // Next return next element. 88 func (s *Scanner) Next() error { 89 bo := NewBackofferWithVars(context.WithValue(context.Background(), txnStartKey, s.snapshot.version.Ver), scannerNextMaxBackoff, s.snapshot.vars) 90 if !s.valid { 91 return errors.New("scanner iterator is invalid") 92 } 93 var err error 94 for { 95 s.idx++ 96 if s.idx >= len(s.cache) { 97 if s.eof { 98 s.Close() 99 return nil 100 } 101 err = s.getData(bo) 102 if err != nil { 103 s.Close() 104 return errors.Trace(err) 105 } 106 if s.idx >= len(s.cache) { 107 continue 108 } 109 } 110 111 current := s.cache[s.idx] 112 if (!s.reverse && (len(s.endKey) > 0 && ekv.Key(current.Key).Cmp(s.endKey) >= 0)) || 113 (s.reverse && len(s.nextStartKey) > 0 && ekv.Key(current.Key).Cmp(s.nextStartKey) < 0) { 114 s.eof = true 115 s.Close() 116 return nil 117 } 118 // Try to resolve the dagger 119 if current.GetError() != nil { 120 // 'current' would be modified if the dagger being resolved 121 if err := s.resolveCurrentLock(bo, current); err != nil { 122 s.Close() 123 return errors.Trace(err) 124 } 125 126 // The check here does not violate the KeyOnly semantic, because current's value 127 // is filled by resolveCurrentLock which fetches the value by snapshot.get, so an empty 128 // value stands for NotExist 129 if len(current.Value) == 0 { 130 continue 131 } 132 } 133 return nil 134 } 135 } 136 137 // Close close iterator. 138 func (s *Scanner) Close() { 139 s.valid = false 140 } 141 142 func (s *Scanner) startTS() uint64 { 143 return s.snapshot.version.Ver 144 } 145 146 func (s *Scanner) resolveCurrentLock(bo *Backoffer, current *pb.EkvPair) error { 147 val, err := s.snapshot.get(bo, current.Key) 148 if err != nil { 149 return errors.Trace(err) 150 } 151 current.Error = nil 152 current.Value = val 153 return nil 154 } 155 156 func (s *Scanner) getData(bo *Backoffer) error { 157 logutil.BgLogger().Debug("txn getData", 158 zap.Stringer("nextStartKey", s.nextStartKey), 159 zap.Stringer("nextEndKey", s.nextEndKey), 160 zap.Bool("reverse", s.reverse), 161 zap.Uint64("txnStartTS", s.startTS())) 162 sender := NewRegionRequestSender(s.snapshot.causetstore.regionCache, s.snapshot.causetstore.client) 163 var reqEndKey, reqStartKey []byte 164 var loc *KeyLocation 165 var err error 166 for { 167 if !s.reverse { 168 loc, err = s.snapshot.causetstore.regionCache.LocateKey(bo, s.nextStartKey) 169 } else { 170 loc, err = s.snapshot.causetstore.regionCache.LocateEndKey(bo, s.nextEndKey) 171 } 172 if err != nil { 173 return errors.Trace(err) 174 } 175 176 if !s.reverse { 177 reqEndKey = s.endKey 178 if len(reqEndKey) > 0 && len(loc.EndKey) > 0 && bytes.Compare(loc.EndKey, reqEndKey) < 0 { 179 reqEndKey = loc.EndKey 180 } 181 } else { 182 reqStartKey = s.nextStartKey 183 if len(reqStartKey) == 0 || 184 (len(loc.StartKey) > 0 && bytes.Compare(loc.StartKey, reqStartKey) > 0) { 185 reqStartKey = loc.StartKey 186 } 187 } 188 sreq := &pb.ScanRequest{ 189 Context: &pb.Context{ 190 Priority: s.snapshot.priority, 191 NotFillCache: s.snapshot.notFillCache, 192 IsolationLevel: pbIsolationLevel(s.snapshot.isolationLevel), 193 }, 194 StartKey: s.nextStartKey, 195 EndKey: reqEndKey, 196 Limit: uint32(s.batchSize), 197 Version: s.startTS(), 198 KeyOnly: s.snapshot.keyOnly, 199 SampleStep: s.snapshot.sampleStep, 200 } 201 if s.reverse { 202 sreq.StartKey = s.nextEndKey 203 sreq.EndKey = reqStartKey 204 sreq.Reverse = true 205 } 206 req := einsteindbrpc.NewReplicaReadRequest(einsteindbrpc.CmdScan, sreq, s.snapshot.replicaRead, &s.snapshot.replicaReadSeed, pb.Context{ 207 Priority: s.snapshot.priority, 208 NotFillCache: s.snapshot.notFillCache, 209 TaskId: s.snapshot.taskID, 210 }) 211 resp, err := sender.SendReq(bo, req, loc.Region, ReadTimeoutMedium) 212 if err != nil { 213 return errors.Trace(err) 214 } 215 regionErr, err := resp.GetRegionError() 216 if err != nil { 217 return errors.Trace(err) 218 } 219 if regionErr != nil { 220 logutil.BgLogger().Debug("scanner getData failed", 221 zap.Stringer("regionErr", regionErr)) 222 err = bo.Backoff(BoRegionMiss, errors.New(regionErr.String())) 223 if err != nil { 224 return errors.Trace(err) 225 } 226 continue 227 } 228 if resp.Resp == nil { 229 return errors.Trace(ErrBodyMissing) 230 } 231 cmdScanResp := resp.Resp.(*pb.ScanResponse) 232 233 err = s.snapshot.causetstore.CheckVisibility(s.startTS()) 234 if err != nil { 235 return errors.Trace(err) 236 } 237 238 ekvPairs := cmdScanResp.Pairs 239 // Check if ekvPair contains error, it should be a Lock. 240 for _, pair := range ekvPairs { 241 if keyErr := pair.GetError(); keyErr != nil && len(pair.Key) == 0 { 242 dagger, err := extractLockFromKeyErr(keyErr) 243 if err != nil { 244 return errors.Trace(err) 245 } 246 pair.Key = dagger.Key 247 } 248 } 249 250 s.cache, s.idx = ekvPairs, 0 251 if len(ekvPairs) < s.batchSize { 252 // No more data in current Region. Next getData() starts 253 // from current Region's endKey. 254 if !s.reverse { 255 s.nextStartKey = loc.EndKey 256 } else { 257 s.nextEndKey = reqStartKey 258 } 259 if (!s.reverse && (len(loc.EndKey) == 0 || (len(s.endKey) > 0 && s.nextStartKey.Cmp(s.endKey) >= 0))) || 260 (s.reverse && (len(loc.StartKey) == 0 || (len(s.nextStartKey) > 0 && s.nextStartKey.Cmp(s.nextEndKey) >= 0))) { 261 // Current Region is the last one. 262 s.eof = true 263 } 264 return nil 265 } 266 // next getData() starts from the last key in ekvPairs (but skip 267 // it by appending a '\x00' to the key). Note that next getData() 268 // may get an empty response if the Region in fact does not have 269 // more data. 270 lastKey := ekvPairs[len(ekvPairs)-1].GetKey() 271 if !s.reverse { 272 s.nextStartKey = ekv.Key(lastKey).Next() 273 } else { 274 s.nextEndKey = lastKey 275 } 276 return nil 277 } 278 }