github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/store/tikv/lock.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  	"github.com/insionng/yougam/libraries/golang/protobuf/proto"
    18  	"github.com/insionng/yougam/libraries/juju/errors"
    19  	pb "github.com/insionng/yougam/libraries/pingcap/kvproto/pkg/kvrpcpb"
    20  )
    21  
    22  type txnLock struct {
    23  	store *tikvStore
    24  	// pl primary lock
    25  	pl  pLock
    26  	key []byte
    27  	ver uint64
    28  }
    29  
    30  func newLock(store *tikvStore, pLock []byte, lockVer uint64, key []byte, ver uint64) txnLock {
    31  	return txnLock{
    32  		store: store,
    33  		pl:    newPLock(pLock, lockVer),
    34  		key:   key,
    35  		ver:   ver,
    36  	}
    37  }
    38  
    39  // txnLockBackoff is for transaction lock retry.
    40  func txnLockBackoff() func() error {
    41  	const (
    42  		maxRetry  = 6
    43  		sleepBase = 300
    44  		sleepCap  = 3000
    45  	)
    46  	return NewBackoff(maxRetry, sleepBase, sleepCap, EqualJitter)
    47  }
    48  
    49  // locks after 3000ms is considered unusual (the client created the lock might
    50  // be dead). Other client may cleanup this kind of lock.
    51  // For locks created recently, we will do backoff and retry.
    52  const lockTTL = 3000
    53  
    54  // cleanup cleanup the lock
    55  func (l *txnLock) cleanup() ([]byte, error) {
    56  	expired, err := l.store.oracle.IsExpired(l.pl.version, lockTTL)
    57  	if err != nil {
    58  		return nil, errors.Trace(err)
    59  	}
    60  	if !expired {
    61  		return nil, errors.Trace(errInnerRetryable)
    62  	}
    63  	req := &pb.Request{
    64  		Type: pb.MessageType_CmdCleanup.Enum(),
    65  		CmdCleanupReq: &pb.CmdCleanupRequest{
    66  			Key:          l.pl.key,
    67  			StartVersion: proto.Uint64(l.pl.version),
    68  		},
    69  	}
    70  	var backoffErr error
    71  	for backoff := regionMissBackoff(); backoffErr == nil; backoffErr = backoff() {
    72  		region, err := l.store.regionCache.GetRegion(l.pl.key)
    73  		if err != nil {
    74  			return nil, errors.Trace(err)
    75  		}
    76  		resp, err := l.store.SendKVReq(req, region.VerID())
    77  		if err != nil {
    78  			return nil, errors.Trace(err)
    79  		}
    80  		if regionErr := resp.GetRegionError(); regionErr != nil {
    81  			continue
    82  		}
    83  		cmdCleanupResp := resp.GetCmdCleanupResp()
    84  		if cmdCleanupResp == nil {
    85  			return nil, errors.Trace(errBodyMissing)
    86  		}
    87  		if keyErr := cmdCleanupResp.GetError(); keyErr != nil {
    88  			return nil, errors.Errorf("unexpected cleanup err: %s", keyErr.String())
    89  		}
    90  		if cmdCleanupResp.CommitVersion == nil {
    91  			// cleanup successfully
    92  			return l.rollbackThenGet()
    93  		}
    94  		// already committed
    95  		return l.commitThenGet(cmdCleanupResp.GetCommitVersion())
    96  	}
    97  	return nil, errors.Annotate(backoffErr, txnRetryableMark)
    98  }
    99  
   100  // If key == nil then only rollback but value is nil
   101  func (l *txnLock) rollbackThenGet() ([]byte, error) {
   102  	req := &pb.Request{
   103  		Type: pb.MessageType_CmdRollbackThenGet.Enum(),
   104  		CmdRbGetReq: &pb.CmdRollbackThenGetRequest{
   105  			Key:         l.key,
   106  			LockVersion: proto.Uint64(l.pl.version),
   107  		},
   108  	}
   109  	var backoffErr error
   110  	for backoff := regionMissBackoff(); backoffErr == nil; backoffErr = backoff() {
   111  		region, err := l.store.regionCache.GetRegion(l.key)
   112  		if err != nil {
   113  			return nil, errors.Trace(err)
   114  		}
   115  		resp, err := l.store.SendKVReq(req, region.VerID())
   116  		if err != nil {
   117  			return nil, errors.Trace(err)
   118  		}
   119  		if regionErr := resp.GetRegionError(); regionErr != nil {
   120  			continue
   121  		}
   122  		cmdRbGResp := resp.GetCmdRbGetResp()
   123  		if cmdRbGResp == nil {
   124  			return nil, errors.Trace(errBodyMissing)
   125  		}
   126  		if keyErr := cmdRbGResp.GetError(); keyErr != nil {
   127  			return nil, errors.Errorf("unexpected rollback err: %s", keyErr.String())
   128  		}
   129  		return cmdRbGResp.GetValue(), nil
   130  	}
   131  	return nil, errors.Annotate(backoffErr, txnRetryableMark)
   132  }
   133  
   134  // If key == nil then only commit but value is nil
   135  func (l *txnLock) commitThenGet(commitVersion uint64) ([]byte, error) {
   136  	req := &pb.Request{
   137  		Type: pb.MessageType_CmdCommitThenGet.Enum(),
   138  		CmdCommitGetReq: &pb.CmdCommitThenGetRequest{
   139  			Key:           l.key,
   140  			LockVersion:   proto.Uint64(l.pl.version),
   141  			CommitVersion: proto.Uint64(commitVersion),
   142  			GetVersion:    proto.Uint64(l.ver),
   143  		},
   144  	}
   145  	var backoffErr error
   146  	for backoff := regionMissBackoff(); backoffErr == nil; backoffErr = backoff() {
   147  		region, err := l.store.regionCache.GetRegion(l.key)
   148  		if err != nil {
   149  			return nil, errors.Trace(err)
   150  		}
   151  		resp, err := l.store.SendKVReq(req, region.VerID())
   152  		if err != nil {
   153  			return nil, errors.Trace(err)
   154  		}
   155  		if regionErr := resp.GetRegionError(); regionErr != nil {
   156  			continue
   157  		}
   158  		cmdCommitGetResp := resp.GetCmdCommitGetResp()
   159  		if cmdCommitGetResp == nil {
   160  			return nil, errors.Trace(errBodyMissing)
   161  		}
   162  		if keyErr := cmdCommitGetResp.GetError(); keyErr != nil {
   163  			return nil, errors.Errorf("unexpected commit err: %s", keyErr.String())
   164  		}
   165  		return cmdCommitGetResp.GetValue(), nil
   166  	}
   167  	return nil, errors.Annotate(backoffErr, txnRetryableMark)
   168  }
   169  
   170  type pLock struct {
   171  	key     []byte
   172  	version uint64
   173  }
   174  
   175  func newPLock(key []byte, ver uint64) pLock {
   176  	return pLock{
   177  		key:     key,
   178  		version: ver,
   179  	}
   180  }