github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/milevadb-server/einsteindb/prewrite.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  	"math"
    18  	"sync/atomic"
    19  	"time"
    20  
    21  	"github.com/opentracing/opentracing-go"
    22  	"github.com/prometheus/client_golang/prometheus"
    23  	pb "github.com/whtcorpsinc/ekvproto/pkg/ekvrpcpb"
    24  	"github.com/whtcorpsinc/errors"
    25  	"github.com/whtcorpsinc/failpoint"
    26  	"github.com/whtcorpsinc/milevadb/causetstore/einsteindb/einsteindbrpc"
    27  	"github.com/whtcorpsinc/milevadb/config"
    28  	"github.com/whtcorpsinc/milevadb/metrics"
    29  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    30  	"go.uber.org/zap"
    31  )
    32  
    33  type actionPrewrite struct{}
    34  
    35  var _ twoPhaseCommitCausetAction = actionPrewrite{}
    36  var EinsteinDBTxnRegionsNumHistogramPrewrite = metrics.EinsteinDBTxnRegionsNumHistogram.WithLabelValues(metricsTag("prewrite"))
    37  
    38  func (actionPrewrite) String() string {
    39  	return "prewrite"
    40  }
    41  
    42  func (actionPrewrite) EinsteinDBTxnRegionsNumHistogram() prometheus.Observer {
    43  	return EinsteinDBTxnRegionsNumHistogramPrewrite
    44  }
    45  
    46  func (c *twoPhaseCommitter) buildPrewriteRequest(batch batchMutations, txnSize uint64) *einsteindbrpc.Request {
    47  	m := &batch.mutations
    48  	mutations := make([]*pb.Mutation, m.len())
    49  	for i := range m.keys {
    50  		mutations[i] = &pb.Mutation{
    51  			Op:    m.ops[i],
    52  			Key:   m.keys[i],
    53  			Value: m.values[i],
    54  		}
    55  	}
    56  	var minCommitTS uint64
    57  	if c.forUFIDelateTS > 0 {
    58  		minCommitTS = c.forUFIDelateTS + 1
    59  	} else {
    60  		minCommitTS = c.startTS + 1
    61  	}
    62  
    63  	failpoint.Inject("mockZeroCommitTS", func(val failpoint.Value) {
    64  		// Should be val.(uint64) but failpoint doesn't support that.
    65  		if tmp, ok := val.(int); ok && uint64(tmp) == c.startTS {
    66  			minCommitTS = 0
    67  		}
    68  	})
    69  
    70  	req := &pb.PrewriteRequest{
    71  		Mutations:         mutations,
    72  		PrimaryLock:       c.primary(),
    73  		StartVersion:      c.startTS,
    74  		LockTtl:           c.lockTTL,
    75  		IsPessimisticLock: m.isPessimisticLock,
    76  		ForUFIDelateTs:    c.forUFIDelateTS,
    77  		TxnSize:           txnSize,
    78  		MinCommitTs:       minCommitTS,
    79  	}
    80  
    81  	if c.isAsyncCommit() {
    82  		if batch.isPrimary {
    83  			req.Secondaries = c.asyncSecondaries()
    84  		}
    85  		req.UseAsyncCommit = true
    86  		// The async commit can not be used for large transactions, and the commit ts can't be pushed.
    87  		req.MinCommitTs = 0
    88  	}
    89  
    90  	return einsteindbrpc.NewRequest(einsteindbrpc.CmdPrewrite, req, pb.Context{Priority: c.priority, SyncLog: c.syncLog})
    91  }
    92  
    93  func (action actionPrewrite) handleSingleBatch(c *twoPhaseCommitter, bo *Backoffer, batch batchMutations) error {
    94  	txnSize := uint64(c.regionTxnSize[batch.region.id])
    95  	// When we retry because of a region miss, we don't know the transaction size. We set the transaction size here
    96  	// to MaxUint64 to avoid unexpected "resolve dagger lite".
    97  	if len(bo.errors) > 0 {
    98  		txnSize = math.MaxUint64
    99  	}
   100  
   101  	req := c.buildPrewriteRequest(batch, txnSize)
   102  	for {
   103  		resp, err := c.causetstore.SendReq(bo, req, batch.region, readTimeoutShort)
   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.prewriteMutations(bo, batch.mutations)
   117  			return errors.Trace(err)
   118  		}
   119  		if resp.Resp == nil {
   120  			return errors.Trace(ErrBodyMissing)
   121  		}
   122  		prewriteResp := resp.Resp.(*pb.PrewriteResponse)
   123  		keyErrs := prewriteResp.GetErrors()
   124  		if len(keyErrs) == 0 {
   125  			if batch.isPrimary {
   126  				// After writing the primary key, if the size of the transaction is large than 32M,
   127  				// start the ttlManager. The ttlManager will be closed in einsteindbTxn.Commit().
   128  				if int64(c.txnSize) > config.GetGlobalConfig().EinsteinDBClient.TTLRefreshedTxnSize {
   129  					c.run(c, nil)
   130  				}
   131  			}
   132  			if c.isAsyncCommit() {
   133  				// 0 if the min_commit_ts is not ready or any other reason that async
   134  				// commit cannot proceed. The client can then fallback to normal way to
   135  				// continue committing the transaction if prewrite are all finished.
   136  				if prewriteResp.MinCommitTs == 0 {
   137  					if c.testingKnobs.noFallBack {
   138  						return nil
   139  					}
   140  					logutil.Logger(bo.ctx).Warn("async commit cannot proceed since the returned minCommitTS is zero, "+
   141  						"fallback to normal path", zap.Uint64("startTS", c.startTS))
   142  					c.setAsyncCommit(false)
   143  				} else {
   144  					c.mu.Lock()
   145  					if prewriteResp.MinCommitTs > c.minCommitTS {
   146  						c.minCommitTS = prewriteResp.MinCommitTs
   147  					}
   148  					c.mu.Unlock()
   149  				}
   150  			}
   151  			return nil
   152  		}
   153  		var locks []*Lock
   154  		for _, keyErr := range keyErrs {
   155  			// Check already exists error
   156  			if alreadyExist := keyErr.GetAlreadyExist(); alreadyExist != nil {
   157  				key := alreadyExist.GetKey()
   158  				return c.extractKeyExistsErr(key)
   159  			}
   160  
   161  			// Extract dagger from key error
   162  			dagger, err1 := extractLockFromKeyErr(keyErr)
   163  			if err1 != nil {
   164  				return errors.Trace(err1)
   165  			}
   166  			logutil.BgLogger().Info("prewrite encounters dagger",
   167  				zap.Uint64("conn", c.connID),
   168  				zap.Stringer("dagger", dagger))
   169  			locks = append(locks, dagger)
   170  		}
   171  		start := time.Now()
   172  		msBeforeExpired, err := c.causetstore.lockResolver.resolveLocksForWrite(bo, c.startTS, locks)
   173  		if err != nil {
   174  			return errors.Trace(err)
   175  		}
   176  		atomic.AddInt64(&c.getDetail().ResolveLockTime, int64(time.Since(start)))
   177  		if msBeforeExpired > 0 {
   178  			err = bo.BackoffWithMaxSleep(BoTxnLock, int(msBeforeExpired), errors.Errorf("2PC prewrite lockedKeys: %d", len(locks)))
   179  			if err != nil {
   180  				return errors.Trace(err)
   181  			}
   182  		}
   183  	}
   184  }
   185  
   186  func (c *twoPhaseCommitter) prewriteMutations(bo *Backoffer, mutations CommitterMutations) error {
   187  	if span := opentracing.SpanFromContext(bo.ctx); span != nil && span.Tracer() != nil {
   188  		span1 := span.Tracer().StartSpan("twoPhaseCommitter.prewriteMutations", opentracing.ChildOf(span.Context()))
   189  		defer span1.Finish()
   190  		bo.ctx = opentracing.ContextWithSpan(bo.ctx, span1)
   191  	}
   192  
   193  	return c.doCausetActionOnMutations(bo, actionPrewrite{}, mutations)
   194  }