github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/store/tikv/snapshot.go (about) 1 // Copyright 2015 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 var ( 26 _ kv.Snapshot = (*tikvSnapshot)(nil) 27 ) 28 29 const ( 30 scanBatchSize = 100 31 maxGetCount = 3 32 batchGetSize = 100 33 ) 34 35 // tikvSnapshot implements MvccSnapshot interface. 36 type tikvSnapshot struct { 37 store *tikvStore 38 version kv.Version 39 } 40 41 // newTiKVSnapshot creates a snapshot of an TiKV store. 42 func newTiKVSnapshot(store *tikvStore, ver kv.Version) *tikvSnapshot { 43 return &tikvSnapshot{ 44 store: store, 45 version: ver, 46 } 47 } 48 49 // makeBatchGetReqs splits each key into corresponding region. 50 func (s *tikvSnapshot) makeBatchGetReqs(keys []kv.Key) (map[RegionVerID]*batchGetRegion, error) { 51 startTS := s.version.Ver 52 multiBatchGet := map[RegionVerID]*batchGetRegion{} 53 for _, k := range keys { 54 region, err := s.store.regionCache.GetRegion(k) 55 if err != nil { 56 return nil, errors.Trace(err) 57 } 58 regionID := region.VerID() 59 singleBatchGet, ok := multiBatchGet[regionID] 60 if !ok { 61 singleBatchGet = &batchGetRegion{ 62 CmdBatchGetRequest: &pb.CmdBatchGetRequest{ 63 Version: proto.Uint64(startTS), 64 }, 65 region: regionID, 66 } 67 multiBatchGet[regionID] = singleBatchGet 68 } 69 cmdBatchGetReq := singleBatchGet.CmdBatchGetRequest 70 cmdBatchGetReq.Keys = append(cmdBatchGetReq.Keys, k) 71 } 72 return multiBatchGet, nil 73 } 74 75 // doBatchGet sends BatchGet RPC request. If any key is locked, use tikvSnapshot.Get() to retry. 76 func (s *tikvSnapshot) doBatchGet(singleBatchGet *batchGetRegion) (map[string][]byte, error) { 77 cmdBatchGetReq := singleBatchGet.CmdBatchGetRequest 78 keys := cmdBatchGetReq.GetKeys() 79 if len(keys) == 0 { 80 return nil, nil 81 } 82 req := &pb.Request{ 83 Type: pb.MessageType_CmdBatchGet.Enum(), 84 CmdBatchGetReq: cmdBatchGetReq, 85 } 86 resp, err := s.store.SendKVReq(req, singleBatchGet.region) 87 if err != nil { 88 return nil, errors.Trace(err) 89 } 90 if regionErr := resp.GetRegionError(); regionErr != nil { 91 //TODO: retry internally 92 return nil, errors.Annotate(errors.New(regionErr.String()), txnRetryableMark) 93 } 94 cmdBatchGetResp := resp.GetCmdBatchGetResp() 95 if cmdBatchGetResp == nil { 96 return nil, errors.Trace(errBodyMissing) 97 } 98 pairs := cmdBatchGetResp.GetPairs() 99 m := make(map[string][]byte, len(pairs)) 100 for _, pair := range pairs { 101 keyErr := pair.GetError() 102 if keyErr == nil { 103 if val := pair.GetValue(); len(val) > 0 { 104 m[string(pair.GetKey())] = val 105 } 106 continue 107 } 108 lockInfo, err := extractLockInfoFromKeyErr(keyErr) 109 if err != nil { 110 return nil, errors.Trace(err) 111 } 112 val, err := s.Get(lockInfo.GetKey()) 113 if err != nil { 114 if terror.ErrorEqual(err, kv.ErrNotExist) { 115 continue 116 } 117 return nil, errors.Trace(err) 118 } 119 m[string(lockInfo.GetKey())] = val 120 } 121 return m, nil 122 } 123 124 // BatchGet gets all the keys' value from kv-server and returns a map contains key/value pairs. 125 // The map will not contain nonexistent keys. 126 func (s *tikvSnapshot) BatchGet(keys []kv.Key) (map[string][]byte, error) { 127 m := make(map[string][]byte, len(keys)) 128 129 multiBatchGet, err := s.makeBatchGetReqs(keys) 130 if err != nil { 131 return nil, errors.Trace(err) 132 } 133 for _, singleBatchGet := range multiBatchGet { 134 keys := singleBatchGet.GetKeys() 135 for startIdx := 0; startIdx < len(keys); startIdx += batchGetSize { 136 endIdx := startIdx + batchGetSize 137 if endIdx > len(keys) { 138 endIdx = len(keys) 139 } 140 newSingleBatchGet := &batchGetRegion{ 141 CmdBatchGetRequest: &pb.CmdBatchGetRequest{ 142 Keys: keys[startIdx:endIdx], 143 Version: proto.Uint64(singleBatchGet.GetVersion()), 144 }, 145 region: singleBatchGet.region, 146 } 147 res, err := s.doBatchGet(newSingleBatchGet) 148 if err != nil { 149 return nil, errors.Trace(err) 150 } 151 m, err = mergeResult(m, res) 152 if err != nil { 153 return nil, errors.Trace(err) 154 } 155 } 156 } 157 158 return m, nil 159 } 160 161 // Get gets the value for key k from snapshot. 162 func (s *tikvSnapshot) Get(k kv.Key) ([]byte, error) { 163 req := &pb.Request{ 164 Type: pb.MessageType_CmdGet.Enum(), 165 CmdGetReq: &pb.CmdGetRequest{ 166 Key: k, 167 Version: proto.Uint64(s.version.Ver), 168 }, 169 } 170 171 var ( 172 backoffErr error 173 regionBackoff = regionMissBackoff() 174 txnBackoff = txnLockBackoff() 175 ) 176 for backoffErr == nil { 177 region, err := s.store.regionCache.GetRegion(k) 178 if err != nil { 179 return nil, errors.Trace(err) 180 } 181 resp, err := s.store.SendKVReq(req, region.VerID()) 182 if err != nil { 183 return nil, errors.Trace(err) 184 } 185 if regionErr := resp.GetRegionError(); regionErr != nil { 186 backoffErr = regionBackoff() 187 continue 188 } 189 cmdGetResp := resp.GetCmdGetResp() 190 if cmdGetResp == nil { 191 return nil, errors.Trace(errBodyMissing) 192 } 193 val := cmdGetResp.GetValue() 194 if keyErr := cmdGetResp.GetError(); keyErr != nil { 195 val, err = s.handleKeyError(keyErr) 196 if err != nil { 197 if terror.ErrorEqual(err, errInnerRetryable) { 198 backoffErr = txnBackoff() 199 continue 200 } 201 return nil, errors.Trace(err) 202 } 203 } 204 if len(val) == 0 { 205 return nil, kv.ErrNotExist 206 } 207 return val, nil 208 } 209 return nil, errors.Annotate(backoffErr, txnRetryableMark) 210 } 211 212 // Seek return a list of key-value pair after `k`. 213 func (s *tikvSnapshot) Seek(k kv.Key) (kv.Iterator, error) { 214 scanner, err := newScanner(s, k, scanBatchSize) 215 return scanner, errors.Trace(err) 216 } 217 218 // SeekReverse creates a reversed Iterator positioned on the first entry which key is less than k. 219 func (s *tikvSnapshot) SeekReverse(k kv.Key) (kv.Iterator, error) { 220 return nil, kv.ErrNotImplemented 221 } 222 223 // Release unimplement. 224 func (s *tikvSnapshot) Release() { 225 } 226 227 func extractLockInfoFromKeyErr(keyErr *pb.KeyError) (*pb.LockInfo, error) { 228 if locked := keyErr.GetLocked(); locked != nil { 229 return locked, nil 230 } 231 if keyErr.Retryable != nil { 232 err := errors.Errorf("tikv restarts txn: %s", keyErr.GetRetryable()) 233 log.Warn(err) 234 return nil, errors.Annotate(err, txnRetryableMark) 235 } 236 if keyErr.Abort != nil { 237 err := errors.Errorf("tikv aborts txn: %s", keyErr.GetAbort()) 238 log.Warn(err) 239 return nil, errors.Trace(err) 240 } 241 return nil, errors.Errorf("unexpected KeyError: %s", keyErr.String()) 242 } 243 244 // handleKeyError tries to resolve locks then retry to get value. 245 func (s *tikvSnapshot) handleKeyError(keyErr *pb.KeyError) ([]byte, error) { 246 lockInfo, err := extractLockInfoFromKeyErr(keyErr) 247 if err != nil { 248 return nil, errors.Trace(err) 249 } 250 lock := newLock(s.store, lockInfo.GetPrimaryLock(), lockInfo.GetLockVersion(), lockInfo.GetKey(), s.version.Ver) 251 val, err := lock.cleanup() 252 if err != nil { 253 return nil, errors.Trace(err) 254 } 255 return val, nil 256 } 257 258 // mergeResult Merge d2 into d1. If d1 and d2 are overlap, it returns error. 259 func mergeResult(d1, d2 map[string][]byte) (map[string][]byte, error) { 260 if d1 == nil { 261 d1 = make(map[string][]byte) 262 } 263 for k2, v2 := range d2 { 264 if v1, ok := d1[k2]; ok { 265 // Because compare []byte takes too much time, 266 // if conflict return error directly even their values are same. 267 return nil, errors.Errorf("add dict conflict key[%s] v1[%q] v2[%q]", 268 k2, v1, v2) 269 } 270 d1[k2] = v2 271 } 272 return d1, nil 273 } 274 275 type batchGetRegion struct { 276 *pb.CmdBatchGetRequest 277 region RegionVerID 278 }