github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/batch_point_get.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 interlock
    15  
    16  import (
    17  	"context"
    18  	"sort"
    19  	"sync/atomic"
    20  
    21  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    22  	"github.com/whtcorpsinc/failpoint"
    23  	"github.com/whtcorpsinc/milevadb/blockcodec"
    24  	"github.com/whtcorpsinc/milevadb/causetstore/einsteindb"
    25  	"github.com/whtcorpsinc/milevadb/ekv"
    26  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    27  	"github.com/whtcorpsinc/milevadb/soliton/codec"
    28  	"github.com/whtcorpsinc/milevadb/soliton/math"
    29  	"github.com/whtcorpsinc/milevadb/soliton/replog"
    30  	"github.com/whtcorpsinc/milevadb/soliton/rowcodec"
    31  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    32  	"github.com/whtcorpsinc/milevadb/stochastikctx/variable"
    33  	"github.com/whtcorpsinc/milevadb/types"
    34  )
    35  
    36  // BatchPointGetInterDirc executes a bunch of point select queries.
    37  type BatchPointGetInterDirc struct {
    38  	baseInterlockingDirectorate
    39  
    40  	tblInfo          *perceptron.BlockInfo
    41  	idxInfo          *perceptron.IndexInfo
    42  	handles          []ekv.Handle
    43  	physIDs          []int64
    44  	partPos          int
    45  	idxVals          [][]types.Causet
    46  	startTS          uint64
    47  	snapshotTS       uint64
    48  	txn              ekv.Transaction
    49  	dagger           bool
    50  	waitTime         int64
    51  	inited           uint32
    52  	values           [][]byte
    53  	index            int
    54  	rowCausetDecoder *rowcodec.ChunkCausetDecoder
    55  	keepOrder        bool
    56  	desc             bool
    57  	batchGetter      ekv.BatchGetter
    58  
    59  	defCausumns []*perceptron.DeferredCausetInfo
    60  	// virtualDeferredCausetIndex records all the indices of virtual defCausumns and sort them in definition
    61  	// to make sure we can compute the virtual defCausumn in right order.
    62  	virtualDeferredCausetIndex []int
    63  
    64  	// virtualDeferredCausetRetFieldTypes records the RetFieldTypes of virtual defCausumns.
    65  	virtualDeferredCausetRetFieldTypes []*types.FieldType
    66  
    67  	snapshot ekv.Snapshot
    68  	stats    *runtimeStatsWithSnapshot
    69  }
    70  
    71  // buildVirtualDeferredCausetInfo saves virtual defCausumn indices and sort them in definition order
    72  func (e *BatchPointGetInterDirc) buildVirtualDeferredCausetInfo() {
    73  	e.virtualDeferredCausetIndex = buildVirtualDeferredCausetIndex(e.Schema(), e.defCausumns)
    74  	if len(e.virtualDeferredCausetIndex) > 0 {
    75  		e.virtualDeferredCausetRetFieldTypes = make([]*types.FieldType, len(e.virtualDeferredCausetIndex))
    76  		for i, idx := range e.virtualDeferredCausetIndex {
    77  			e.virtualDeferredCausetRetFieldTypes[i] = e.schemaReplicant.DeferredCausets[idx].RetType
    78  		}
    79  	}
    80  }
    81  
    82  // Open implements the InterlockingDirectorate interface.
    83  func (e *BatchPointGetInterDirc) Open(context.Context) error {
    84  	e.snapshotTS = e.startTS
    85  	txnCtx := e.ctx.GetStochastikVars().TxnCtx
    86  	if e.dagger {
    87  		e.snapshotTS = txnCtx.GetForUFIDelateTS()
    88  	}
    89  	txn, err := e.ctx.Txn(false)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	e.txn = txn
    94  	var snapshot ekv.Snapshot
    95  	if txn.Valid() && txnCtx.StartTS == txnCtx.GetForUFIDelateTS() {
    96  		// We can safely reuse the transaction snapshot if startTS is equal to forUFIDelateTS.
    97  		// The snapshot may contains cache that can reduce RPC call.
    98  		snapshot = txn.GetSnapshot()
    99  	} else {
   100  		snapshot, err = e.ctx.GetStore().GetSnapshot(ekv.Version{Ver: e.snapshotTS})
   101  		if err != nil {
   102  			return err
   103  		}
   104  	}
   105  	if e.runtimeStats != nil {
   106  		snapshotStats := &einsteindb.SnapshotRuntimeStats{}
   107  		e.stats = &runtimeStatsWithSnapshot{
   108  			SnapshotRuntimeStats: snapshotStats,
   109  		}
   110  		snapshot.SetOption(ekv.DefCauslectRuntimeStats, snapshotStats)
   111  		e.ctx.GetStochastikVars().StmtCtx.RuntimeStatsDefCausl.RegisterStats(e.id, e.stats)
   112  	}
   113  	if e.ctx.GetStochastikVars().GetReplicaRead().IsFollowerRead() {
   114  		snapshot.SetOption(ekv.ReplicaRead, ekv.ReplicaReadFollower)
   115  	}
   116  	snapshot.SetOption(ekv.TaskID, e.ctx.GetStochastikVars().StmtCtx.TaskID)
   117  	var batchGetter ekv.BatchGetter = snapshot
   118  	if txn.Valid() {
   119  		batchGetter = ekv.NewBufferBatchGetter(txn.GetMemBuffer(), &PessimisticLockCacheGetter{txnCtx: txnCtx}, snapshot)
   120  	}
   121  	e.snapshot = snapshot
   122  	e.batchGetter = batchGetter
   123  	return nil
   124  }
   125  
   126  // Close implements the InterlockingDirectorate interface.
   127  func (e *BatchPointGetInterDirc) Close() error {
   128  	if e.runtimeStats != nil && e.snapshot != nil {
   129  		e.snapshot.DelOption(ekv.DefCauslectRuntimeStats)
   130  	}
   131  	e.inited = 0
   132  	e.index = 0
   133  	return nil
   134  }
   135  
   136  // Next implements the InterlockingDirectorate interface.
   137  func (e *BatchPointGetInterDirc) Next(ctx context.Context, req *chunk.Chunk) error {
   138  	req.Reset()
   139  	if atomic.CompareAndSwapUint32(&e.inited, 0, 1) {
   140  		if err := e.initialize(ctx); err != nil {
   141  			return err
   142  		}
   143  	}
   144  
   145  	if e.index >= len(e.values) {
   146  		return nil
   147  	}
   148  	for !req.IsFull() && e.index < len(e.values) {
   149  		handle, val := e.handles[e.index], e.values[e.index]
   150  		err := DecodeEventValToChunk(e.base().ctx, e.schemaReplicant, e.tblInfo, handle, val, req, e.rowCausetDecoder)
   151  		if err != nil {
   152  			return err
   153  		}
   154  		e.index++
   155  	}
   156  
   157  	err := FillVirtualDeferredCausetValue(e.virtualDeferredCausetRetFieldTypes, e.virtualDeferredCausetIndex, e.schemaReplicant, e.defCausumns, e.ctx, req)
   158  	if err != nil {
   159  		return err
   160  	}
   161  	return nil
   162  }
   163  
   164  func datumsContainNull(vals []types.Causet) bool {
   165  	for _, val := range vals {
   166  		if val.IsNull() {
   167  			return true
   168  		}
   169  	}
   170  	return false
   171  }
   172  
   173  func (e *BatchPointGetInterDirc) initialize(ctx context.Context) error {
   174  	var handleVals map[string][]byte
   175  	var indexKeys []ekv.Key
   176  	var err error
   177  	batchGetter := e.batchGetter
   178  	if e.idxInfo != nil && !isCommonHandleRead(e.tblInfo, e.idxInfo) {
   179  		// `SELECT a, b FROM t WHERE (a, b) IN ((1, 2), (1, 2), (2, 1), (1, 2))` should not return duplicated rows
   180  		dedup := make(map[replog.MublockString]struct{})
   181  		keys := make([]ekv.Key, 0, len(e.idxVals))
   182  		for _, idxVals := range e.idxVals {
   183  			// For all x, 'x IN (null)' evaluate to null, so the query get no result.
   184  			if datumsContainNull(idxVals) {
   185  				continue
   186  			}
   187  
   188  			physID := getPhysID(e.tblInfo, idxVals[e.partPos].GetInt64())
   189  			idxKey, err1 := EncodeUniqueIndexKey(e.ctx, e.tblInfo, e.idxInfo, idxVals, physID)
   190  			if err1 != nil && !ekv.ErrNotExist.Equal(err1) {
   191  				return err1
   192  			}
   193  			s := replog.String(idxKey)
   194  			if _, found := dedup[s]; found {
   195  				continue
   196  			}
   197  			dedup[s] = struct{}{}
   198  			keys = append(keys, idxKey)
   199  		}
   200  		if e.keepOrder {
   201  			sort.Slice(keys, func(i int, j int) bool {
   202  				if e.desc {
   203  					return keys[i].Cmp(keys[j]) > 0
   204  				}
   205  				return keys[i].Cmp(keys[j]) < 0
   206  			})
   207  		}
   208  		indexKeys = keys
   209  
   210  		// SELECT * FROM t WHERE x IN (null), in this case there is no key.
   211  		if len(keys) == 0 {
   212  			return nil
   213  		}
   214  
   215  		// Fetch all handles.
   216  		handleVals, err = batchGetter.BatchGet(ctx, keys)
   217  		if err != nil {
   218  			return err
   219  		}
   220  
   221  		e.handles = make([]ekv.Handle, 0, len(keys))
   222  		if e.tblInfo.Partition != nil {
   223  			e.physIDs = make([]int64, 0, len(keys))
   224  		}
   225  		for _, key := range keys {
   226  			handleVal := handleVals[string(key)]
   227  			if len(handleVal) == 0 {
   228  				continue
   229  			}
   230  			handle, err1 := blockcodec.DecodeHandleInUniqueIndexValue(handleVal, e.tblInfo.IsCommonHandle)
   231  			if err1 != nil {
   232  				return err1
   233  			}
   234  			e.handles = append(e.handles, handle)
   235  			if e.tblInfo.Partition != nil {
   236  				e.physIDs = append(e.physIDs, blockcodec.DecodeBlockID(key))
   237  			}
   238  		}
   239  
   240  		// The injection is used to simulate following scenario:
   241  		// 1. Stochastik A create a point get query but pause before second time `GET` ekv from backend
   242  		// 2. Stochastik B create an UFIDelATE query to uFIDelate the record that will be obtained in step 1
   243  		// 3. Then point get retrieve data from backend after step 2 finished
   244  		// 4. Check the result
   245  		failpoint.InjectContext(ctx, "batchPointGetRepeablockReadTest-step1", func() {
   246  			if ch, ok := ctx.Value("batchPointGetRepeablockReadTest").(chan struct{}); ok {
   247  				// Make `UFIDelATE` continue
   248  				close(ch)
   249  			}
   250  			// Wait `UFIDelATE` finished
   251  			failpoint.InjectContext(ctx, "batchPointGetRepeablockReadTest-step2", nil)
   252  		})
   253  	} else if e.keepOrder {
   254  		sort.Slice(e.handles, func(i int, j int) bool {
   255  			if e.desc {
   256  				return e.handles[i].Compare(e.handles[j]) > 0
   257  			}
   258  			return e.handles[i].Compare(e.handles[j]) < 0
   259  		})
   260  	}
   261  
   262  	keys := make([]ekv.Key, len(e.handles))
   263  	for i, handle := range e.handles {
   264  		var tID int64
   265  		if len(e.physIDs) > 0 {
   266  			tID = e.physIDs[i]
   267  		} else {
   268  			if handle.IsInt() {
   269  				tID = getPhysID(e.tblInfo, handle.IntValue())
   270  			} else {
   271  				_, d, err1 := codec.DecodeOne(handle.EncodedDefCaus(e.partPos))
   272  				if err1 != nil {
   273  					return err1
   274  				}
   275  				tID = getPhysID(e.tblInfo, d.GetInt64())
   276  			}
   277  		}
   278  		key := blockcodec.EncodeEventKeyWithHandle(tID, handle)
   279  		keys[i] = key
   280  	}
   281  
   282  	var values map[string][]byte
   283  	rc := e.ctx.GetStochastikVars().IsPessimisticReadConsistency()
   284  	// Lock keys (include exists and non-exists keys) before fetch all values for Repeablock Read Isolation.
   285  	if e.dagger && !rc {
   286  		lockKeys := make([]ekv.Key, len(keys), len(keys)+len(indexKeys))
   287  		copy(lockKeys, keys)
   288  		for _, idxKey := range indexKeys {
   289  			// dagger the non-exist index key, using len(val) in case BatchGet result contains some zero len entries
   290  			if val := handleVals[string(idxKey)]; len(val) == 0 {
   291  				lockKeys = append(lockKeys, idxKey)
   292  			}
   293  		}
   294  		err = LockKeys(ctx, e.ctx, e.waitTime, lockKeys...)
   295  		if err != nil {
   296  			return err
   297  		}
   298  	}
   299  	// Fetch all values.
   300  	values, err = batchGetter.BatchGet(ctx, keys)
   301  	if err != nil {
   302  		return err
   303  	}
   304  	handles := make([]ekv.Handle, 0, len(values))
   305  	var existKeys []ekv.Key
   306  	if e.dagger && rc {
   307  		existKeys = make([]ekv.Key, 0, len(values))
   308  	}
   309  	e.values = make([][]byte, 0, len(values))
   310  	for i, key := range keys {
   311  		val := values[string(key)]
   312  		if len(val) == 0 {
   313  			if e.idxInfo != nil && (!e.tblInfo.IsCommonHandle || !e.idxInfo.Primary) {
   314  				return ekv.ErrNotExist.GenWithStack("inconsistent extra index %s, handle %d not found in causet",
   315  					e.idxInfo.Name.O, e.handles[i])
   316  			}
   317  			continue
   318  		}
   319  		e.values = append(e.values, val)
   320  		handles = append(handles, e.handles[i])
   321  		if e.dagger && rc {
   322  			existKeys = append(existKeys, key)
   323  		}
   324  	}
   325  	// Lock exists keys only for Read Committed Isolation.
   326  	if e.dagger && rc {
   327  		err = LockKeys(ctx, e.ctx, e.waitTime, existKeys...)
   328  		if err != nil {
   329  			return err
   330  		}
   331  	}
   332  	e.handles = handles
   333  	return nil
   334  }
   335  
   336  // LockKeys locks the keys for pessimistic transaction.
   337  func LockKeys(ctx context.Context, seCtx stochastikctx.Context, lockWaitTime int64, keys ...ekv.Key) error {
   338  	txnCtx := seCtx.GetStochastikVars().TxnCtx
   339  	lctx := newLockCtx(seCtx.GetStochastikVars(), lockWaitTime)
   340  	if txnCtx.IsPessimistic {
   341  		lctx.ReturnValues = true
   342  		lctx.Values = make(map[string]ekv.ReturnedValue, len(keys))
   343  	}
   344  	err := doLockKeys(ctx, seCtx, lctx, keys...)
   345  	if err != nil {
   346  		return err
   347  	}
   348  	if txnCtx.IsPessimistic {
   349  		// When doLockKeys returns without error, no other goroutines access the map,
   350  		// it's safe to read it without mutex.
   351  		for _, key := range keys {
   352  			rv := lctx.Values[string(key)]
   353  			if !rv.AlreadyLocked {
   354  				txnCtx.SetPessimisticLockCache(key, rv.Value)
   355  			}
   356  		}
   357  	}
   358  	return nil
   359  }
   360  
   361  // PessimisticLockCacheGetter implements the ekv.Getter interface.
   362  // It is used as a midbse cache to construct the BufferedBatchGetter.
   363  type PessimisticLockCacheGetter struct {
   364  	txnCtx *variable.TransactionContext
   365  }
   366  
   367  // Get implements the ekv.Getter interface.
   368  func (getter *PessimisticLockCacheGetter) Get(_ context.Context, key ekv.Key) ([]byte, error) {
   369  	val, ok := getter.txnCtx.GetKeyInPessimisticLockCache(key)
   370  	if ok {
   371  		return val, nil
   372  	}
   373  	return nil, ekv.ErrNotExist
   374  }
   375  
   376  func getPhysID(tblInfo *perceptron.BlockInfo, intVal int64) int64 {
   377  	pi := tblInfo.Partition
   378  	if pi == nil {
   379  		return tblInfo.ID
   380  	}
   381  	partIdx := math.Abs(intVal % int64(pi.Num))
   382  	return pi.Definitions[partIdx].ID
   383  }