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 }