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  }