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 }