github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/dbs/dbs_worker.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  	"fmt"
    19  	"sync"
    20  	"sync/atomic"
    21  	"time"
    22  
    23  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    24  	"github.com/whtcorpsinc/BerolinaSQL/terror"
    25  	"github.com/whtcorpsinc/errors"
    26  	"github.com/whtcorpsinc/failpoint"
    27  	pumpcli "github.com/whtcorpsinc/milevadb-tools/milevadb-binlog/pump_client"
    28  	"github.com/whtcorpsinc/milevadb/dbs/soliton"
    29  	"github.com/whtcorpsinc/milevadb/ekv"
    30  	"github.com/whtcorpsinc/milevadb/metrics"
    31  	milevadbutil "github.com/whtcorpsinc/milevadb/soliton"
    32  	"github.com/whtcorpsinc/milevadb/soliton/admin"
    33  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    34  	"github.com/whtcorpsinc/milevadb/spacetime"
    35  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    36  	"github.com/whtcorpsinc/milevadb/stochastikctx/binloginfo"
    37  	"github.com/whtcorpsinc/milevadb/stochastikctx/variable"
    38  	"go.uber.org/zap"
    39  )
    40  
    41  var (
    42  	// RunWorker indicates if this MilevaDB server starts DBS worker and can run DBS job.
    43  	RunWorker = true
    44  	// dbsWorkerID is used for generating the next DBS worker ID.
    45  	dbsWorkerID = int32(0)
    46  	// WaitTimeWhenErrorOccurred is waiting interval when processing DBS jobs encounter errors.
    47  	WaitTimeWhenErrorOccurred = int64(1 * time.Second)
    48  )
    49  
    50  // GetWaitTimeWhenErrorOccurred return waiting interval when processing DBS jobs encounter errors.
    51  func GetWaitTimeWhenErrorOccurred() time.Duration {
    52  	return time.Duration(atomic.LoadInt64(&WaitTimeWhenErrorOccurred))
    53  }
    54  
    55  // SetWaitTimeWhenErrorOccurred uFIDelate waiting interval when processing DBS jobs encounter errors.
    56  func SetWaitTimeWhenErrorOccurred(dur time.Duration) {
    57  	atomic.StoreInt64(&WaitTimeWhenErrorOccurred, int64(dur))
    58  }
    59  
    60  type workerType byte
    61  
    62  const (
    63  	// generalWorker is the worker who handles all DBS memexs except “add index”.
    64  	generalWorker workerType = 0
    65  	// addIdxWorker is the worker who handles the operation of adding indexes.
    66  	addIdxWorker workerType = 1
    67  	// waitDependencyJobInterval is the interval when the dependency job doesn't be done.
    68  	waitDependencyJobInterval = 200 * time.Millisecond
    69  	// noneDependencyJob means a job has no dependency-job.
    70  	noneDependencyJob = 0
    71  )
    72  
    73  // worker is used for handling DBS jobs.
    74  // Now we have two HoTTs of workers.
    75  type worker struct {
    76  	id       int32
    77  	tp       workerType
    78  	dbsJobCh chan struct{}
    79  	ctx      context.Context
    80  	wg       sync.WaitGroup
    81  
    82  	sessPool        *stochastikPool // sessPool is used to new stochastik to execute ALLEGROALLEGROSQL in dbs package.
    83  	reorgCtx        *reorgCtx       // reorgCtx is used for reorganization.
    84  	delRangeManager delRangeManager
    85  	logCtx          context.Context
    86  }
    87  
    88  func newWorker(ctx context.Context, tp workerType, sessPool *stochastikPool, delRangeMgr delRangeManager) *worker {
    89  	worker := &worker{
    90  		id:              atomic.AddInt32(&dbsWorkerID, 1),
    91  		tp:              tp,
    92  		dbsJobCh:        make(chan struct{}, 1),
    93  		ctx:             ctx,
    94  		reorgCtx:        &reorgCtx{notifyCancelReorgJob: 0},
    95  		sessPool:        sessPool,
    96  		delRangeManager: delRangeMgr,
    97  	}
    98  
    99  	worker.logCtx = logutil.WithKeyValue(context.Background(), "worker", worker.String())
   100  	return worker
   101  }
   102  
   103  func (w *worker) typeStr() string {
   104  	var str string
   105  	switch w.tp {
   106  	case generalWorker:
   107  		str = "general"
   108  	case addIdxWorker:
   109  		str = perceptron.AddIndexStr
   110  	default:
   111  		str = "unknown"
   112  	}
   113  	return str
   114  }
   115  
   116  func (w *worker) String() string {
   117  	return fmt.Sprintf("worker %d, tp %s", w.id, w.typeStr())
   118  }
   119  
   120  func (w *worker) close() {
   121  	startTime := time.Now()
   122  	w.wg.Wait()
   123  	logutil.Logger(w.logCtx).Info("[dbs] DBS worker closed", zap.Duration("take time", time.Since(startTime)))
   124  }
   125  
   126  // start is used for async online schemaReplicant changing, it will try to become the tenant firstly,
   127  // then wait or pull the job queue to handle a schemaReplicant change job.
   128  func (w *worker) start(d *dbsCtx) {
   129  	logutil.Logger(w.logCtx).Info("[dbs] start DBS worker")
   130  	defer w.wg.Done()
   131  	defer milevadbutil.Recover(
   132  		metrics.LabelDBSWorker,
   133  		fmt.Sprintf("DBS ID %s, %s start", d.uuid, w),
   134  		nil, true,
   135  	)
   136  
   137  	// We use 4 * lease time to check tenant's timeout, so here, we will uFIDelate tenant's status
   138  	// every 2 * lease time. If lease is 0, we will use default 1s.
   139  	// But we use etcd to speed up, normally it takes less than 1s now, so we use 1s as the max value.
   140  	checkTime := chooseLeaseTime(2*d.lease, 1*time.Second)
   141  
   142  	ticker := time.NewTicker(checkTime)
   143  	defer ticker.Stop()
   144  
   145  	for {
   146  		select {
   147  		case <-ticker.C:
   148  			logutil.Logger(w.logCtx).Debug("[dbs] wait to check DBS status again", zap.Duration("interval", checkTime))
   149  		case <-w.dbsJobCh:
   150  		case <-w.ctx.Done():
   151  			return
   152  		}
   153  
   154  		err := w.handleDBSJobQueue(d)
   155  		if err != nil {
   156  			logutil.Logger(w.logCtx).Error("[dbs] handle DBS job failed", zap.Error(err))
   157  		}
   158  	}
   159  }
   160  
   161  func asyncNotify(ch chan struct{}) {
   162  	select {
   163  	case ch <- struct{}{}:
   164  	default:
   165  	}
   166  }
   167  
   168  // buildJobDependence sets the curjob's dependency-ID.
   169  // The dependency-job's ID must less than the current job's ID, and we need the largest one in the list.
   170  func buildJobDependence(t *spacetime.Meta, curJob *perceptron.Job) error {
   171  	// Jobs in the same queue are ordered. If we want to find a job's dependency-job, we need to look for
   172  	// it from the other queue. So if the job is "CausetActionAddIndex" job, we need find its dependency-job from DefaultJobList.
   173  	var jobs []*perceptron.Job
   174  	var err error
   175  	switch curJob.Type {
   176  	case perceptron.CausetActionAddIndex, perceptron.CausetActionAddPrimaryKey:
   177  		jobs, err = t.GetAllDBSJobsInQueue(spacetime.DefaultJobListKey)
   178  	default:
   179  		jobs, err = t.GetAllDBSJobsInQueue(spacetime.AddIndexJobListKey)
   180  	}
   181  	if err != nil {
   182  		return errors.Trace(err)
   183  	}
   184  
   185  	for _, job := range jobs {
   186  		if curJob.ID < job.ID {
   187  			continue
   188  		}
   189  		isDependent, err := curJob.IsDependentOn(job)
   190  		if err != nil {
   191  			return errors.Trace(err)
   192  		}
   193  		if isDependent {
   194  			logutil.BgLogger().Info("[dbs] current DBS job depends on other job", zap.String("currentJob", curJob.String()), zap.String("dependentJob", job.String()))
   195  			curJob.DependencyID = job.ID
   196  			break
   197  		}
   198  	}
   199  	return nil
   200  }
   201  
   202  func (d *dbs) limitDBSJobs() {
   203  	defer d.wg.Done()
   204  	defer milevadbutil.Recover(metrics.LabelDBS, "limitDBSJobs", nil, true)
   205  
   206  	tasks := make([]*limitJobTask, 0, batchAddingJobs)
   207  	for {
   208  		select {
   209  		case task := <-d.limitJobCh:
   210  			tasks = tasks[:0]
   211  			jobLen := len(d.limitJobCh)
   212  			tasks = append(tasks, task)
   213  			for i := 0; i < jobLen; i++ {
   214  				tasks = append(tasks, <-d.limitJobCh)
   215  			}
   216  			d.addBatchDBSJobs(tasks)
   217  		case <-d.ctx.Done():
   218  			return
   219  		}
   220  	}
   221  }
   222  
   223  // addBatchDBSJobs gets global job IDs and puts the DBS jobs in the DBS queue.
   224  func (d *dbs) addBatchDBSJobs(tasks []*limitJobTask) {
   225  	startTime := time.Now()
   226  	err := ekv.RunInNewTxn(d.causetstore, true, func(txn ekv.Transaction) error {
   227  		t := spacetime.NewMeta(txn)
   228  		ids, err := t.GenGlobalIDs(len(tasks))
   229  		if err != nil {
   230  			return errors.Trace(err)
   231  		}
   232  		for i, task := range tasks {
   233  			job := task.job
   234  			job.Version = currentVersion
   235  			job.StartTS = txn.StartTS()
   236  			job.ID = ids[i]
   237  			if err = buildJobDependence(t, job); err != nil {
   238  				return errors.Trace(err)
   239  			}
   240  
   241  			if job.Type == perceptron.CausetActionAddIndex || job.Type == perceptron.CausetActionAddPrimaryKey {
   242  				jobKey := spacetime.AddIndexJobListKey
   243  				err = t.EnQueueDBSJob(job, jobKey)
   244  			} else {
   245  				err = t.EnQueueDBSJob(job)
   246  			}
   247  			if err != nil {
   248  				return errors.Trace(err)
   249  			}
   250  		}
   251  		return nil
   252  	})
   253  	var jobs string
   254  	for _, task := range tasks {
   255  		task.err <- err
   256  		jobs += task.job.String() + "; "
   257  		metrics.DBSWorkerHistogram.WithLabelValues(metrics.WorkerAddDBSJob, task.job.Type.String(),
   258  			metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds())
   259  	}
   260  	logutil.BgLogger().Info("[dbs] add DBS jobs", zap.Int("batch count", len(tasks)), zap.String("jobs", jobs))
   261  }
   262  
   263  // getHistoryDBSJob gets a DBS job with job's ID from history queue.
   264  func (d *dbs) getHistoryDBSJob(id int64) (*perceptron.Job, error) {
   265  	var job *perceptron.Job
   266  
   267  	err := ekv.RunInNewTxn(d.causetstore, false, func(txn ekv.Transaction) error {
   268  		t := spacetime.NewMeta(txn)
   269  		var err1 error
   270  		job, err1 = t.GetHistoryDBSJob(id)
   271  		return errors.Trace(err1)
   272  	})
   273  
   274  	return job, errors.Trace(err)
   275  }
   276  
   277  // getFirstDBSJob gets the first DBS job form DBS queue.
   278  func (w *worker) getFirstDBSJob(t *spacetime.Meta) (*perceptron.Job, error) {
   279  	job, err := t.GetDBSJobByIdx(0)
   280  	return job, errors.Trace(err)
   281  }
   282  
   283  // handleUFIDelateJobError handles the too large DBS job.
   284  func (w *worker) handleUFIDelateJobError(t *spacetime.Meta, job *perceptron.Job, err error) error {
   285  	if err == nil {
   286  		return nil
   287  	}
   288  	if ekv.ErrEntryTooLarge.Equal(err) {
   289  		logutil.Logger(w.logCtx).Warn("[dbs] uFIDelate DBS job failed", zap.String("job", job.String()), zap.Error(err))
   290  		// Reduce this txn entry size.
   291  		job.BinlogInfo.Clean()
   292  		job.Error = toTError(err)
   293  		job.ErrorCount++
   294  		job.SchemaState = perceptron.StateNone
   295  		job.State = perceptron.JobStateCancelled
   296  		err = w.finishDBSJob(t, job)
   297  	}
   298  	return errors.Trace(err)
   299  }
   300  
   301  // uFIDelateDBSJob uFIDelates the DBS job information.
   302  // Every time we enter another state except final state, we must call this function.
   303  func (w *worker) uFIDelateDBSJob(t *spacetime.Meta, job *perceptron.Job, meetErr bool) error {
   304  	failpoint.Inject("mockErrEntrySizeTooLarge", func(val failpoint.Value) {
   305  		if val.(bool) {
   306  			failpoint.Return(ekv.ErrEntryTooLarge)
   307  		}
   308  	})
   309  	uFIDelateRawArgs := true
   310  	// If there is an error when running job and the RawArgs hasn't been decoded by DecodeArgs,
   311  	// so we shouldn't replace RawArgs with the marshaling Args.
   312  	if meetErr && (job.RawArgs != nil && job.Args == nil) {
   313  		logutil.Logger(w.logCtx).Info("[dbs] meet something wrong before uFIDelate DBS job, shouldn't uFIDelate raw args",
   314  			zap.String("job", job.String()))
   315  		uFIDelateRawArgs = false
   316  	}
   317  	return errors.Trace(t.UFIDelateDBSJob(0, job, uFIDelateRawArgs))
   318  }
   319  
   320  func (w *worker) deleteRange(job *perceptron.Job) error {
   321  	var err error
   322  	if job.Version <= currentVersion {
   323  		err = w.delRangeManager.addDelRangeJob(job)
   324  	} else {
   325  		err = errInvalidDBSJobVersion.GenWithStackByArgs(job.Version, currentVersion)
   326  	}
   327  	return errors.Trace(err)
   328  }
   329  
   330  // finishDBSJob deletes the finished DBS job in the dbs queue and puts it to history queue.
   331  // If the DBS job need to handle in background, it will prepare a background job.
   332  func (w *worker) finishDBSJob(t *spacetime.Meta, job *perceptron.Job) (err error) {
   333  	startTime := time.Now()
   334  	defer func() {
   335  		metrics.DBSWorkerHistogram.WithLabelValues(metrics.WorkerFinishDBSJob, job.Type.String(), metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds())
   336  	}()
   337  
   338  	if !job.IsCancelled() {
   339  		switch job.Type {
   340  		case perceptron.CausetActionAddIndex, perceptron.CausetActionAddPrimaryKey:
   341  			if job.State != perceptron.JobStateRollbackDone {
   342  				break
   343  			}
   344  
   345  			// After rolling back an AddIndex operation, we need to use delete-range to delete the half-done index data.
   346  			err = w.deleteRange(job)
   347  		case perceptron.CausetActionDropSchema, perceptron.CausetActionDropBlock, perceptron.CausetActionTruncateBlock, perceptron.CausetActionDropIndex, perceptron.CausetActionDropPrimaryKey,
   348  			perceptron.CausetActionDropBlockPartition, perceptron.CausetActionTruncateBlockPartition, perceptron.CausetActionDropDeferredCauset, perceptron.CausetActionDropDeferredCausets, perceptron.CausetActionModifyDeferredCauset:
   349  			err = w.deleteRange(job)
   350  		}
   351  	}
   352  	switch job.Type {
   353  	case perceptron.CausetActionRecoverBlock:
   354  		err = finishRecoverBlock(w, t, job)
   355  	}
   356  	if err != nil {
   357  		return errors.Trace(err)
   358  	}
   359  
   360  	_, err = t.DeQueueDBSJob()
   361  	if err != nil {
   362  		return errors.Trace(err)
   363  	}
   364  
   365  	job.BinlogInfo.FinishedTS = t.StartTS
   366  	logutil.Logger(w.logCtx).Info("[dbs] finish DBS job", zap.String("job", job.String()))
   367  	uFIDelateRawArgs := true
   368  	if job.Type == perceptron.CausetActionAddPrimaryKey && !job.IsCancelled() {
   369  		// CausetActionAddPrimaryKey needs to check the warnings information in job.Args.
   370  		// Notice: warnings is used to support non-strict mode.
   371  		uFIDelateRawArgs = false
   372  	}
   373  	err = t.AddHistoryDBSJob(job, uFIDelateRawArgs)
   374  	return errors.Trace(err)
   375  }
   376  
   377  func finishRecoverBlock(w *worker, t *spacetime.Meta, job *perceptron.Job) error {
   378  	tbInfo := &perceptron.BlockInfo{}
   379  	var autoIncID, autoRandID, dropJobID, recoverBlockCheckFlag int64
   380  	var snapshotTS uint64
   381  	err := job.DecodeArgs(tbInfo, &autoIncID, &dropJobID, &snapshotTS, &recoverBlockCheckFlag, &autoRandID)
   382  	if err != nil {
   383  		return errors.Trace(err)
   384  	}
   385  	if recoverBlockCheckFlag == recoverBlockCheckFlagEnableGC {
   386  		err = enableGC(w)
   387  		if err != nil {
   388  			return errors.Trace(err)
   389  		}
   390  	}
   391  	return nil
   392  }
   393  
   394  func isDependencyJobDone(t *spacetime.Meta, job *perceptron.Job) (bool, error) {
   395  	if job.DependencyID == noneDependencyJob {
   396  		return true, nil
   397  	}
   398  
   399  	historyJob, err := t.GetHistoryDBSJob(job.DependencyID)
   400  	if err != nil {
   401  		return false, errors.Trace(err)
   402  	}
   403  	if historyJob == nil {
   404  		return false, nil
   405  	}
   406  	logutil.BgLogger().Info("[dbs] current DBS job dependent job is finished", zap.String("currentJob", job.String()), zap.Int64("dependentJobID", job.DependencyID))
   407  	job.DependencyID = noneDependencyJob
   408  	return true, nil
   409  }
   410  
   411  func newMetaWithQueueTp(txn ekv.Transaction, tp string) *spacetime.Meta {
   412  	if tp == perceptron.AddIndexStr || tp == perceptron.AddPrimaryKeyStr {
   413  		return spacetime.NewMeta(txn, spacetime.AddIndexJobListKey)
   414  	}
   415  	return spacetime.NewMeta(txn)
   416  }
   417  
   418  // handleDBSJobQueue handles DBS jobs in DBS Job queue.
   419  func (w *worker) handleDBSJobQueue(d *dbsCtx) error {
   420  	once := true
   421  	waitDependencyJobCnt := 0
   422  	for {
   423  		if isChanClosed(w.ctx.Done()) {
   424  			return nil
   425  		}
   426  
   427  		var (
   428  			job       *perceptron.Job
   429  			schemaVer int64
   430  			runJobErr error
   431  		)
   432  		waitTime := 2 * d.lease
   433  		err := ekv.RunInNewTxn(d.causetstore, false, func(txn ekv.Transaction) error {
   434  			// We are not tenant, return and retry checking later.
   435  			if !d.isTenant() {
   436  				return nil
   437  			}
   438  
   439  			var err error
   440  			t := newMetaWithQueueTp(txn, w.typeStr())
   441  			// We become the tenant. Get the first job and run it.
   442  			job, err = w.getFirstDBSJob(t)
   443  			if job == nil || err != nil {
   444  				return errors.Trace(err)
   445  			}
   446  			if isDone, err1 := isDependencyJobDone(t, job); err1 != nil || !isDone {
   447  				return errors.Trace(err1)
   448  			}
   449  
   450  			if once {
   451  				w.waitSchemaSynced(d, job, waitTime)
   452  				once = false
   453  				return nil
   454  			}
   455  
   456  			if job.IsDone() || job.IsRollbackDone() {
   457  				if !job.IsRollbackDone() {
   458  					job.State = perceptron.JobStateSynced
   459  				}
   460  				err = w.finishDBSJob(t, job)
   461  				return errors.Trace(err)
   462  			}
   463  
   464  			d.mu.RLock()
   465  			d.mu.hook.OnJobRunBefore(job)
   466  			d.mu.RUnlock()
   467  
   468  			// If running job meets error, we will save this error in job Error
   469  			// and retry later if the job is not cancelled.
   470  			schemaVer, runJobErr = w.runDBSJob(d, t, job)
   471  			if job.IsCancelled() {
   472  				txn.Reset()
   473  				err = w.finishDBSJob(t, job)
   474  				return errors.Trace(err)
   475  			}
   476  			if runJobErr != nil && !job.IsRollingback() && !job.IsRollbackDone() {
   477  				// If the running job meets an error
   478  				// and the job state is rolling back, it means that we have already handled this error.
   479  				// Some DBS jobs (such as adding indexes) may need to uFIDelate the causet info and the schemaReplicant version,
   480  				// then shouldn't discard the KV modification.
   481  				// And the job state is rollback done, it means the job was already finished, also shouldn't discard too.
   482  				// Otherwise, we should discard the KV modification when running job.
   483  				txn.Reset()
   484  				// If error happens after uFIDelateSchemaVersion(), then the schemaVer is uFIDelated.
   485  				// Result in the retry duration is up to 2 * lease.
   486  				schemaVer = 0
   487  			}
   488  			err = w.uFIDelateDBSJob(t, job, runJobErr != nil)
   489  			if err = w.handleUFIDelateJobError(t, job, err); err != nil {
   490  				return errors.Trace(err)
   491  			}
   492  			writeBinlog(d.binlogCli, txn, job)
   493  			return nil
   494  		})
   495  
   496  		if runJobErr != nil {
   497  			// wait a while to retry again. If we don't wait here, DBS will retry this job immediately,
   498  			// which may act like a deadlock.
   499  			logutil.Logger(w.logCtx).Info("[dbs] run DBS job failed, sleeps a while then retries it.",
   500  				zap.Duration("waitTime", GetWaitTimeWhenErrorOccurred()), zap.Error(runJobErr))
   501  			time.Sleep(GetWaitTimeWhenErrorOccurred())
   502  		}
   503  
   504  		if err != nil {
   505  			return errors.Trace(err)
   506  		} else if job == nil {
   507  			// No job now, return and retry getting later.
   508  			return nil
   509  		}
   510  		w.waitDependencyJobFinished(job, &waitDependencyJobCnt)
   511  
   512  		// Here means the job enters another state (delete only, write only, public, etc...) or is cancelled.
   513  		// If the job is done or still running or rolling back, we will wait 2 * lease time to guarantee other servers to uFIDelate
   514  		// the newest schemaReplicant.
   515  		ctx, cancel := context.WithTimeout(w.ctx, waitTime)
   516  		w.waitSchemaChanged(ctx, d, waitTime, schemaVer, job)
   517  		cancel()
   518  
   519  		d.mu.RLock()
   520  		d.mu.hook.OnJobUFIDelated(job)
   521  		d.mu.RUnlock()
   522  
   523  		if job.IsSynced() || job.IsCancelled() {
   524  			asyncNotify(d.dbsJobDoneCh)
   525  		}
   526  	}
   527  }
   528  
   529  func skipWriteBinlog(job *perceptron.Job) bool {
   530  	switch job.Type {
   531  	// CausetActionUFIDelateTiFlashReplicaStatus is a MilevaDB internal DBS,
   532  	// it's used to uFIDelate causet's TiFlash replica available status.
   533  	case perceptron.CausetActionUFIDelateTiFlashReplicaStatus:
   534  		return true
   535  	// It is done without modifying causet info, bin log is not needed
   536  	case perceptron.CausetActionAlterBlockAlterPartition:
   537  		return true
   538  	}
   539  
   540  	return false
   541  }
   542  
   543  func writeBinlog(binlogCli *pumpcli.PumpsClient, txn ekv.Transaction, job *perceptron.Job) {
   544  	if job.IsDone() || job.IsRollbackDone() ||
   545  		// When this defCausumn is in the "delete only" and "delete reorg" states, the binlog of "drop defCausumn" has not been written yet,
   546  		// but the defCausumn has been removed from the binlog of the write operation.
   547  		// So we add this binlog to enable downstream components to handle DML correctly in this schemaReplicant state.
   548  		((job.Type == perceptron.CausetActionDropDeferredCauset || job.Type == perceptron.CausetActionDropDeferredCausets) && job.SchemaState == perceptron.StateDeleteOnly) {
   549  		if skipWriteBinlog(job) {
   550  			return
   551  		}
   552  		binloginfo.SetDBSBinlog(binlogCli, txn, job.ID, int32(job.SchemaState), job.Query)
   553  	}
   554  }
   555  
   556  // waitDependencyJobFinished waits for the dependency-job to be finished.
   557  // If the dependency job isn't finished yet, we'd better wait a moment.
   558  func (w *worker) waitDependencyJobFinished(job *perceptron.Job, cnt *int) {
   559  	if job.DependencyID != noneDependencyJob {
   560  		intervalCnt := int(3 * time.Second / waitDependencyJobInterval)
   561  		if *cnt%intervalCnt == 0 {
   562  			logutil.Logger(w.logCtx).Info("[dbs] DBS job need to wait dependent job, sleeps a while, then retries it.",
   563  				zap.Int64("jobID", job.ID),
   564  				zap.Int64("dependentJobID", job.DependencyID),
   565  				zap.Duration("waitTime", waitDependencyJobInterval))
   566  		}
   567  		time.Sleep(waitDependencyJobInterval)
   568  		*cnt++
   569  	} else {
   570  		*cnt = 0
   571  	}
   572  }
   573  
   574  func chooseLeaseTime(t, max time.Duration) time.Duration {
   575  	if t == 0 || t > max {
   576  		return max
   577  	}
   578  	return t
   579  }
   580  
   581  // runDBSJob runs a DBS job. It returns the current schemaReplicant version in this transaction and the error.
   582  func (w *worker) runDBSJob(d *dbsCtx, t *spacetime.Meta, job *perceptron.Job) (ver int64, err error) {
   583  	defer milevadbutil.Recover(metrics.LabelDBSWorker, fmt.Sprintf("%s runDBSJob", w),
   584  		func() {
   585  			// If run DBS job panic, just cancel the DBS jobs.
   586  			job.State = perceptron.JobStateCancelling
   587  		}, false)
   588  
   589  	// Mock for run dbs job panic.
   590  	failpoint.Inject("mockPanicInRunDBSJob", func(val failpoint.Value) {})
   591  
   592  	logutil.Logger(w.logCtx).Info("[dbs] run DBS job", zap.String("job", job.String()))
   593  	timeStart := time.Now()
   594  	defer func() {
   595  		metrics.DBSWorkerHistogram.WithLabelValues(metrics.WorkerRunDBSJob, job.Type.String(), metrics.RetLabel(err)).Observe(time.Since(timeStart).Seconds())
   596  	}()
   597  	if job.IsFinished() {
   598  		return
   599  	}
   600  	// The cause of this job state is that the job is cancelled by client.
   601  	if job.IsCancelling() {
   602  		return convertJob2RollbackJob(w, d, t, job)
   603  	}
   604  
   605  	if !job.IsRollingback() && !job.IsCancelling() {
   606  		job.State = perceptron.JobStateRunning
   607  	}
   608  
   609  	switch job.Type {
   610  	case perceptron.CausetActionCreateSchema:
   611  		ver, err = onCreateSchema(d, t, job)
   612  	case perceptron.CausetActionModifySchemaCharsetAndDefCauslate:
   613  		ver, err = onModifySchemaCharsetAndDefCauslate(t, job)
   614  	case perceptron.CausetActionDropSchema:
   615  		ver, err = onDropSchema(t, job)
   616  	case perceptron.CausetActionCreateBlock:
   617  		ver, err = onCreateBlock(d, t, job)
   618  	case perceptron.CausetActionRepairBlock:
   619  		ver, err = onRepairBlock(d, t, job)
   620  	case perceptron.CausetActionCreateView:
   621  		ver, err = onCreateView(d, t, job)
   622  	case perceptron.CausetActionDropBlock, perceptron.CausetActionDropView, perceptron.CausetActionDropSequence:
   623  		ver, err = onDropBlockOrView(t, job)
   624  	case perceptron.CausetActionDropBlockPartition:
   625  		ver, err = onDropBlockPartition(t, job)
   626  	case perceptron.CausetActionTruncateBlockPartition:
   627  		ver, err = onTruncateBlockPartition(d, t, job)
   628  	case perceptron.CausetActionExchangeBlockPartition:
   629  		ver, err = w.onExchangeBlockPartition(d, t, job)
   630  	case perceptron.CausetActionAddDeferredCauset:
   631  		ver, err = onAddDeferredCauset(d, t, job)
   632  	case perceptron.CausetActionAddDeferredCausets:
   633  		ver, err = onAddDeferredCausets(d, t, job)
   634  	case perceptron.CausetActionDropDeferredCauset:
   635  		ver, err = onDropDeferredCauset(t, job)
   636  	case perceptron.CausetActionDropDeferredCausets:
   637  		ver, err = onDropDeferredCausets(t, job)
   638  	case perceptron.CausetActionModifyDeferredCauset:
   639  		ver, err = w.onModifyDeferredCauset(d, t, job)
   640  	case perceptron.CausetActionSetDefaultValue:
   641  		ver, err = onSetDefaultValue(t, job)
   642  	case perceptron.CausetActionAddIndex:
   643  		ver, err = w.onCreateIndex(d, t, job, false)
   644  	case perceptron.CausetActionAddPrimaryKey:
   645  		ver, err = w.onCreateIndex(d, t, job, true)
   646  	case perceptron.CausetActionDropIndex, perceptron.CausetActionDropPrimaryKey:
   647  		ver, err = onDropIndex(t, job)
   648  	case perceptron.CausetActionRenameIndex:
   649  		ver, err = onRenameIndex(t, job)
   650  	case perceptron.CausetActionAddForeignKey:
   651  		ver, err = onCreateForeignKey(t, job)
   652  	case perceptron.CausetActionDropForeignKey:
   653  		ver, err = onDropForeignKey(t, job)
   654  	case perceptron.CausetActionTruncateBlock:
   655  		ver, err = onTruncateBlock(d, t, job)
   656  	case perceptron.CausetActionRebaseAutoID:
   657  		ver, err = onRebaseRowIDType(d.causetstore, t, job)
   658  	case perceptron.CausetActionRebaseAutoRandomBase:
   659  		ver, err = onRebaseAutoRandomType(d.causetstore, t, job)
   660  	case perceptron.CausetActionRenameBlock:
   661  		ver, err = onRenameBlock(d, t, job)
   662  	case perceptron.CausetActionShardRowID:
   663  		ver, err = w.onShardRowID(d, t, job)
   664  	case perceptron.CausetActionModifyBlockComment:
   665  		ver, err = onModifyBlockComment(t, job)
   666  	case perceptron.CausetActionModifyBlockAutoIdCache:
   667  		ver, err = onModifyBlockAutoIDCache(t, job)
   668  	case perceptron.CausetActionAddBlockPartition:
   669  		ver, err = onAddBlockPartition(d, t, job)
   670  	case perceptron.CausetActionModifyBlockCharsetAndDefCauslate:
   671  		ver, err = onModifyBlockCharsetAndDefCauslate(t, job)
   672  	case perceptron.CausetActionRecoverBlock:
   673  		ver, err = w.onRecoverBlock(d, t, job)
   674  	case perceptron.CausetActionLockBlock:
   675  		ver, err = onLockBlocks(t, job)
   676  	case perceptron.CausetActionUnlockBlock:
   677  		ver, err = onUnlockBlocks(t, job)
   678  	case perceptron.CausetActionSetTiFlashReplica:
   679  		ver, err = w.onSetBlockFlashReplica(t, job)
   680  	case perceptron.CausetActionUFIDelateTiFlashReplicaStatus:
   681  		ver, err = onUFIDelateFlashReplicaStatus(t, job)
   682  	case perceptron.CausetActionCreateSequence:
   683  		ver, err = onCreateSequence(d, t, job)
   684  	case perceptron.CausetActionAlterIndexVisibility:
   685  		ver, err = onAlterIndexVisibility(t, job)
   686  	case perceptron.CausetActionAlterBlockAlterPartition:
   687  		ver, err = onAlterBlockPartition(t, job)
   688  	default:
   689  		// Invalid job, cancel it.
   690  		job.State = perceptron.JobStateCancelled
   691  		err = errInvalidDBSJob.GenWithStack("invalid dbs job type: %v", job.Type)
   692  	}
   693  
   694  	// Save errors in job, so that others can know errors happened.
   695  	if err != nil {
   696  		job.Error = toTError(err)
   697  		job.ErrorCount++
   698  
   699  		// If job is cancelled, we shouldn't return an error and shouldn't load DBS variables.
   700  		if job.State == perceptron.JobStateCancelled {
   701  			logutil.Logger(w.logCtx).Info("[dbs] DBS job is cancelled normally", zap.Error(err))
   702  			return ver, nil
   703  		}
   704  		logutil.Logger(w.logCtx).Error("[dbs] run DBS job error", zap.Error(err))
   705  
   706  		// Load global dbs variables.
   707  		if err1 := loadDBSVars(w); err1 != nil {
   708  			logutil.Logger(w.logCtx).Error("[dbs] load DBS global variable failed", zap.Error(err1))
   709  		}
   710  		// Check error limit to avoid falling into an infinite loop.
   711  		if job.ErrorCount > variable.GetDBSErrorCountLimit() && job.State == perceptron.JobStateRunning && admin.IsJobRollbackable(job) {
   712  			logutil.Logger(w.logCtx).Warn("[dbs] DBS job error count exceed the limit, cancelling it now", zap.Int64("jobID", job.ID), zap.Int64("errorCountLimit", variable.GetDBSErrorCountLimit()))
   713  			job.State = perceptron.JobStateCancelling
   714  		}
   715  	}
   716  	return
   717  }
   718  
   719  func loadDBSVars(w *worker) error {
   720  	// Get stochastikctx from context resource pool.
   721  	var ctx stochastikctx.Context
   722  	ctx, err := w.sessPool.get()
   723  	if err != nil {
   724  		return errors.Trace(err)
   725  	}
   726  	defer w.sessPool.put(ctx)
   727  	return soliton.LoadDBSVars(ctx)
   728  }
   729  
   730  func toTError(err error) *terror.Error {
   731  	originErr := errors.Cause(err)
   732  	tErr, ok := originErr.(*terror.Error)
   733  	if ok {
   734  		return tErr
   735  	}
   736  
   737  	// TODO: Add the error code.
   738  	return terror.ClassDBS.Synthesize(terror.CodeUnknown, err.Error())
   739  }
   740  
   741  // waitSchemaChanged waits for the completion of uFIDelating all servers' schemaReplicant. In order to make sure that happens,
   742  // we wait 2 * lease time.
   743  func (w *worker) waitSchemaChanged(ctx context.Context, d *dbsCtx, waitTime time.Duration, latestSchemaVersion int64, job *perceptron.Job) {
   744  	if !job.IsRunning() && !job.IsRollingback() && !job.IsDone() && !job.IsRollbackDone() {
   745  		return
   746  	}
   747  	if waitTime == 0 {
   748  		return
   749  	}
   750  
   751  	timeStart := time.Now()
   752  	var err error
   753  	defer func() {
   754  		metrics.DBSWorkerHistogram.WithLabelValues(metrics.WorkerWaitSchemaChanged, job.Type.String(), metrics.RetLabel(err)).Observe(time.Since(timeStart).Seconds())
   755  	}()
   756  
   757  	if latestSchemaVersion == 0 {
   758  		logutil.Logger(w.logCtx).Info("[dbs] schemaReplicant version doesn't change")
   759  		return
   760  	}
   761  
   762  	err = d.schemaSyncer.TenantUFIDelateGlobalVersion(ctx, latestSchemaVersion)
   763  	if err != nil {
   764  		logutil.Logger(w.logCtx).Info("[dbs] uFIDelate latest schemaReplicant version failed", zap.Int64("ver", latestSchemaVersion), zap.Error(err))
   765  		if terror.ErrorEqual(err, context.DeadlineExceeded) {
   766  			// If err is context.DeadlineExceeded, it means waitTime(2 * lease) is elapsed. So all the schemas are synced by ticker.
   767  			// There is no need to use etcd to sync. The function returns directly.
   768  			return
   769  		}
   770  	}
   771  
   772  	// TenantCheckAllVersions returns only when context is timeout(2 * lease) or all MilevaDB schemas are synced.
   773  	err = d.schemaSyncer.TenantCheckAllVersions(ctx, latestSchemaVersion)
   774  	if err != nil {
   775  		logutil.Logger(w.logCtx).Info("[dbs] wait latest schemaReplicant version to deadline", zap.Int64("ver", latestSchemaVersion), zap.Error(err))
   776  		if terror.ErrorEqual(err, context.DeadlineExceeded) {
   777  			return
   778  		}
   779  		d.schemaSyncer.NotifyCleanExpiredPaths()
   780  		// Wait until timeout.
   781  		select {
   782  		case <-ctx.Done():
   783  			return
   784  		}
   785  	}
   786  	logutil.Logger(w.logCtx).Info("[dbs] wait latest schemaReplicant version changed",
   787  		zap.Int64("ver", latestSchemaVersion),
   788  		zap.Duration("take time", time.Since(timeStart)),
   789  		zap.String("job", job.String()))
   790  }
   791  
   792  // waitSchemaSynced handles the following situation:
   793  // If the job enters a new state, and the worker crashs when it's in the process of waiting for 2 * lease time,
   794  // Then the worker restarts quickly, we may run the job immediately again,
   795  // but in this case we don't wait enough 2 * lease time to let other servers uFIDelate the schemaReplicant.
   796  // So here we get the latest schemaReplicant version to make sure all servers' schemaReplicant version uFIDelate to the latest schemaReplicant version
   797  // in a cluster, or to wait for 2 * lease time.
   798  func (w *worker) waitSchemaSynced(d *dbsCtx, job *perceptron.Job, waitTime time.Duration) {
   799  	if !job.IsRunning() && !job.IsRollingback() && !job.IsDone() && !job.IsRollbackDone() {
   800  		return
   801  	}
   802  	ctx, cancelFunc := context.WithTimeout(w.ctx, waitTime)
   803  	defer cancelFunc()
   804  
   805  	latestSchemaVersion, err := d.schemaSyncer.MustGetGlobalVersion(ctx)
   806  	if err != nil {
   807  		logutil.Logger(w.logCtx).Warn("[dbs] get global version failed", zap.Error(err))
   808  		return
   809  	}
   810  	w.waitSchemaChanged(ctx, d, waitTime, latestSchemaVersion, job)
   811  }
   812  
   813  // uFIDelateSchemaVersion increments the schemaReplicant version by 1 and sets SchemaDiff.
   814  func uFIDelateSchemaVersion(t *spacetime.Meta, job *perceptron.Job) (int64, error) {
   815  	schemaVersion, err := t.GenSchemaVersion()
   816  	if err != nil {
   817  		return 0, errors.Trace(err)
   818  	}
   819  	diff := &perceptron.SchemaDiff{
   820  		Version:  schemaVersion,
   821  		Type:     job.Type,
   822  		SchemaID: job.SchemaID,
   823  	}
   824  	switch job.Type {
   825  	case perceptron.CausetActionTruncateBlock:
   826  		// Truncate causet has two causet ID, should be handled differently.
   827  		err = job.DecodeArgs(&diff.BlockID)
   828  		if err != nil {
   829  			return 0, errors.Trace(err)
   830  		}
   831  		diff.OldBlockID = job.BlockID
   832  	case perceptron.CausetActionCreateView:
   833  		tbInfo := &perceptron.BlockInfo{}
   834  		var orReplace bool
   835  		var oldTbInfoID int64
   836  		if err := job.DecodeArgs(tbInfo, &orReplace, &oldTbInfoID); err != nil {
   837  			return 0, errors.Trace(err)
   838  		}
   839  		// When the memex is "create or replace view " and we need to drop the old view,
   840  		// it has two causet IDs and should be handled differently.
   841  		if oldTbInfoID > 0 && orReplace {
   842  			diff.OldBlockID = oldTbInfoID
   843  		}
   844  		diff.BlockID = tbInfo.ID
   845  	case perceptron.CausetActionRenameBlock:
   846  		err = job.DecodeArgs(&diff.OldSchemaID)
   847  		if err != nil {
   848  			return 0, errors.Trace(err)
   849  		}
   850  		diff.BlockID = job.BlockID
   851  	case perceptron.CausetActionExchangeBlockPartition:
   852  		var (
   853  			ptSchemaID int64
   854  			ptBlockID  int64
   855  		)
   856  		err = job.DecodeArgs(&diff.BlockID, &ptSchemaID, &ptBlockID)
   857  		if err != nil {
   858  			return 0, errors.Trace(err)
   859  		}
   860  		diff.OldBlockID = job.BlockID
   861  		affects := make([]*perceptron.AffectedOption, 1)
   862  		affects[0] = &perceptron.AffectedOption{
   863  			SchemaID:   ptSchemaID,
   864  			BlockID:    ptBlockID,
   865  			OldBlockID: ptBlockID,
   866  		}
   867  		diff.AffectedOpts = affects
   868  	default:
   869  		diff.BlockID = job.BlockID
   870  	}
   871  	err = t.SetSchemaDiff(diff)
   872  	return schemaVersion, errors.Trace(err)
   873  }
   874  
   875  func isChanClosed(quitCh <-chan struct{}) bool {
   876  	select {
   877  	case <-quitCh:
   878  		return true
   879  	default:
   880  		return false
   881  	}
   882  }