github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/store/tikv/coprocessor.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 "bytes" 18 "io" 19 "io/ioutil" 20 "sync" 21 22 "github.com/insionng/yougam/libraries/golang/protobuf/proto" 23 "github.com/insionng/yougam/libraries/juju/errors" 24 "github.com/insionng/yougam/libraries/ngaut/log" 25 "github.com/insionng/yougam/libraries/pingcap/kvproto/pkg/coprocessor" 26 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 27 "github.com/insionng/yougam/libraries/pingcap/tidb/terror" 28 "github.com/insionng/yougam/libraries/pingcap/tipb/go-tipb" 29 ) 30 31 // CopClient is coprocessor client. 32 type CopClient struct { 33 store *tikvStore 34 } 35 36 // SupportRequestType checks whether reqType is supported. 37 func (c *CopClient) SupportRequestType(reqType, subType int64) bool { 38 switch reqType { 39 case kv.ReqTypeSelect: 40 return supportExpr(tipb.ExprType(subType)) 41 case kv.ReqTypeIndex: 42 switch subType { 43 case kv.ReqSubTypeDesc, kv.ReqSubTypeBasic: 44 return true 45 } 46 } 47 return false 48 } 49 50 func supportExpr(exprType tipb.ExprType) bool { 51 switch exprType { 52 case tipb.ExprType_Null, tipb.ExprType_Int64, tipb.ExprType_Uint64, tipb.ExprType_String, tipb.ExprType_Bytes, 53 tipb.ExprType_MysqlDuration, tipb.ExprType_MysqlDecimal, 54 tipb.ExprType_ColumnRef, 55 tipb.ExprType_And, tipb.ExprType_Or, 56 tipb.ExprType_LT, tipb.ExprType_LE, tipb.ExprType_EQ, tipb.ExprType_NE, 57 tipb.ExprType_GE, tipb.ExprType_GT, tipb.ExprType_NullEQ, 58 tipb.ExprType_In, tipb.ExprType_ValueList, 59 tipb.ExprType_Like, tipb.ExprType_Not: 60 return true 61 case kv.ReqSubTypeDesc: 62 return true 63 default: 64 return false 65 } 66 } 67 68 // Send builds the request and gets the coprocessor iterator response. 69 func (c *CopClient) Send(req *kv.Request) kv.Response { 70 tasks, err := buildCopTasks(c.store.regionCache, req.KeyRanges, req.Desc) 71 if err != nil { 72 return copErrorResponse{err} 73 } 74 it := &copIterator{ 75 store: c.store, 76 req: req, 77 tasks: tasks, 78 concurrency: req.Concurrency, 79 } 80 if it.concurrency > len(tasks) { 81 it.concurrency = len(tasks) 82 } 83 if it.concurrency < 1 { 84 // Make sure that there is at least one worker. 85 it.concurrency = 1 86 } 87 if !it.req.KeepOrder { 88 it.respChan = make(chan *coprocessor.Response, it.concurrency) 89 } 90 it.errChan = make(chan error, 1) 91 if len(it.tasks) == 0 { 92 it.Close() 93 } 94 it.run() 95 return it 96 } 97 98 const ( 99 taskNew int = iota 100 taskRunning 101 taskDone 102 ) 103 104 // copTask contains a related Region and KeyRange for a kv.Request. 105 type copTask struct { 106 region *Region 107 ranges []kv.KeyRange 108 109 status int 110 idx int // Index of task in the tasks slice. 111 respChan chan *coprocessor.Response 112 } 113 114 func (t *copTask) pbRanges() []*coprocessor.KeyRange { 115 ranges := make([]*coprocessor.KeyRange, 0, len(t.ranges)) 116 for _, r := range t.ranges { 117 ranges = append(ranges, &coprocessor.KeyRange{ 118 Start: r.StartKey, 119 End: r.EndKey, 120 }) 121 } 122 return ranges 123 } 124 125 func buildCopTasks(cache *RegionCache, ranges []kv.KeyRange, desc bool) ([]*copTask, error) { 126 var tasks []*copTask 127 for _, r := range ranges { 128 var err error 129 if tasks, err = appendTask(tasks, cache, r); err != nil { 130 return nil, errors.Trace(err) 131 } 132 } 133 if desc { 134 reverseTasks(tasks) 135 } 136 return tasks, nil 137 } 138 139 func reverseTasks(tasks []*copTask) { 140 for i := 0; i < len(tasks)/2; i++ { 141 j := len(tasks) - i - 1 142 tasks[i], tasks[j] = tasks[j], tasks[i] 143 tasks[i].idx, tasks[j].idx = tasks[j].idx, tasks[i].idx 144 } 145 } 146 147 func appendTask(tasks []*copTask, cache *RegionCache, r kv.KeyRange) ([]*copTask, error) { 148 var last *copTask 149 if len(tasks) > 0 { 150 last = tasks[len(tasks)-1] 151 } 152 // Ensure `r` (or part of `r`) is inside `last`, create a task if need. 153 if last == nil || !last.region.Contains(r.StartKey) { 154 region, err := cache.GetRegion(r.StartKey) 155 if err != nil { 156 return nil, errors.Trace(err) 157 } 158 last = &copTask{ 159 idx: len(tasks), 160 region: region, 161 status: taskNew, 162 } 163 last.respChan = make(chan *coprocessor.Response, 1) 164 tasks = append(tasks, last) 165 } 166 if last.region.Contains(r.EndKey) || bytes.Equal(last.region.EndKey(), r.EndKey) { 167 // The whole range is inside last task. 168 last.ranges = append(last.ranges, r) 169 } else { 170 // Part of r is not in the range of last task. 171 last.ranges = append(last.ranges, kv.KeyRange{ 172 StartKey: r.StartKey, 173 EndKey: last.region.EndKey(), 174 }) 175 remain := kv.KeyRange{ 176 StartKey: last.region.EndKey(), 177 EndKey: r.EndKey, 178 } 179 return appendTask(tasks, cache, remain) 180 } 181 return tasks, nil 182 } 183 184 type copIterator struct { 185 store *tikvStore 186 req *kv.Request 187 tasks []*copTask 188 189 mu sync.RWMutex 190 respGot int 191 concurrency int 192 respChan chan *coprocessor.Response 193 errChan chan error 194 finished bool 195 } 196 197 // Pick the next new copTask and send request to tikv-server. 198 func (it *copIterator) work() { 199 for { 200 it.mu.Lock() 201 if it.finished { 202 it.mu.Lock() 203 break 204 } 205 // Find the next task to send. 206 var task *copTask 207 for _, t := range it.tasks { 208 if t.status == taskNew { 209 task = t 210 break 211 } 212 } 213 if task == nil { 214 it.mu.Unlock() 215 break 216 } 217 task.status = taskRunning 218 it.mu.Unlock() 219 resp, err := it.handleTask(task) 220 if err != nil { 221 it.errChan <- err 222 break 223 } 224 if !it.req.KeepOrder { 225 it.respChan <- resp 226 } else { 227 task.respChan <- resp 228 } 229 } 230 } 231 232 func (it *copIterator) run() { 233 // Start it.concurrency number of workers to handle cop requests. 234 for i := 0; i < it.concurrency; i++ { 235 go it.work() 236 } 237 } 238 239 // Return next coprocessor result. 240 func (it *copIterator) Next() (io.ReadCloser, error) { 241 if it.finished { 242 return nil, nil 243 } 244 var ( 245 resp *coprocessor.Response 246 err error 247 ) 248 // If data order matters, response should be returned in the same order as copTask slice. 249 // Otherwise all responses are returned from a single channel. 250 if !it.req.KeepOrder { 251 // Get next fetched resp from chan 252 select { 253 case resp = <-it.respChan: 254 case err = <-it.errChan: 255 } 256 } else { 257 var task *copTask 258 it.mu.Lock() 259 for _, t := range it.tasks { 260 if t.status != taskDone { 261 task = t 262 break 263 } 264 } 265 it.mu.Unlock() 266 if task == nil { 267 it.Close() 268 return nil, nil 269 } 270 select { 271 case resp = <-task.respChan: 272 case err = <-it.errChan: 273 } 274 it.mu.Lock() 275 task.status = taskDone 276 it.mu.Unlock() 277 } 278 if err != nil { 279 it.Close() 280 return nil, err 281 } 282 it.mu.Lock() 283 defer it.mu.Unlock() 284 it.respGot++ 285 if it.respGot == len(it.tasks) { 286 it.Close() 287 } 288 return ioutil.NopCloser(bytes.NewBuffer(resp.Data)), nil 289 } 290 291 // Handle single copTask. 292 func (it *copIterator) handleTask(task *copTask) (*coprocessor.Response, error) { 293 var backoffErr error 294 for backoff := rpcBackoff(); backoffErr == nil; backoffErr = backoff() { 295 client, err := it.store.getClient(task.region.GetAddress()) 296 if err != nil { 297 return nil, errors.Trace(err) 298 } 299 req := &coprocessor.Request{ 300 Context: task.region.GetContext(), 301 Tp: proto.Int64(it.req.Tp), 302 Data: it.req.Data, 303 Ranges: task.pbRanges(), 304 } 305 resp, err := client.SendCopReq(req) 306 if err != nil { 307 it.store.regionCache.NextPeer(task.region.VerID()) 308 err1 := it.rebuildCurrentTask(task) 309 if err1 != nil { 310 return nil, errors.Trace(err1) 311 } 312 log.Warnf("send coprocessor request error: %v, try next peer later", err) 313 continue 314 } 315 if e := resp.GetRegionError(); e != nil { 316 if notLeader := e.GetNotLeader(); notLeader != nil { 317 it.store.regionCache.UpdateLeader(task.region.VerID(), notLeader.GetLeader().GetId()) 318 } else { 319 it.store.regionCache.DropRegion(task.region.VerID()) 320 } 321 err = it.rebuildCurrentTask(task) 322 if err != nil { 323 return nil, errors.Trace(err) 324 } 325 log.Warnf("coprocessor region error: %v, retry later", e) 326 continue 327 } 328 if e := resp.GetLocked(); e != nil { 329 lock := newLock(it.store, e.GetPrimaryLock(), e.GetLockVersion(), e.GetKey(), e.GetLockVersion()) 330 _, lockErr := lock.cleanup() 331 if lockErr == nil || terror.ErrorEqual(lockErr, errInnerRetryable) { 332 continue 333 } 334 log.Warnf("cleanup lock error: %v", lockErr) 335 return nil, errors.Trace(lockErr) 336 } 337 if e := resp.GetOtherError(); e != "" { 338 err = errors.Errorf("other error: %s", e) 339 log.Warnf("coprocessor err: %v", err) 340 return nil, errors.Trace(err) 341 } 342 return resp, nil 343 } 344 return nil, errors.Trace(backoffErr) 345 } 346 347 // Rebuild current task. It may be split into multiple tasks (in region split scenario). 348 func (it *copIterator) rebuildCurrentTask(task *copTask) error { 349 newTasks, err := buildCopTasks(it.store.regionCache, task.ranges, it.req.Desc) 350 if err != nil { 351 return errors.Trace(err) 352 } 353 if len(newTasks) == 0 { 354 // TODO: check this, this should never happend. 355 return nil 356 } 357 it.mu.Lock() 358 defer it.mu.Unlock() 359 // We should put the original task back to the original place in the task list. 360 // So the tasks can be handled in order. 361 t := newTasks[0] 362 task.region = t.region 363 task.ranges = t.ranges 364 close(t.respChan) 365 newTasks[0] = task 366 it.tasks = append(it.tasks[:task.idx], append(newTasks, it.tasks[task.idx+1:]...)...) 367 // Update index. 368 for i := task.idx; i < len(it.tasks); i++ { 369 it.tasks[i].idx = i 370 } 371 return nil 372 } 373 374 func (it *copIterator) Close() error { 375 it.finished = true 376 return nil 377 } 378 379 // copErrorResponse returns error when calling Next() 380 type copErrorResponse struct{ error } 381 382 func (it copErrorResponse) Next() (io.ReadCloser, error) { 383 return nil, it.error 384 } 385 386 func (it copErrorResponse) Close() error { 387 return nil 388 }