github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/stochastik/txn.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 stochastik
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"fmt"
    20  	"runtime/trace"
    21  	"strings"
    22  	"sync/atomic"
    23  
    24  	"github.com/opentracing/opentracing-go"
    25  	"github.com/whtcorpsinc/BerolinaSQL/terror"
    26  	"github.com/whtcorpsinc/errors"
    27  	"github.com/whtcorpsinc/failpoint"
    28  	"github.com/whtcorpsinc/fidelpb/go-binlog"
    29  	"github.com/whtcorpsinc/milevadb/blockcodec"
    30  	"github.com/whtcorpsinc/milevadb/causetstore/einsteindb/oracle"
    31  	"github.com/whtcorpsinc/milevadb/config"
    32  	"github.com/whtcorpsinc/milevadb/ekv"
    33  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    34  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    35  	"github.com/whtcorpsinc/milevadb/stochastikctx/binloginfo"
    36  	"go.uber.org/zap"
    37  )
    38  
    39  // TxnState wraps ekv.Transaction to provide a new ekv.Transaction.
    40  // 1. It holds all memex related modification in the buffer before flush to the txn,
    41  // so if execute memex meets error, the txn won't be made dirty.
    42  // 2. It's a lazy transaction, that means it's a txnFuture before StartTS() is really need.
    43  type TxnState struct {
    44  	// States of a TxnState should be one of the followings:
    45  	// Invalid: ekv.Transaction == nil && txnFuture == nil
    46  	// Pending: ekv.Transaction == nil && txnFuture != nil
    47  	// Valid:	ekv.Transaction != nil && txnFuture == nil
    48  	ekv.Transaction
    49  	txnFuture *txnFuture
    50  
    51  	initCnt       int
    52  	stagingHandle ekv.StagingHandle
    53  	mutations     map[int64]*binlog.TableMutation
    54  }
    55  
    56  func (st *TxnState) init() {
    57  	st.mutations = make(map[int64]*binlog.TableMutation)
    58  }
    59  
    60  func (st *TxnState) initStmtBuf() {
    61  	if st.Transaction == nil {
    62  		return
    63  	}
    64  	buf := st.Transaction.GetMemBuffer()
    65  	st.initCnt = buf.Len()
    66  	st.stagingHandle = buf.Staging()
    67  }
    68  
    69  // countHint is estimated count of mutations.
    70  func (st *TxnState) countHint() int {
    71  	if st.stagingHandle == ekv.InvalidStagingHandle {
    72  		return 0
    73  	}
    74  	return st.Transaction.GetMemBuffer().Len() - st.initCnt
    75  }
    76  
    77  func (st *TxnState) flushStmtBuf() {
    78  	if st.stagingHandle == ekv.InvalidStagingHandle {
    79  		return
    80  	}
    81  	buf := st.Transaction.GetMemBuffer()
    82  	buf.Release(st.stagingHandle)
    83  	st.initCnt = buf.Len()
    84  }
    85  
    86  func (st *TxnState) cleanupStmtBuf() {
    87  	if st.stagingHandle == ekv.InvalidStagingHandle {
    88  		return
    89  	}
    90  	buf := st.Transaction.GetMemBuffer()
    91  	buf.Cleanup(st.stagingHandle)
    92  	st.initCnt = buf.Len()
    93  }
    94  
    95  // Size implements the MemBuffer interface.
    96  func (st *TxnState) Size() int {
    97  	if st.Transaction == nil {
    98  		return 0
    99  	}
   100  	return st.Transaction.Size()
   101  }
   102  
   103  // Valid implements the ekv.Transaction interface.
   104  func (st *TxnState) Valid() bool {
   105  	return st.Transaction != nil && st.Transaction.Valid()
   106  }
   107  
   108  func (st *TxnState) pending() bool {
   109  	return st.Transaction == nil && st.txnFuture != nil
   110  }
   111  
   112  func (st *TxnState) validOrPending() bool {
   113  	return st.txnFuture != nil || st.Valid()
   114  }
   115  
   116  func (st *TxnState) String() string {
   117  	if st.Transaction != nil {
   118  		return st.Transaction.String()
   119  	}
   120  	if st.txnFuture != nil {
   121  		return "txnFuture"
   122  	}
   123  	return "invalid transaction"
   124  }
   125  
   126  // GoString implements the "%#v" format for fmt.Printf.
   127  func (st *TxnState) GoString() string {
   128  	var s strings.Builder
   129  	s.WriteString("Txn{")
   130  	if st.pending() {
   131  		s.WriteString("state=pending")
   132  	} else if st.Valid() {
   133  		s.WriteString("state=valid")
   134  		fmt.Fprintf(&s, ", txnStartTS=%d", st.Transaction.StartTS())
   135  		if len(st.mutations) > 0 {
   136  			fmt.Fprintf(&s, ", len(mutations)=%d, %#v", len(st.mutations), st.mutations)
   137  		}
   138  	} else {
   139  		s.WriteString("state=invalid")
   140  	}
   141  
   142  	s.WriteString("}")
   143  	return s.String()
   144  }
   145  
   146  func (st *TxnState) changeInvalidToValid(txn ekv.Transaction) {
   147  	st.Transaction = txn
   148  	st.initStmtBuf()
   149  	st.txnFuture = nil
   150  }
   151  
   152  func (st *TxnState) changeInvalidToPending(future *txnFuture) {
   153  	st.Transaction = nil
   154  	st.txnFuture = future
   155  }
   156  
   157  func (st *TxnState) changePendingToValid(ctx context.Context) error {
   158  	if st.txnFuture == nil {
   159  		return errors.New("transaction future is not set")
   160  	}
   161  
   162  	future := st.txnFuture
   163  	st.txnFuture = nil
   164  
   165  	defer trace.StartRegion(ctx, "WaitTsoFuture").End()
   166  	txn, err := future.wait()
   167  	if err != nil {
   168  		st.Transaction = nil
   169  		return err
   170  	}
   171  	st.Transaction = txn
   172  	st.initStmtBuf()
   173  	return nil
   174  }
   175  
   176  func (st *TxnState) changeToInvalid() {
   177  	if st.stagingHandle != ekv.InvalidStagingHandle {
   178  		st.Transaction.GetMemBuffer().Cleanup(st.stagingHandle)
   179  	}
   180  	st.stagingHandle = ekv.InvalidStagingHandle
   181  	st.Transaction = nil
   182  	st.txnFuture = nil
   183  }
   184  
   185  var hasMockAutoIncIDRetry = int64(0)
   186  
   187  func enableMockAutoIncIDRetry() {
   188  	atomic.StoreInt64(&hasMockAutoIncIDRetry, 1)
   189  }
   190  
   191  func mockAutoIncIDRetry() bool {
   192  	return atomic.LoadInt64(&hasMockAutoIncIDRetry) == 1
   193  }
   194  
   195  var mockAutoRandIDRetryCount = int64(0)
   196  
   197  func needMockAutoRandIDRetry() bool {
   198  	return atomic.LoadInt64(&mockAutoRandIDRetryCount) > 0
   199  }
   200  
   201  func decreaseMockAutoRandIDRetryCount() {
   202  	atomic.AddInt64(&mockAutoRandIDRetryCount, -1)
   203  }
   204  
   205  // ResetMockAutoRandIDRetryCount set the number of occurrences of
   206  // `ekv.ErrTxnRetryable` when calling TxnState.Commit().
   207  func ResetMockAutoRandIDRetryCount(failTimes int64) {
   208  	atomic.StoreInt64(&mockAutoRandIDRetryCount, failTimes)
   209  }
   210  
   211  // Commit overrides the Transaction interface.
   212  func (st *TxnState) Commit(ctx context.Context) error {
   213  	defer st.reset()
   214  	if len(st.mutations) != 0 || st.countHint() != 0 {
   215  		logutil.BgLogger().Error("the code should never run here",
   216  			zap.String("TxnState", st.GoString()),
   217  			zap.Int("staging handler", int(st.stagingHandle)),
   218  			zap.Stack("something must be wrong"))
   219  		return errors.Trace(ekv.ErrInvalidTxn)
   220  	}
   221  
   222  	// mockCommitError8942 is used for PR #8942.
   223  	failpoint.Inject("mockCommitError8942", func(val failpoint.Value) {
   224  		if val.(bool) {
   225  			failpoint.Return(ekv.ErrTxnRetryable)
   226  		}
   227  	})
   228  
   229  	// mockCommitRetryForAutoIncID is used to mock an commit retry for adjustAutoIncrementCauset.
   230  	failpoint.Inject("mockCommitRetryForAutoIncID", func(val failpoint.Value) {
   231  		if val.(bool) && !mockAutoIncIDRetry() {
   232  			enableMockAutoIncIDRetry()
   233  			failpoint.Return(ekv.ErrTxnRetryable)
   234  		}
   235  	})
   236  
   237  	failpoint.Inject("mockCommitRetryForAutoRandID", func(val failpoint.Value) {
   238  		if val.(bool) && needMockAutoRandIDRetry() {
   239  			decreaseMockAutoRandIDRetryCount()
   240  			failpoint.Return(ekv.ErrTxnRetryable)
   241  		}
   242  	})
   243  
   244  	return st.Transaction.Commit(ctx)
   245  }
   246  
   247  // Rollback overrides the Transaction interface.
   248  func (st *TxnState) Rollback() error {
   249  	defer st.reset()
   250  	return st.Transaction.Rollback()
   251  }
   252  
   253  func (st *TxnState) reset() {
   254  	st.cleanup()
   255  	st.changeToInvalid()
   256  }
   257  
   258  func (st *TxnState) cleanup() {
   259  	st.cleanupStmtBuf()
   260  	st.initStmtBuf()
   261  	for key := range st.mutations {
   262  		delete(st.mutations, key)
   263  	}
   264  }
   265  
   266  // KeysNeedToLock returns the keys need to be locked.
   267  func (st *TxnState) KeysNeedToLock() ([]ekv.Key, error) {
   268  	if st.stagingHandle == ekv.InvalidStagingHandle {
   269  		return nil, nil
   270  	}
   271  	keys := make([]ekv.Key, 0, st.countHint())
   272  	buf := st.Transaction.GetMemBuffer()
   273  	buf.InspectStage(st.stagingHandle, func(k ekv.Key, flags ekv.KeyFlags, v []byte) {
   274  		if !keyNeedToLock(k, v, flags) {
   275  			return
   276  		}
   277  		keys = append(keys, k)
   278  	})
   279  	return keys, nil
   280  }
   281  
   282  func keyNeedToLock(k, v []byte, flags ekv.KeyFlags) bool {
   283  	isTableKey := bytes.HasPrefix(k, blockcodec.TablePrefix())
   284  	if !isTableKey {
   285  		// spacetime key always need to dagger.
   286  		return true
   287  	}
   288  	if flags.HasPresumeKeyNotExists() {
   289  		return true
   290  	}
   291  	isDelete := len(v) == 0
   292  	if isDelete {
   293  		// only need to delete event key.
   294  		return k[10] == 'r'
   295  	}
   296  	if blockcodec.IsUntouchedIndexKValue(k, v) {
   297  		return false
   298  	}
   299  	isNonUniqueIndex := blockcodec.IsIndexKey(k) && len(v) == 1
   300  	// Put event key and unique index need to dagger.
   301  	return !isNonUniqueIndex
   302  }
   303  
   304  func getBinlogMutation(ctx stochastikctx.Context, blockID int64) *binlog.TableMutation {
   305  	bin := binloginfo.GetPrewriteValue(ctx, true)
   306  	for i := range bin.Mutations {
   307  		if bin.Mutations[i].TableId == blockID {
   308  			return &bin.Mutations[i]
   309  		}
   310  	}
   311  	idx := len(bin.Mutations)
   312  	bin.Mutations = append(bin.Mutations, binlog.TableMutation{TableId: blockID})
   313  	return &bin.Mutations[idx]
   314  }
   315  
   316  func mergeToMutation(m1, m2 *binlog.TableMutation) {
   317  	m1.InsertedRows = append(m1.InsertedRows, m2.InsertedRows...)
   318  	m1.UFIDelatedRows = append(m1.UFIDelatedRows, m2.UFIDelatedRows...)
   319  	m1.DeletedIds = append(m1.DeletedIds, m2.DeletedIds...)
   320  	m1.DeletedPks = append(m1.DeletedPks, m2.DeletedPks...)
   321  	m1.DeletedRows = append(m1.DeletedRows, m2.DeletedRows...)
   322  	m1.Sequence = append(m1.Sequence, m2.Sequence...)
   323  }
   324  
   325  type txnFailFuture struct{}
   326  
   327  func (txnFailFuture) Wait() (uint64, error) {
   328  	return 0, errors.New("mock get timestamp fail")
   329  }
   330  
   331  // txnFuture is a promise, which promises to return a txn in future.
   332  type txnFuture struct {
   333  	future      oracle.Future
   334  	causetstore ekv.CausetStorage
   335  }
   336  
   337  func (tf *txnFuture) wait() (ekv.Transaction, error) {
   338  	startTS, err := tf.future.Wait()
   339  	if err == nil {
   340  		return tf.causetstore.BeginWithStartTS(startTS)
   341  	} else if config.GetGlobalConfig().CausetStore == "entangledstore" {
   342  		return nil, err
   343  	}
   344  
   345  	logutil.BgLogger().Warn("wait tso failed", zap.Error(err))
   346  	// It would retry get timestamp.
   347  	return tf.causetstore.Begin()
   348  }
   349  
   350  func (s *stochastik) getTxnFuture(ctx context.Context) *txnFuture {
   351  	if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil {
   352  		span1 := span.Tracer().StartSpan("stochastik.getTxnFuture", opentracing.ChildOf(span.Context()))
   353  		defer span1.Finish()
   354  		ctx = opentracing.ContextWithSpan(ctx, span1)
   355  	}
   356  
   357  	oracleStore := s.causetstore.GetOracle()
   358  	var tsFuture oracle.Future
   359  	if s.stochastikVars.LowResolutionTSO {
   360  		tsFuture = oracleStore.GetLowResolutionTimestampAsync(ctx)
   361  	} else {
   362  		tsFuture = oracleStore.GetTimestampAsync(ctx)
   363  	}
   364  	ret := &txnFuture{future: tsFuture, causetstore: s.causetstore}
   365  	failpoint.InjectContext(ctx, "mockGetTSFail", func() {
   366  		ret.future = txnFailFuture{}
   367  	})
   368  	return ret
   369  }
   370  
   371  // HasDirtyContent checks whether there's dirty uFIDelate on the given causet.
   372  // Put this function here is to avoid cycle import.
   373  func (s *stochastik) HasDirtyContent(tid int64) bool {
   374  	if s.txn.Transaction == nil {
   375  		return false
   376  	}
   377  	seekKey := blockcodec.EncodeTablePrefix(tid)
   378  	it, err := s.txn.GetMemBuffer().Iter(seekKey, nil)
   379  	terror.Log(err)
   380  	return it.Valid() && bytes.HasPrefix(it.Key(), seekKey)
   381  }
   382  
   383  // StmtCommit implements the stochastikctx.Context interface.
   384  func (s *stochastik) StmtCommit() {
   385  	defer func() {
   386  		s.txn.cleanup()
   387  	}()
   388  
   389  	st := &s.txn
   390  	st.flushStmtBuf()
   391  
   392  	// Need to flush binlog.
   393  	for blockID, delta := range st.mutations {
   394  		mutation := getBinlogMutation(s, blockID)
   395  		mergeToMutation(mutation, delta)
   396  	}
   397  }
   398  
   399  // StmtRollback implements the stochastikctx.Context interface.
   400  func (s *stochastik) StmtRollback() {
   401  	s.txn.cleanup()
   402  }
   403  
   404  // StmtGetMutation implements the stochastikctx.Context interface.
   405  func (s *stochastik) StmtGetMutation(blockID int64) *binlog.TableMutation {
   406  	st := &s.txn
   407  	if _, ok := st.mutations[blockID]; !ok {
   408  		st.mutations[blockID] = &binlog.TableMutation{TableId: blockID}
   409  	}
   410  	return st.mutations[blockID]
   411  }