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 }