github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/milevadb-server/einsteindb/pessimistic.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 einsteindb 15 16 import ( 17 "sync/atomic" 18 "time" 19 20 "github.com/prometheus/client_golang/prometheus" 21 pb "github.com/whtcorpsinc/ekvproto/pkg/ekvrpcpb" 22 "github.com/whtcorpsinc/errors" 23 "github.com/whtcorpsinc/failpoint" 24 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb/einsteindbrpc" 25 "github.com/whtcorpsinc/milevadb/ekv" 26 "github.com/whtcorpsinc/milevadb/metrics" 27 ) 28 29 type actionPessimisticLock struct { 30 *ekv.LockCtx 31 } 32 type actionPessimisticRollback struct{} 33 34 var ( 35 _ twoPhaseCommitCausetAction = actionPessimisticLock{} 36 _ twoPhaseCommitCausetAction = actionPessimisticRollback{} 37 38 EinsteinDBTxnRegionsNumHistogramPessimisticLock = metrics.EinsteinDBTxnRegionsNumHistogram.WithLabelValues(metricsTag("pessimistic_lock")) 39 EinsteinDBTxnRegionsNumHistogramPessimisticRollback = metrics.EinsteinDBTxnRegionsNumHistogram.WithLabelValues(metricsTag("pessimistic_rollback")) 40 ) 41 42 func (actionPessimisticLock) String() string { 43 return "pessimistic_lock" 44 } 45 46 func (actionPessimisticLock) EinsteinDBTxnRegionsNumHistogram() prometheus.Observer { 47 return EinsteinDBTxnRegionsNumHistogramPessimisticLock 48 } 49 50 func (actionPessimisticRollback) String() string { 51 return "pessimistic_rollback" 52 } 53 54 func (actionPessimisticRollback) EinsteinDBTxnRegionsNumHistogram() prometheus.Observer { 55 return EinsteinDBTxnRegionsNumHistogramPessimisticRollback 56 } 57 58 func (action actionPessimisticLock) handleSingleBatch(c *twoPhaseCommitter, bo *Backoffer, batch batchMutations) error { 59 m := &batch.mutations 60 mutations := make([]*pb.Mutation, m.len()) 61 for i := range m.keys { 62 mut := &pb.Mutation{ 63 Op: pb.Op_PessimisticLock, 64 Key: m.keys[i], 65 } 66 if c.txn.us.HasPresumeKeyNotExists(m.keys[i]) { 67 mut.Assertion = pb.Assertion_NotExist 68 } 69 mutations[i] = mut 70 } 71 elapsed := uint64(time.Since(c.txn.startTime) / time.Millisecond) 72 req := einsteindbrpc.NewRequest(einsteindbrpc.CmdPessimisticLock, &pb.PessimisticLockRequest{ 73 Mutations: mutations, 74 PrimaryLock: c.primary(), 75 StartVersion: c.startTS, 76 ForUFIDelateTs: c.forUFIDelateTS, 77 LockTtl: elapsed + atomic.LoadUint64(&ManagedLockTTL), 78 IsFirstLock: c.isFirstLock, 79 WaitTimeout: action.LockWaitTime, 80 ReturnValues: action.ReturnValues, 81 MinCommitTs: c.forUFIDelateTS + 1, 82 }, pb.Context{Priority: c.priority, SyncLog: c.syncLog}) 83 lockWaitStartTime := action.WaitStartTime 84 for { 85 // if lockWaitTime set, refine the request `WaitTimeout` field based on timeout limit 86 if action.LockWaitTime > 0 { 87 timeLeft := action.LockWaitTime - (time.Since(lockWaitStartTime)).Milliseconds() 88 if timeLeft <= 0 { 89 req.PessimisticLock().WaitTimeout = ekv.LockNoWait 90 } else { 91 req.PessimisticLock().WaitTimeout = timeLeft 92 } 93 } 94 failpoint.Inject("PessimisticLockErrWriteConflict", func() error { 95 time.Sleep(300 * time.Millisecond) 96 return ekv.ErrWriteConflict 97 }) 98 startTime := time.Now() 99 resp, err := c.causetstore.SendReq(bo, req, batch.region, readTimeoutShort) 100 if action.LockCtx.Stats != nil { 101 atomic.AddInt64(&action.LockCtx.Stats.LockRPCTime, int64(time.Since(startTime))) 102 atomic.AddInt64(&action.LockCtx.Stats.LockRPCCount, 1) 103 } 104 if err != nil { 105 return errors.Trace(err) 106 } 107 regionErr, err := resp.GetRegionError() 108 if err != nil { 109 return errors.Trace(err) 110 } 111 if regionErr != nil { 112 err = bo.Backoff(BoRegionMiss, errors.New(regionErr.String())) 113 if err != nil { 114 return errors.Trace(err) 115 } 116 err = c.pessimisticLockMutations(bo, action.LockCtx, batch.mutations) 117 return errors.Trace(err) 118 } 119 if resp.Resp == nil { 120 return errors.Trace(ErrBodyMissing) 121 } 122 lockResp := resp.Resp.(*pb.PessimisticLockResponse) 123 keyErrs := lockResp.GetErrors() 124 if len(keyErrs) == 0 { 125 if action.ReturnValues { 126 action.ValuesLock.Lock() 127 for i, mutation := range mutations { 128 action.Values[string(mutation.Key)] = ekv.ReturnedValue{Value: lockResp.Values[i]} 129 } 130 action.ValuesLock.Unlock() 131 } 132 return nil 133 } 134 var locks []*Lock 135 for _, keyErr := range keyErrs { 136 // Check already exists error 137 if alreadyExist := keyErr.GetAlreadyExist(); alreadyExist != nil { 138 key := alreadyExist.GetKey() 139 return c.extractKeyExistsErr(key) 140 } 141 if deadlock := keyErr.Deadlock; deadlock != nil { 142 return &ErrDeadlock{Deadlock: deadlock} 143 } 144 145 // Extract dagger from key error 146 dagger, err1 := extractLockFromKeyErr(keyErr) 147 if err1 != nil { 148 return errors.Trace(err1) 149 } 150 locks = append(locks, dagger) 151 } 152 // Because we already waited on einsteindb, no need to Backoff here. 153 // einsteindb default will wait 3s(also the maximum wait value) when dagger error occurs 154 startTime = time.Now() 155 msBeforeTxnExpired, _, err := c.causetstore.lockResolver.ResolveLocks(bo, 0, locks) 156 if err != nil { 157 return errors.Trace(err) 158 } 159 if action.LockCtx.Stats != nil { 160 atomic.AddInt64(&action.LockCtx.Stats.ResolveLockTime, int64(time.Since(startTime))) 161 } 162 163 // If msBeforeTxnExpired is not zero, it means there are still locks blocking us acquiring 164 // the pessimistic dagger. We should return acquire fail with nowait set or timeout error if necessary. 165 if msBeforeTxnExpired > 0 { 166 if action.LockWaitTime == ekv.LockNoWait { 167 return ErrLockAcquireFailAndNoWaitSet 168 } else if action.LockWaitTime == ekv.LockAlwaysWait { 169 // do nothing but keep wait 170 } else { 171 // the lockWaitTime is set, we should return wait timeout if we are still blocked by a dagger 172 if time.Since(lockWaitStartTime).Milliseconds() >= action.LockWaitTime { 173 return errors.Trace(ErrLockWaitTimeout) 174 } 175 } 176 if action.LockCtx.PessimisticLockWaited != nil { 177 atomic.StoreInt32(action.LockCtx.PessimisticLockWaited, 1) 178 } 179 } 180 181 // Handle the killed flag when waiting for the pessimistic dagger. 182 // When a txn runs into LockKeys() and backoff here, it has no chance to call 183 // interlock.Next() and check the killed flag. 184 if action.Killed != nil { 185 // Do not reset the killed flag here! 186 // actionPessimisticLock runs on each region parallelly, we have to consider that 187 // the error may be dropped. 188 if atomic.LoadUint32(action.Killed) == 1 { 189 return errors.Trace(ErrQueryInterrupted) 190 } 191 } 192 } 193 } 194 195 func (actionPessimisticRollback) handleSingleBatch(c *twoPhaseCommitter, bo *Backoffer, batch batchMutations) error { 196 req := einsteindbrpc.NewRequest(einsteindbrpc.CmdPessimisticRollback, &pb.PessimisticRollbackRequest{ 197 StartVersion: c.startTS, 198 ForUFIDelateTs: c.forUFIDelateTS, 199 Keys: batch.mutations.keys, 200 }) 201 resp, err := c.causetstore.SendReq(bo, req, batch.region, readTimeoutShort) 202 if err != nil { 203 return errors.Trace(err) 204 } 205 regionErr, err := resp.GetRegionError() 206 if err != nil { 207 return errors.Trace(err) 208 } 209 if regionErr != nil { 210 err = bo.Backoff(BoRegionMiss, errors.New(regionErr.String())) 211 if err != nil { 212 return errors.Trace(err) 213 } 214 err = c.pessimisticRollbackMutations(bo, batch.mutations) 215 return errors.Trace(err) 216 } 217 return nil 218 } 219 220 func (c *twoPhaseCommitter) pessimisticLockMutations(bo *Backoffer, lockCtx *ekv.LockCtx, mutations CommitterMutations) error { 221 return c.doCausetActionOnMutations(bo, actionPessimisticLock{lockCtx}, mutations) 222 } 223 224 func (c *twoPhaseCommitter) pessimisticRollbackMutations(bo *Backoffer, mutations CommitterMutations) error { 225 return c.doCausetActionOnMutations(bo, actionPessimisticRollback{}, mutations) 226 }