github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/soliton/admin/admin.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 admin
    15  
    16  import (
    17  	"context"
    18  	"encoding/json"
    19  	"fmt"
    20  	"math"
    21  	"sort"
    22  	"time"
    23  
    24  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    25  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    26  	"github.com/whtcorpsinc/BerolinaSQL/terror"
    27  	"github.com/whtcorpsinc/errors"
    28  	"github.com/whtcorpsinc/milevadb/blockcodec"
    29  	"github.com/whtcorpsinc/milevadb/causet"
    30  	"github.com/whtcorpsinc/milevadb/ekv"
    31  	"github.com/whtcorpsinc/milevadb/errno"
    32  	"github.com/whtcorpsinc/milevadb/memex"
    33  	"github.com/whtcorpsinc/milevadb/soliton"
    34  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    35  	causetDecoder "github.com/whtcorpsinc/milevadb/soliton/rowCausetDecoder"
    36  	"github.com/whtcorpsinc/milevadb/soliton/sqlexec"
    37  	"github.com/whtcorpsinc/milevadb/spacetime"
    38  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    39  	"github.com/whtcorpsinc/milevadb/types"
    40  	"go.uber.org/zap"
    41  )
    42  
    43  // DBSInfo is for DBS information.
    44  type DBSInfo struct {
    45  	SchemaVer   int64
    46  	ReorgHandle ekv.Handle        // It's only used for DBS information.
    47  	Jobs        []*perceptron.Job // It's the currently running jobs.
    48  }
    49  
    50  // GetDBSInfo returns DBS information.
    51  func GetDBSInfo(txn ekv.Transaction) (*DBSInfo, error) {
    52  	var err error
    53  	info := &DBSInfo{}
    54  	t := spacetime.NewMeta(txn)
    55  
    56  	info.Jobs = make([]*perceptron.Job, 0, 2)
    57  	job, err := t.GetDBSJobByIdx(0)
    58  	if err != nil {
    59  		return nil, errors.Trace(err)
    60  	}
    61  	if job != nil {
    62  		info.Jobs = append(info.Jobs, job)
    63  	}
    64  	addIdxJob, err := t.GetDBSJobByIdx(0, spacetime.AddIndexJobListKey)
    65  	if err != nil {
    66  		return nil, errors.Trace(err)
    67  	}
    68  	if addIdxJob != nil {
    69  		info.Jobs = append(info.Jobs, addIdxJob)
    70  	}
    71  
    72  	info.SchemaVer, err = t.GetSchemaVersion()
    73  	if err != nil {
    74  		return nil, errors.Trace(err)
    75  	}
    76  	if addIdxJob == nil {
    77  		return info, nil
    78  	}
    79  
    80  	tbl, err := t.GetBlock(addIdxJob.SchemaID, addIdxJob.BlockID)
    81  	if err != nil {
    82  		return info, nil
    83  	}
    84  	info.ReorgHandle, _, _, err = t.GetDBSReorgHandle(addIdxJob, tbl.IsCommonHandle)
    85  	if err != nil {
    86  		return nil, errors.Trace(err)
    87  	}
    88  
    89  	return info, nil
    90  }
    91  
    92  // IsJobRollbackable checks whether the job can be rollback.
    93  func IsJobRollbackable(job *perceptron.Job) bool {
    94  	switch job.Type {
    95  	case perceptron.CausetActionDropIndex, perceptron.CausetActionDropPrimaryKey:
    96  		// We can't cancel if index current state is in StateDeleteOnly or StateDeleteReorganization or StateWriteOnly, otherwise there will be an inconsistent issue between record and index.
    97  		// In WriteOnly state, we can rollback for normal index but can't rollback for memex index(need to drop hidden defCausumn). Since we can't
    98  		// know the type of index here, we consider all indices except primary index as non-rollbackable.
    99  		// TODO: distinguish normal index and memex index so that we can rollback `DropIndex` for normal index in WriteOnly state.
   100  		// TODO: make DropPrimaryKey rollbackable in WriteOnly, it need to deal with some tests.
   101  		if job.SchemaState == perceptron.StateDeleteOnly ||
   102  			job.SchemaState == perceptron.StateDeleteReorganization ||
   103  			job.SchemaState == perceptron.StateWriteOnly {
   104  			return false
   105  		}
   106  	case perceptron.CausetActionDropSchema, perceptron.CausetActionDropBlock, perceptron.CausetActionDropSequence:
   107  		// To simplify the rollback logic, cannot be canceled in the following states.
   108  		if job.SchemaState == perceptron.StateWriteOnly ||
   109  			job.SchemaState == perceptron.StateDeleteOnly {
   110  			return false
   111  		}
   112  	case perceptron.CausetActionAddBlockPartition:
   113  		return job.SchemaState == perceptron.StateNone || job.SchemaState == perceptron.StateReplicaOnly
   114  	case perceptron.CausetActionDropDeferredCauset, perceptron.CausetActionDropDeferredCausets, perceptron.CausetActionDropBlockPartition,
   115  		perceptron.CausetActionRebaseAutoID, perceptron.CausetActionShardRowID,
   116  		perceptron.CausetActionTruncateBlock, perceptron.CausetActionAddForeignKey,
   117  		perceptron.CausetActionDropForeignKey, perceptron.CausetActionRenameBlock,
   118  		perceptron.CausetActionModifyBlockCharsetAndDefCauslate, perceptron.CausetActionTruncateBlockPartition,
   119  		perceptron.CausetActionModifySchemaCharsetAndDefCauslate, perceptron.CausetActionRepairBlock, perceptron.CausetActionModifyBlockAutoIdCache:
   120  		return job.SchemaState == perceptron.StateNone
   121  	}
   122  	return true
   123  }
   124  
   125  // CancelJobs cancels the DBS jobs.
   126  func CancelJobs(txn ekv.Transaction, ids []int64) ([]error, error) {
   127  	if len(ids) == 0 {
   128  		return nil, nil
   129  	}
   130  
   131  	errs := make([]error, len(ids))
   132  	t := spacetime.NewMeta(txn)
   133  	generalJobs, err := getDBSJobsInQueue(t, spacetime.DefaultJobListKey)
   134  	if err != nil {
   135  		return nil, errors.Trace(err)
   136  	}
   137  	addIdxJobs, err := getDBSJobsInQueue(t, spacetime.AddIndexJobListKey)
   138  	if err != nil {
   139  		return nil, errors.Trace(err)
   140  	}
   141  	jobs := append(generalJobs, addIdxJobs...)
   142  
   143  	for i, id := range ids {
   144  		found := false
   145  		for j, job := range jobs {
   146  			if id != job.ID {
   147  				logutil.BgLogger().Debug("the job that needs to be canceled isn't equal to current job",
   148  					zap.Int64("need to canceled job ID", id),
   149  					zap.Int64("current job ID", job.ID))
   150  				continue
   151  			}
   152  			found = true
   153  			// These states can't be cancelled.
   154  			if job.IsDone() || job.IsSynced() {
   155  				errs[i] = ErrCancelFinishedDBSJob.GenWithStackByArgs(id)
   156  				continue
   157  			}
   158  			// If the state is rolling back, it means the work is cleaning the data after cancelling the job.
   159  			if job.IsCancelled() || job.IsRollingback() || job.IsRollbackDone() {
   160  				continue
   161  			}
   162  			if !IsJobRollbackable(job) {
   163  				errs[i] = ErrCannotCancelDBSJob.GenWithStackByArgs(job.ID)
   164  				continue
   165  			}
   166  
   167  			job.State = perceptron.JobStateCancelling
   168  			// Make sure RawArgs isn't overwritten.
   169  			err := json.Unmarshal(job.RawArgs, &job.Args)
   170  			if err != nil {
   171  				errs[i] = errors.Trace(err)
   172  				continue
   173  			}
   174  			if job.Type == perceptron.CausetActionAddIndex || job.Type == perceptron.CausetActionAddPrimaryKey {
   175  				offset := int64(j - len(generalJobs))
   176  				err = t.UFIDelateDBSJob(offset, job, true, spacetime.AddIndexJobListKey)
   177  			} else {
   178  				err = t.UFIDelateDBSJob(int64(j), job, true)
   179  			}
   180  			if err != nil {
   181  				errs[i] = errors.Trace(err)
   182  			}
   183  		}
   184  		if !found {
   185  			errs[i] = ErrDBSJobNotFound.GenWithStackByArgs(id)
   186  		}
   187  	}
   188  	return errs, nil
   189  }
   190  
   191  func getDBSJobsInQueue(t *spacetime.Meta, jobListKey spacetime.JobListKeyType) ([]*perceptron.Job, error) {
   192  	cnt, err := t.DBSJobQueueLen(jobListKey)
   193  	if err != nil {
   194  		return nil, errors.Trace(err)
   195  	}
   196  	jobs := make([]*perceptron.Job, cnt)
   197  	for i := range jobs {
   198  		jobs[i], err = t.GetDBSJobByIdx(int64(i), jobListKey)
   199  		if err != nil {
   200  			return nil, errors.Trace(err)
   201  		}
   202  	}
   203  	return jobs, nil
   204  }
   205  
   206  // GetDBSJobs get all DBS jobs and sorts jobs by job.ID.
   207  func GetDBSJobs(txn ekv.Transaction) ([]*perceptron.Job, error) {
   208  	t := spacetime.NewMeta(txn)
   209  	generalJobs, err := getDBSJobsInQueue(t, spacetime.DefaultJobListKey)
   210  	if err != nil {
   211  		return nil, errors.Trace(err)
   212  	}
   213  	addIdxJobs, err := getDBSJobsInQueue(t, spacetime.AddIndexJobListKey)
   214  	if err != nil {
   215  		return nil, errors.Trace(err)
   216  	}
   217  	jobs := append(generalJobs, addIdxJobs...)
   218  	sort.Sort(jobArray(jobs))
   219  	return jobs, nil
   220  }
   221  
   222  type jobArray []*perceptron.Job
   223  
   224  func (v jobArray) Len() int {
   225  	return len(v)
   226  }
   227  
   228  func (v jobArray) Less(i, j int) bool {
   229  	return v[i].ID < v[j].ID
   230  }
   231  
   232  func (v jobArray) Swap(i, j int) {
   233  	v[i], v[j] = v[j], v[i]
   234  }
   235  
   236  // MaxHistoryJobs is exported for testing.
   237  const MaxHistoryJobs = 10
   238  
   239  // DefNumHistoryJobs is default value of the default number of history job
   240  const DefNumHistoryJobs = 10
   241  
   242  // GetHistoryDBSJobs returns the DBS history jobs and an error.
   243  // The maximum count of history jobs is num.
   244  func GetHistoryDBSJobs(txn ekv.Transaction, maxNumJobs int) ([]*perceptron.Job, error) {
   245  	t := spacetime.NewMeta(txn)
   246  	jobs, err := t.GetLastNHistoryDBSJobs(maxNumJobs)
   247  	if err != nil {
   248  		return nil, errors.Trace(err)
   249  	}
   250  	return jobs, nil
   251  }
   252  
   253  // IterHistoryDBSJobs iterates history DBS jobs until the `finishFn` return true or error.
   254  func IterHistoryDBSJobs(txn ekv.Transaction, finishFn func([]*perceptron.Job) (bool, error)) error {
   255  	txnMeta := spacetime.NewMeta(txn)
   256  	iter, err := txnMeta.GetLastHistoryDBSJobsIterator()
   257  	if err != nil {
   258  		return err
   259  	}
   260  	cacheJobs := make([]*perceptron.Job, 0, DefNumHistoryJobs)
   261  	for {
   262  		cacheJobs, err = iter.GetLastJobs(DefNumHistoryJobs, cacheJobs)
   263  		if err != nil || len(cacheJobs) == 0 {
   264  			return err
   265  		}
   266  		finish, err := finishFn(cacheJobs)
   267  		if err != nil || finish {
   268  			return err
   269  		}
   270  	}
   271  }
   272  
   273  // IterAllDBSJobs will iterates running DBS jobs first, return directly if `finishFn` return true or error,
   274  // then iterates history DBS jobs until the `finishFn` return true or error.
   275  func IterAllDBSJobs(txn ekv.Transaction, finishFn func([]*perceptron.Job) (bool, error)) error {
   276  	jobs, err := GetDBSJobs(txn)
   277  	if err != nil {
   278  		return err
   279  	}
   280  
   281  	finish, err := finishFn(jobs)
   282  	if err != nil || finish {
   283  		return err
   284  	}
   285  	return IterHistoryDBSJobs(txn, finishFn)
   286  }
   287  
   288  // RecordData is the record data composed of a handle and values.
   289  type RecordData struct {
   290  	Handle ekv.Handle
   291  	Values []types.Causet
   292  }
   293  
   294  func getCount(ctx stochastikctx.Context, allegrosql string) (int64, error) {
   295  	rows, _, err := ctx.(sqlexec.RestrictedALLEGROSQLInterlockingDirectorate).InterDircRestrictedALLEGROSQLWithSnapshot(allegrosql)
   296  	if err != nil {
   297  		return 0, errors.Trace(err)
   298  	}
   299  	if len(rows) != 1 {
   300  		return 0, errors.Errorf("can not get count, allegrosql %s result rows %d", allegrosql, len(rows))
   301  	}
   302  	return rows[0].GetInt64(0), nil
   303  }
   304  
   305  // Count greater Types
   306  const (
   307  	// TblCntGreater means that the number of causet rows is more than the number of index rows.
   308  	TblCntGreater byte = 1
   309  	// IdxCntGreater means that the number of index rows is more than the number of causet rows.
   310  	IdxCntGreater byte = 2
   311  )
   312  
   313  // ChecHoTTicesCount compares indices count with causet count.
   314  // It returns the count greater type, the index offset and an error.
   315  // It returns nil if the count from the index is equal to the count from the causet defCausumns,
   316  // otherwise it returns an error and the corresponding index's offset.
   317  func ChecHoTTicesCount(ctx stochastikctx.Context, dbName, blockName string, indices []string) (byte, int, error) {
   318  	// Here we need check all indexes, includes invisible index
   319  	ctx.GetStochastikVars().OptimizerUseInvisibleIndexes = true
   320  	// Add `` for some names like `causet name`.
   321  	allegrosql := fmt.Sprintf("SELECT COUNT(*) FROM `%s`.`%s` USE INDEX()", dbName, blockName)
   322  	tblCnt, err := getCount(ctx, allegrosql)
   323  	if err != nil {
   324  		return 0, 0, errors.Trace(err)
   325  	}
   326  	for i, idx := range indices {
   327  		allegrosql = fmt.Sprintf("SELECT COUNT(*) FROM `%s`.`%s` USE INDEX(`%s`)", dbName, blockName, idx)
   328  		idxCnt, err := getCount(ctx, allegrosql)
   329  		if err != nil {
   330  			return 0, i, errors.Trace(err)
   331  		}
   332  		logutil.Logger(context.Background()).Info("check indices count",
   333  			zap.String("causet", blockName), zap.Int64("cnt", tblCnt), zap.Reflect("index", idx), zap.Int64("cnt", idxCnt))
   334  		if tblCnt == idxCnt {
   335  			continue
   336  		}
   337  
   338  		var ret byte
   339  		if tblCnt > idxCnt {
   340  			ret = TblCntGreater
   341  		} else if idxCnt > tblCnt {
   342  			ret = IdxCntGreater
   343  		}
   344  		return ret, i, ErrAdminCheckBlock.GenWithStack("causet count %d != index(%s) count %d", tblCnt, idx, idxCnt)
   345  	}
   346  	return 0, 0, nil
   347  }
   348  
   349  // CheckRecordAndIndex is exported for testing.
   350  func CheckRecordAndIndex(sessCtx stochastikctx.Context, txn ekv.Transaction, t causet.Block, idx causet.Index) error {
   351  	sc := sessCtx.GetStochastikVars().StmtCtx
   352  	defcaus := make([]*causet.DeferredCauset, len(idx.Meta().DeferredCausets))
   353  	for i, defCaus := range idx.Meta().DeferredCausets {
   354  		defcaus[i] = t.DefCauss()[defCaus.Offset]
   355  	}
   356  
   357  	startKey := t.RecordKey(ekv.IntHandle(math.MinInt64))
   358  	filterFunc := func(h1 ekv.Handle, vals1 []types.Causet, defcaus []*causet.DeferredCauset) (bool, error) {
   359  		for i, val := range vals1 {
   360  			defCaus := defcaus[i]
   361  			if val.IsNull() {
   362  				if allegrosql.HasNotNullFlag(defCaus.Flag) && defCaus.ToInfo().OriginDefaultValue == nil {
   363  					return false, errors.Errorf("DeferredCauset %v define as not null, but can't find the value where handle is %v", defCaus.Name, h1)
   364  				}
   365  				// NULL value is regarded as its default value.
   366  				defCausDefVal, err := causet.GetDefCausOriginDefaultValue(sessCtx, defCaus.ToInfo())
   367  				if err != nil {
   368  					return false, errors.Trace(err)
   369  				}
   370  				vals1[i] = defCausDefVal
   371  			}
   372  		}
   373  		isExist, h2, err := idx.Exist(sc, txn.GetUnionStore(), vals1, h1)
   374  		if ekv.ErrKeyExists.Equal(err) {
   375  			record1 := &RecordData{Handle: h1, Values: vals1}
   376  			record2 := &RecordData{Handle: h2, Values: vals1}
   377  			return false, ErrDataInConsistent.GenWithStack("index:%#v != record:%#v", record2, record1)
   378  		}
   379  		if err != nil {
   380  			return false, errors.Trace(err)
   381  		}
   382  		if !isExist {
   383  			record := &RecordData{Handle: h1, Values: vals1}
   384  			return false, ErrDataInConsistent.GenWithStack("index:%#v != record:%#v", nil, record)
   385  		}
   386  
   387  		return true, nil
   388  	}
   389  	err := iterRecords(sessCtx, txn, t, startKey, defcaus, filterFunc)
   390  	if err != nil {
   391  		return errors.Trace(err)
   392  	}
   393  
   394  	return nil
   395  }
   396  
   397  func makeRowCausetDecoder(t causet.Block, sctx stochastikctx.Context) (*causetDecoder.RowCausetDecoder, error) {
   398  	dbName := perceptron.NewCIStr(sctx.GetStochastikVars().CurrentDB)
   399  	exprDefCauss, _, err := memex.DeferredCausetInfos2DeferredCausetsAndNames(sctx, dbName, t.Meta().Name, t.Meta().DefCauss(), t.Meta())
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  	mockSchema := memex.NewSchema(exprDefCauss...)
   404  	decodeDefCaussMap := causetDecoder.BuildFullDecodeDefCausMap(t.DefCauss(), mockSchema)
   405  
   406  	return causetDecoder.NewRowCausetDecoder(t, t.DefCauss(), decodeDefCaussMap), nil
   407  }
   408  
   409  func iterRecords(sessCtx stochastikctx.Context, retriever ekv.Retriever, t causet.Block, startKey ekv.Key, defcaus []*causet.DeferredCauset, fn causet.RecordIterFunc) error {
   410  	prefix := t.RecordPrefix()
   411  	keyUpperBound := prefix.PrefixNext()
   412  
   413  	it, err := retriever.Iter(startKey, keyUpperBound)
   414  	if err != nil {
   415  		return errors.Trace(err)
   416  	}
   417  	defer it.Close()
   418  
   419  	if !it.Valid() {
   420  		return nil
   421  	}
   422  
   423  	logutil.BgLogger().Debug("record",
   424  		zap.Stringer("startKey", startKey),
   425  		zap.Stringer("key", it.Key()),
   426  		zap.Binary("value", it.Value()))
   427  	rowCausetDecoder, err := makeRowCausetDecoder(t, sessCtx)
   428  	if err != nil {
   429  		return err
   430  	}
   431  	for it.Valid() && it.Key().HasPrefix(prefix) {
   432  		// first ekv pair is event dagger information.
   433  		// TODO: check valid dagger
   434  		// get event handle
   435  		handle, err := blockcodec.DecodeRowKey(it.Key())
   436  		if err != nil {
   437  			return errors.Trace(err)
   438  		}
   439  
   440  		rowMap, err := rowCausetDecoder.DecodeAndEvalRowWithMap(sessCtx, handle, it.Value(), sessCtx.GetStochastikVars().Location(), time.UTC, nil)
   441  		if err != nil {
   442  			return errors.Trace(err)
   443  		}
   444  		data := make([]types.Causet, 0, len(defcaus))
   445  		for _, defCaus := range defcaus {
   446  			data = append(data, rowMap[defCaus.ID])
   447  		}
   448  		more, err := fn(handle, data, defcaus)
   449  		if !more || err != nil {
   450  			return errors.Trace(err)
   451  		}
   452  
   453  		rk := t.RecordKey(handle)
   454  		err = ekv.NextUntil(it, soliton.RowKeyPrefixFilter(rk))
   455  		if err != nil {
   456  			return errors.Trace(err)
   457  		}
   458  	}
   459  
   460  	return nil
   461  }
   462  
   463  var (
   464  	// ErrDataInConsistent indicate that meets inconsistent data.
   465  	ErrDataInConsistent = terror.ClassAdmin.New(errno.ErrDataInConsistent, errno.MyALLEGROSQLErrName[errno.ErrDataInConsistent])
   466  	// ErrDBSJobNotFound indicates the job id was not found.
   467  	ErrDBSJobNotFound = terror.ClassAdmin.New(errno.ErrDBSJobNotFound, errno.MyALLEGROSQLErrName[errno.ErrDBSJobNotFound])
   468  	// ErrCancelFinishedDBSJob returns when cancel a finished dbs job.
   469  	ErrCancelFinishedDBSJob = terror.ClassAdmin.New(errno.ErrCancelFinishedDBSJob, errno.MyALLEGROSQLErrName[errno.ErrCancelFinishedDBSJob])
   470  	// ErrCannotCancelDBSJob returns when cancel a almost finished dbs job, because cancel in now may cause data inconsistency.
   471  	ErrCannotCancelDBSJob = terror.ClassAdmin.New(errno.ErrCannotCancelDBSJob, errno.MyALLEGROSQLErrName[errno.ErrCannotCancelDBSJob])
   472  	// ErrAdminCheckBlock returns when the causet records is inconsistent with the index values.
   473  	ErrAdminCheckBlock = terror.ClassAdmin.New(errno.ErrAdminCheckBlock, errno.MyALLEGROSQLErrName[errno.ErrAdminCheckBlock])
   474  )