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  }