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  }