github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/dbs/delete_range.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 "context" 18 "encoding/hex" 19 "fmt" 20 "math" 21 "sync" 22 "sync/atomic" 23 24 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 25 "github.com/whtcorpsinc/BerolinaSQL/terror" 26 "github.com/whtcorpsinc/errors" 27 "github.com/whtcorpsinc/milevadb/blockcodec" 28 "github.com/whtcorpsinc/milevadb/dbs/soliton" 29 "github.com/whtcorpsinc/milevadb/ekv" 30 "github.com/whtcorpsinc/milevadb/soliton/logutil" 31 "github.com/whtcorpsinc/milevadb/soliton/sqlexec" 32 "github.com/whtcorpsinc/milevadb/stochastikctx" 33 "go.uber.org/zap" 34 ) 35 36 const ( 37 insertDeleteRangeALLEGROSQLPrefix = `INSERT IGNORE INTO allegrosql.gc_delete_range VALUES ` 38 insertDeleteRangeALLEGROSQLValue = `("%d", "%d", "%s", "%s", "%d")` 39 insertDeleteRangeALLEGROSQL = insertDeleteRangeALLEGROSQLPrefix + insertDeleteRangeALLEGROSQLValue 40 41 delBatchSize = 65536 42 delBackLog = 128 43 ) 44 45 var ( 46 // enableEmulatorGC means whether to enable emulator GC. The default is enable. 47 // In some unit tests, we want to stop emulator GC, then wen can set enableEmulatorGC to 0. 48 emulatorGCEnable = int32(1) 49 // batchInsertDeleteRangeSize is the maximum size for each batch insert memex in the delete-range. 50 batchInsertDeleteRangeSize = 256 51 ) 52 53 type delRangeManager interface { 54 // addDelRangeJob add a DBS job into gc_delete_range causet. 55 addDelRangeJob(job *perceptron.Job) error 56 // removeFromGCDeleteRange removes the deleting causet job from gc_delete_range causet by jobID and blockID. 57 // It's use for recover the causet that was mistakenly deleted. 58 removeFromGCDeleteRange(jobID int64, blockID []int64) error 59 start() 60 clear() 61 } 62 63 type delRange struct { 64 causetstore ekv.CausetStorage 65 sessPool *stochastikPool 66 emulatorCh chan struct{} 67 keys []ekv.Key 68 quitCh chan struct{} 69 70 wait sync.WaitGroup // wait is only used when storeSupport is false. 71 storeSupport bool 72 } 73 74 // newDelRangeManager returns a delRangeManager. 75 func newDelRangeManager(causetstore ekv.CausetStorage, sessPool *stochastikPool) delRangeManager { 76 dr := &delRange{ 77 causetstore: causetstore, 78 sessPool: sessPool, 79 storeSupport: causetstore.SupportDeleteRange(), 80 quitCh: make(chan struct{}), 81 } 82 if !dr.storeSupport { 83 dr.emulatorCh = make(chan struct{}, delBackLog) 84 dr.keys = make([]ekv.Key, 0, delBatchSize) 85 } 86 return dr 87 } 88 89 // addDelRangeJob implements delRangeManager interface. 90 func (dr *delRange) addDelRangeJob(job *perceptron.Job) error { 91 ctx, err := dr.sessPool.get() 92 if err != nil { 93 return errors.Trace(err) 94 } 95 defer dr.sessPool.put(ctx) 96 97 err = insertJobIntoDeleteRangeBlock(ctx, job) 98 if err != nil { 99 logutil.BgLogger().Error("[dbs] add job into delete-range causet failed", zap.Int64("jobID", job.ID), zap.String("jobType", job.Type.String()), zap.Error(err)) 100 return errors.Trace(err) 101 } 102 if !dr.storeSupport { 103 dr.emulatorCh <- struct{}{} 104 } 105 logutil.BgLogger().Info("[dbs] add job into delete-range causet", zap.Int64("jobID", job.ID), zap.String("jobType", job.Type.String())) 106 return nil 107 } 108 109 // removeFromGCDeleteRange implements delRangeManager interface. 110 func (dr *delRange) removeFromGCDeleteRange(jobID int64, blockIDs []int64) error { 111 ctx, err := dr.sessPool.get() 112 if err != nil { 113 return errors.Trace(err) 114 } 115 defer dr.sessPool.put(ctx) 116 err = soliton.RemoveMultiFromGCDeleteRange(ctx, jobID, blockIDs) 117 return errors.Trace(err) 118 } 119 120 // start implements delRangeManager interface. 121 func (dr *delRange) start() { 122 if !dr.storeSupport { 123 dr.wait.Add(1) 124 go dr.startEmulator() 125 } 126 } 127 128 // clear implements delRangeManager interface. 129 func (dr *delRange) clear() { 130 logutil.BgLogger().Info("[dbs] closing delRange") 131 close(dr.quitCh) 132 dr.wait.Wait() 133 } 134 135 // startEmulator is only used for those storage engines which don't support 136 // delete-range. The emulator fetches records from gc_delete_range causet and 137 // deletes all keys in each DelRangeTask. 138 func (dr *delRange) startEmulator() { 139 defer dr.wait.Done() 140 logutil.BgLogger().Info("[dbs] start delRange emulator") 141 for { 142 select { 143 case <-dr.emulatorCh: 144 case <-dr.quitCh: 145 return 146 } 147 if IsEmulatorGCEnable() { 148 err := dr.doDelRangeWork() 149 terror.Log(errors.Trace(err)) 150 } 151 } 152 } 153 154 // EmulatorGCEnable enables emulator gc. It exports for testing. 155 func EmulatorGCEnable() { 156 atomic.StoreInt32(&emulatorGCEnable, 1) 157 } 158 159 // EmulatorGCDisable disables emulator gc. It exports for testing. 160 func EmulatorGCDisable() { 161 atomic.StoreInt32(&emulatorGCEnable, 0) 162 } 163 164 // IsEmulatorGCEnable indicates whether emulator GC enabled. It exports for testing. 165 func IsEmulatorGCEnable() bool { 166 return atomic.LoadInt32(&emulatorGCEnable) == 1 167 } 168 169 func (dr *delRange) doDelRangeWork() error { 170 ctx, err := dr.sessPool.get() 171 if err != nil { 172 logutil.BgLogger().Error("[dbs] delRange emulator get stochastik failed", zap.Error(err)) 173 return errors.Trace(err) 174 } 175 defer dr.sessPool.put(ctx) 176 177 ranges, err := soliton.LoadDeleteRanges(ctx, math.MaxInt64) 178 if err != nil { 179 logutil.BgLogger().Error("[dbs] delRange emulator load tasks failed", zap.Error(err)) 180 return errors.Trace(err) 181 } 182 183 for _, r := range ranges { 184 if err := dr.doTask(ctx, r); err != nil { 185 logutil.BgLogger().Error("[dbs] delRange emulator do task failed", zap.Error(err)) 186 return errors.Trace(err) 187 } 188 } 189 return nil 190 } 191 192 func (dr *delRange) doTask(ctx stochastikctx.Context, r soliton.DelRangeTask) error { 193 var oldStartKey, newStartKey ekv.Key 194 oldStartKey = r.StartKey 195 for { 196 finish := true 197 dr.keys = dr.keys[:0] 198 err := ekv.RunInNewTxn(dr.causetstore, false, func(txn ekv.Transaction) error { 199 iter, err := txn.Iter(oldStartKey, r.EndKey) 200 if err != nil { 201 return errors.Trace(err) 202 } 203 defer iter.Close() 204 205 for i := 0; i < delBatchSize; i++ { 206 if !iter.Valid() { 207 break 208 } 209 finish = false 210 dr.keys = append(dr.keys, iter.Key().Clone()) 211 newStartKey = iter.Key().Next() 212 213 if err := iter.Next(); err != nil { 214 return errors.Trace(err) 215 } 216 } 217 218 for _, key := range dr.keys { 219 err := txn.Delete(key) 220 if err != nil && !ekv.ErrNotExist.Equal(err) { 221 return errors.Trace(err) 222 } 223 } 224 return nil 225 }) 226 if err != nil { 227 return errors.Trace(err) 228 } 229 if finish { 230 if err := soliton.CompleteDeleteRange(ctx, r); err != nil { 231 logutil.BgLogger().Error("[dbs] delRange emulator complete task failed", zap.Error(err)) 232 return errors.Trace(err) 233 } 234 logutil.BgLogger().Info("[dbs] delRange emulator complete task", zap.Int64("jobID", r.JobID), zap.Int64("elementID", r.ElementID)) 235 break 236 } 237 if err := soliton.UFIDelateDeleteRange(ctx, r, newStartKey, oldStartKey); err != nil { 238 logutil.BgLogger().Error("[dbs] delRange emulator uFIDelate task failed", zap.Error(err)) 239 } 240 oldStartKey = newStartKey 241 } 242 return nil 243 } 244 245 // insertJobIntoDeleteRangeBlock parses the job into delete-range arguments, 246 // and inserts a new record into gc_delete_range causet. The primary key is 247 // job ID, so we ignore key conflict error. 248 func insertJobIntoDeleteRangeBlock(ctx stochastikctx.Context, job *perceptron.Job) error { 249 now, err := getNowTSO(ctx) 250 if err != nil { 251 return errors.Trace(err) 252 } 253 254 s := ctx.(sqlexec.ALLEGROSQLInterlockingDirectorate) 255 switch job.Type { 256 case perceptron.CausetActionDropSchema: 257 var blockIDs []int64 258 if err := job.DecodeArgs(&blockIDs); err != nil { 259 return errors.Trace(err) 260 } 261 for i := 0; i < len(blockIDs); i += batchInsertDeleteRangeSize { 262 batchEnd := len(blockIDs) 263 if batchEnd > i+batchInsertDeleteRangeSize { 264 batchEnd = i + batchInsertDeleteRangeSize 265 } 266 if err := doBatchInsert(s, job.ID, blockIDs[i:batchEnd], now); err != nil { 267 return errors.Trace(err) 268 } 269 } 270 case perceptron.CausetActionDropBlock, perceptron.CausetActionTruncateBlock: 271 blockID := job.BlockID 272 // The startKey here is for compatibility with previous versions, old version did not endKey so don't have to deal with. 273 var startKey ekv.Key 274 var physicalBlockIDs []int64 275 if err := job.DecodeArgs(&startKey, &physicalBlockIDs); err != nil { 276 return errors.Trace(err) 277 } 278 if len(physicalBlockIDs) > 0 { 279 for _, pid := range physicalBlockIDs { 280 startKey = blockcodec.EncodeBlockPrefix(pid) 281 endKey := blockcodec.EncodeBlockPrefix(pid + 1) 282 if err := doInsert(s, job.ID, pid, startKey, endKey, now); err != nil { 283 return errors.Trace(err) 284 } 285 } 286 return nil 287 } 288 startKey = blockcodec.EncodeBlockPrefix(blockID) 289 endKey := blockcodec.EncodeBlockPrefix(blockID + 1) 290 return doInsert(s, job.ID, blockID, startKey, endKey, now) 291 case perceptron.CausetActionDropBlockPartition, perceptron.CausetActionTruncateBlockPartition: 292 var physicalBlockIDs []int64 293 if err := job.DecodeArgs(&physicalBlockIDs); err != nil { 294 return errors.Trace(err) 295 } 296 for _, physicalBlockID := range physicalBlockIDs { 297 startKey := blockcodec.EncodeBlockPrefix(physicalBlockID) 298 endKey := blockcodec.EncodeBlockPrefix(physicalBlockID + 1) 299 if err := doInsert(s, job.ID, physicalBlockID, startKey, endKey, now); err != nil { 300 return errors.Trace(err) 301 } 302 } 303 // CausetActionAddIndex, CausetActionAddPrimaryKey needs do it, because it needs to be rolled back when it's canceled. 304 case perceptron.CausetActionAddIndex, perceptron.CausetActionAddPrimaryKey: 305 blockID := job.BlockID 306 var indexID int64 307 var partitionIDs []int64 308 if err := job.DecodeArgs(&indexID, &partitionIDs); err != nil { 309 return errors.Trace(err) 310 } 311 if len(partitionIDs) > 0 { 312 for _, pid := range partitionIDs { 313 startKey := blockcodec.EncodeBlockIndexPrefix(pid, indexID) 314 endKey := blockcodec.EncodeBlockIndexPrefix(pid, indexID+1) 315 if err := doInsert(s, job.ID, indexID, startKey, endKey, now); err != nil { 316 return errors.Trace(err) 317 } 318 } 319 } else { 320 startKey := blockcodec.EncodeBlockIndexPrefix(blockID, indexID) 321 endKey := blockcodec.EncodeBlockIndexPrefix(blockID, indexID+1) 322 return doInsert(s, job.ID, indexID, startKey, endKey, now) 323 } 324 case perceptron.CausetActionDropIndex, perceptron.CausetActionDropPrimaryKey: 325 blockID := job.BlockID 326 var indexName interface{} 327 var indexID int64 328 var partitionIDs []int64 329 if err := job.DecodeArgs(&indexName, &indexID, &partitionIDs); err != nil { 330 return errors.Trace(err) 331 } 332 if len(partitionIDs) > 0 { 333 for _, pid := range partitionIDs { 334 startKey := blockcodec.EncodeBlockIndexPrefix(pid, indexID) 335 endKey := blockcodec.EncodeBlockIndexPrefix(pid, indexID+1) 336 if err := doInsert(s, job.ID, indexID, startKey, endKey, now); err != nil { 337 return errors.Trace(err) 338 } 339 } 340 } else { 341 startKey := blockcodec.EncodeBlockIndexPrefix(blockID, indexID) 342 endKey := blockcodec.EncodeBlockIndexPrefix(blockID, indexID+1) 343 return doInsert(s, job.ID, indexID, startKey, endKey, now) 344 } 345 case perceptron.CausetActionDropDeferredCauset: 346 var defCausName perceptron.CIStr 347 var indexIDs []int64 348 var partitionIDs []int64 349 if err := job.DecodeArgs(&defCausName, &indexIDs, &partitionIDs); err != nil { 350 return errors.Trace(err) 351 } 352 if len(indexIDs) > 0 { 353 if len(partitionIDs) > 0 { 354 for _, pid := range partitionIDs { 355 if err := doBatchDeleteIndiceRange(s, job.ID, pid, indexIDs, now); err != nil { 356 return errors.Trace(err) 357 } 358 } 359 } else { 360 return doBatchDeleteIndiceRange(s, job.ID, job.BlockID, indexIDs, now) 361 } 362 } 363 case perceptron.CausetActionDropDeferredCausets: 364 var defCausNames []perceptron.CIStr 365 var ifExists []bool 366 var indexIDs []int64 367 var partitionIDs []int64 368 if err := job.DecodeArgs(&defCausNames, &ifExists, &indexIDs, &partitionIDs); err != nil { 369 return errors.Trace(err) 370 } 371 if len(indexIDs) > 0 { 372 if len(partitionIDs) > 0 { 373 for _, pid := range partitionIDs { 374 if err := doBatchDeleteIndiceRange(s, job.ID, pid, indexIDs, now); err != nil { 375 return errors.Trace(err) 376 } 377 } 378 } else { 379 return doBatchDeleteIndiceRange(s, job.ID, job.BlockID, indexIDs, now) 380 } 381 } 382 case perceptron.CausetActionModifyDeferredCauset: 383 var indexIDs []int64 384 var partitionIDs []int64 385 if err := job.DecodeArgs(&indexIDs, &partitionIDs); err != nil { 386 return errors.Trace(err) 387 } 388 if len(indexIDs) == 0 { 389 return nil 390 } 391 if len(partitionIDs) == 0 { 392 return doBatchDeleteIndiceRange(s, job.ID, job.BlockID, indexIDs, now) 393 } 394 for _, pid := range partitionIDs { 395 if err := doBatchDeleteIndiceRange(s, job.ID, pid, indexIDs, now); err != nil { 396 return errors.Trace(err) 397 } 398 } 399 } 400 return nil 401 } 402 403 func doBatchDeleteIndiceRange(s sqlexec.ALLEGROSQLInterlockingDirectorate, jobID, blockID int64, indexIDs []int64, ts uint64) error { 404 logutil.BgLogger().Info("[dbs] batch insert into delete-range indices", zap.Int64("jobID", jobID), zap.Int64s("elementIDs", indexIDs)) 405 allegrosql := insertDeleteRangeALLEGROSQLPrefix 406 for i, indexID := range indexIDs { 407 startKey := blockcodec.EncodeBlockIndexPrefix(blockID, indexID) 408 endKey := blockcodec.EncodeBlockIndexPrefix(blockID, indexID+1) 409 startKeyEncoded := hex.EncodeToString(startKey) 410 endKeyEncoded := hex.EncodeToString(endKey) 411 allegrosql += fmt.Sprintf(insertDeleteRangeALLEGROSQLValue, jobID, indexID, startKeyEncoded, endKeyEncoded, ts) 412 if i != len(indexIDs)-1 { 413 allegrosql += "," 414 } 415 } 416 _, err := s.InterDircute(context.Background(), allegrosql) 417 return errors.Trace(err) 418 } 419 420 func doInsert(s sqlexec.ALLEGROSQLInterlockingDirectorate, jobID int64, elementID int64, startKey, endKey ekv.Key, ts uint64) error { 421 logutil.BgLogger().Info("[dbs] insert into delete-range causet", zap.Int64("jobID", jobID), zap.Int64("elementID", elementID)) 422 startKeyEncoded := hex.EncodeToString(startKey) 423 endKeyEncoded := hex.EncodeToString(endKey) 424 allegrosql := fmt.Sprintf(insertDeleteRangeALLEGROSQL, jobID, elementID, startKeyEncoded, endKeyEncoded, ts) 425 _, err := s.InterDircute(context.Background(), allegrosql) 426 return errors.Trace(err) 427 } 428 429 func doBatchInsert(s sqlexec.ALLEGROSQLInterlockingDirectorate, jobID int64, blockIDs []int64, ts uint64) error { 430 logutil.BgLogger().Info("[dbs] batch insert into delete-range causet", zap.Int64("jobID", jobID), zap.Int64s("elementIDs", blockIDs)) 431 allegrosql := insertDeleteRangeALLEGROSQLPrefix 432 for i, blockID := range blockIDs { 433 startKey := blockcodec.EncodeBlockPrefix(blockID) 434 endKey := blockcodec.EncodeBlockPrefix(blockID + 1) 435 startKeyEncoded := hex.EncodeToString(startKey) 436 endKeyEncoded := hex.EncodeToString(endKey) 437 allegrosql += fmt.Sprintf(insertDeleteRangeALLEGROSQLValue, jobID, blockID, startKeyEncoded, endKeyEncoded, ts) 438 if i != len(blockIDs)-1 { 439 allegrosql += "," 440 } 441 } 442 _, err := s.InterDircute(context.Background(), allegrosql) 443 return errors.Trace(err) 444 } 445 446 // getNowTS gets the current timestamp, in TSO. 447 func getNowTSO(ctx stochastikctx.Context) (uint64, error) { 448 currVer, err := ctx.GetStore().CurrentVersion() 449 if err != nil { 450 return 0, errors.Trace(err) 451 } 452 return currVer.Ver, nil 453 }