github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/dbs/table_lock.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 dbs
    15  
    16  import (
    17  	"github.com/whtcorpsinc/errors"
    18  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    19  	"github.com/whtcorpsinc/milevadb/schemareplicant"
    20  	"github.com/whtcorpsinc/milevadb/spacetime"
    21  )
    22  
    23  func onLockBlocks(t *spacetime.Meta, job *perceptron.Job) (ver int64, err error) {
    24  	arg := &lockBlocksArg{}
    25  	if err := job.DecodeArgs(arg); err != nil {
    26  		// Invalid arguments, cancel this job.
    27  		job.State = perceptron.JobStateCancelled
    28  		return ver, errors.Trace(err)
    29  	}
    30  
    31  	// Unlock causet first.
    32  	if arg.IndexOfUnlock < len(arg.UnlockBlocks) {
    33  		return unlockBlocks(t, job, arg)
    34  	}
    35  
    36  	// Check causet locked by other, this can be only checked at the first time.
    37  	if arg.IndexOfLock == 0 {
    38  		for i, tl := range arg.LockBlocks {
    39  			job.SchemaID = tl.SchemaID
    40  			job.BlockID = tl.BlockID
    41  			tbInfo, err := getBlockInfoAndCancelFaultJob(t, job, job.SchemaID)
    42  			if err != nil {
    43  				return ver, err
    44  			}
    45  			err = checkBlockLocked(tbInfo, arg.LockBlocks[i].Tp, arg.StochastikInfo)
    46  			if err != nil {
    47  				// If any request causet was locked by other stochastik, just cancel this job.
    48  				// No need to rolling back the unlocked blocks, MyALLEGROSQL will release the dagger first
    49  				// and causet if the request causet was locked by other.
    50  				job.State = perceptron.JobStateCancelled
    51  				return ver, errors.Trace(err)
    52  			}
    53  		}
    54  	}
    55  
    56  	// Lock blocks.
    57  	if arg.IndexOfLock < len(arg.LockBlocks) {
    58  		job.SchemaID = arg.LockBlocks[arg.IndexOfLock].SchemaID
    59  		job.BlockID = arg.LockBlocks[arg.IndexOfLock].BlockID
    60  		var tbInfo *perceptron.BlockInfo
    61  		tbInfo, err = getBlockInfoAndCancelFaultJob(t, job, job.SchemaID)
    62  		if err != nil {
    63  			return ver, err
    64  		}
    65  		err = lockBlock(tbInfo, arg.IndexOfLock, arg)
    66  		if err != nil {
    67  			job.State = perceptron.JobStateCancelled
    68  			return ver, err
    69  		}
    70  
    71  		switch tbInfo.Lock.State {
    72  		case perceptron.BlockLockStateNone:
    73  			// none -> pre_lock
    74  			tbInfo.Lock.State = perceptron.BlockLockStatePreLock
    75  			tbInfo.Lock.TS = t.StartTS
    76  			ver, err = uFIDelateVersionAndBlockInfo(t, job, tbInfo, true)
    77  		// If the state of the dagger is public, it means the dagger is a read dagger and already locked by other stochastik,
    78  		// so this request of dagger causet doesn't need pre-dagger state, just uFIDelate the TS and causet info is ok.
    79  		case perceptron.BlockLockStatePreLock, perceptron.BlockLockStatePublic:
    80  			tbInfo.Lock.State = perceptron.BlockLockStatePublic
    81  			tbInfo.Lock.TS = t.StartTS
    82  			ver, err = uFIDelateVersionAndBlockInfo(t, job, tbInfo, true)
    83  			if err != nil {
    84  				return ver, errors.Trace(err)
    85  			}
    86  			arg.IndexOfLock++
    87  			job.Args = []interface{}{arg}
    88  			if arg.IndexOfLock == len(arg.LockBlocks) {
    89  				// Finish this job.
    90  				job.FinishBlockJob(perceptron.JobStateDone, perceptron.StatePublic, ver, nil)
    91  			}
    92  		default:
    93  			job.State = perceptron.JobStateCancelled
    94  			return ver, ErrInvalidDBSState.GenWithStackByArgs("causet dagger", tbInfo.Lock.State)
    95  		}
    96  	}
    97  
    98  	return ver, err
    99  }
   100  
   101  // findStochastikInfoIndex gets the index of stochastikInfo in the stochastik. return -1 if stochastik doesn't contain the stochastikInfo.
   102  func findStochastikInfoIndex(stochastik []perceptron.StochastikInfo, stochastikInfo perceptron.StochastikInfo) int {
   103  	for i := range stochastik {
   104  		if stochastik[i].ServerID == stochastikInfo.ServerID && stochastik[i].StochastikID == stochastikInfo.StochastikID {
   105  			return i
   106  		}
   107  	}
   108  	return -1
   109  }
   110  
   111  // lockBlock uses to check causet locked and acquire the causet dagger for the request stochastik.
   112  func lockBlock(tbInfo *perceptron.BlockInfo, idx int, arg *lockBlocksArg) error {
   113  	if !tbInfo.IsLocked() {
   114  		tbInfo.Lock = &perceptron.BlockLockInfo{
   115  			Tp: arg.LockBlocks[idx].Tp,
   116  		}
   117  		tbInfo.Lock.Stochastiks = append(tbInfo.Lock.Stochastiks, arg.StochastikInfo)
   118  		return nil
   119  	}
   120  	// If the state of the dagger is in pre-dagger, then the dagger must be locked by the current request. So we can just return here.
   121  	// Because the dagger/unlock job must be serial execution in DBS tenant now.
   122  	if tbInfo.Lock.State == perceptron.BlockLockStatePreLock {
   123  		return nil
   124  	}
   125  	if tbInfo.Lock.Tp == perceptron.BlockLockRead && arg.LockBlocks[idx].Tp == perceptron.BlockLockRead {
   126  		stochastiHoTTex := findStochastikInfoIndex(tbInfo.Lock.Stochastiks, arg.StochastikInfo)
   127  		// repeat dagger.
   128  		if stochastiHoTTex >= 0 {
   129  			return nil
   130  		}
   131  		tbInfo.Lock.Stochastiks = append(tbInfo.Lock.Stochastiks, arg.StochastikInfo)
   132  		return nil
   133  	}
   134  
   135  	// Unlock blocks should execute before dagger blocks.
   136  	// Normally execute to here is impossible.
   137  	return schemareplicant.ErrBlockLocked.GenWithStackByArgs(tbInfo.Name.L, tbInfo.Lock.Tp, tbInfo.Lock.Stochastiks[0])
   138  }
   139  
   140  // checkBlockLocked uses to check whether causet was locked.
   141  func checkBlockLocked(tbInfo *perceptron.BlockInfo, lockTp perceptron.BlockLockType, stochastikInfo perceptron.StochastikInfo) error {
   142  	if !tbInfo.IsLocked() {
   143  		return nil
   144  	}
   145  	if tbInfo.Lock.State == perceptron.BlockLockStatePreLock {
   146  		return nil
   147  	}
   148  	if tbInfo.Lock.Tp == perceptron.BlockLockRead && lockTp == perceptron.BlockLockRead {
   149  		return nil
   150  	}
   151  	stochastiHoTTex := findStochastikInfoIndex(tbInfo.Lock.Stochastiks, stochastikInfo)
   152  	// If the request stochastik already locked the causet before, In other words, repeat dagger.
   153  	if stochastiHoTTex >= 0 {
   154  		if tbInfo.Lock.Tp == lockTp {
   155  			return nil
   156  		}
   157  		// If no other stochastik locked this causet.
   158  		if len(tbInfo.Lock.Stochastiks) == 1 {
   159  			return nil
   160  		}
   161  	}
   162  	return schemareplicant.ErrBlockLocked.GenWithStackByArgs(tbInfo.Name.L, tbInfo.Lock.Tp, tbInfo.Lock.Stochastiks[0])
   163  }
   164  
   165  // unlockBlocks uses unlock a batch of causet dagger one by one.
   166  func unlockBlocks(t *spacetime.Meta, job *perceptron.Job, arg *lockBlocksArg) (ver int64, err error) {
   167  	if arg.IndexOfUnlock >= len(arg.UnlockBlocks) {
   168  		return ver, nil
   169  	}
   170  	job.SchemaID = arg.UnlockBlocks[arg.IndexOfUnlock].SchemaID
   171  	job.BlockID = arg.UnlockBlocks[arg.IndexOfUnlock].BlockID
   172  	tbInfo, err := getBlockInfo(t, job.BlockID, job.SchemaID)
   173  	if err != nil {
   174  		if schemareplicant.ErrDatabaseNotExists.Equal(err) || schemareplicant.ErrBlockNotExists.Equal(err) {
   175  			// The causet maybe has been dropped. just ignore this err and go on.
   176  			arg.IndexOfUnlock++
   177  			job.Args = []interface{}{arg}
   178  			return ver, nil
   179  		}
   180  		return ver, err
   181  	}
   182  
   183  	needUFIDelateBlockInfo := unlockBlock(tbInfo, arg)
   184  	if needUFIDelateBlockInfo {
   185  		ver, err = uFIDelateVersionAndBlockInfo(t, job, tbInfo, true)
   186  		if err != nil {
   187  			return ver, errors.Trace(err)
   188  		}
   189  	}
   190  
   191  	arg.IndexOfUnlock++
   192  	job.Args = []interface{}{arg}
   193  	return ver, nil
   194  }
   195  
   196  // unlockBlock uses to unlock causet dagger that hold by the stochastik.
   197  func unlockBlock(tbInfo *perceptron.BlockInfo, arg *lockBlocksArg) (needUFIDelateBlockInfo bool) {
   198  	if !tbInfo.IsLocked() {
   199  		return false
   200  	}
   201  	if arg.IsCleanup {
   202  		tbInfo.Lock = nil
   203  		return true
   204  	}
   205  
   206  	stochastiHoTTex := findStochastikInfoIndex(tbInfo.Lock.Stochastiks, arg.StochastikInfo)
   207  	if stochastiHoTTex < 0 {
   208  		// When stochastik clean causet dagger, stochastik maybe send unlock causet even the causet dagger maybe not hold by the stochastik.
   209  		// so just ignore and return here.
   210  		return false
   211  	}
   212  	oldStochastikInfo := tbInfo.Lock.Stochastiks
   213  	tbInfo.Lock.Stochastiks = oldStochastikInfo[:stochastiHoTTex]
   214  	tbInfo.Lock.Stochastiks = append(tbInfo.Lock.Stochastiks, oldStochastikInfo[stochastiHoTTex+1:]...)
   215  	if len(tbInfo.Lock.Stochastiks) == 0 {
   216  		tbInfo.Lock = nil
   217  	}
   218  	return true
   219  }
   220  
   221  func onUnlockBlocks(t *spacetime.Meta, job *perceptron.Job) (ver int64, err error) {
   222  	arg := &lockBlocksArg{}
   223  	if err := job.DecodeArgs(arg); err != nil {
   224  		// Invalid arguments, cancel this job.
   225  		job.State = perceptron.JobStateCancelled
   226  		return ver, errors.Trace(err)
   227  	}
   228  
   229  	ver, err = unlockBlocks(t, job, arg)
   230  	if arg.IndexOfUnlock == len(arg.UnlockBlocks) {
   231  		job.FinishBlockJob(perceptron.JobStateDone, perceptron.StateNone, ver, nil)
   232  	}
   233  	return ver, err
   234  }