github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/checksum/executor.go (about)

     1  // Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.
     2  
     3  package checksum
     4  
     5  import (
     6  	"context"
     7  
     8  	"github.com/pingcap/br/pkg/metautil"
     9  
    10  	"github.com/gogo/protobuf/proto"
    11  	"github.com/pingcap/errors"
    12  	"github.com/pingcap/log"
    13  	"github.com/pingcap/parser/model"
    14  	"github.com/pingcap/tidb/distsql"
    15  	"github.com/pingcap/tidb/kv"
    16  	"github.com/pingcap/tidb/sessionctx/variable"
    17  	"github.com/pingcap/tidb/tablecodec"
    18  	"github.com/pingcap/tidb/util/ranger"
    19  	"github.com/pingcap/tipb/go-tipb"
    20  	"go.uber.org/zap"
    21  )
    22  
    23  // ExecutorBuilder is used to build a "kv.Request".
    24  type ExecutorBuilder struct {
    25  	table *model.TableInfo
    26  	ts    uint64
    27  
    28  	oldTable *metautil.Table
    29  
    30  	concurrency uint
    31  }
    32  
    33  // NewExecutorBuilder returns a new executor builder.
    34  func NewExecutorBuilder(table *model.TableInfo, ts uint64) *ExecutorBuilder {
    35  	return &ExecutorBuilder{
    36  		table: table,
    37  		ts:    ts,
    38  
    39  		concurrency: variable.DefDistSQLScanConcurrency,
    40  	}
    41  }
    42  
    43  // SetOldTable set a old table info to the builder.
    44  func (builder *ExecutorBuilder) SetOldTable(oldTable *metautil.Table) *ExecutorBuilder {
    45  	builder.oldTable = oldTable
    46  	return builder
    47  }
    48  
    49  // SetConcurrency set the concurrency of the checksum executing.
    50  func (builder *ExecutorBuilder) SetConcurrency(conc uint) *ExecutorBuilder {
    51  	builder.concurrency = conc
    52  	return builder
    53  }
    54  
    55  // Build builds a checksum executor.
    56  func (builder *ExecutorBuilder) Build() (*Executor, error) {
    57  	reqs, err := buildChecksumRequest(builder.table, builder.oldTable, builder.ts, builder.concurrency)
    58  	if err != nil {
    59  		return nil, errors.Trace(err)
    60  	}
    61  	return &Executor{reqs: reqs}, nil
    62  }
    63  
    64  func buildChecksumRequest(
    65  	newTable *model.TableInfo,
    66  	oldTable *metautil.Table,
    67  	startTS uint64,
    68  	concurrency uint,
    69  ) ([]*kv.Request, error) {
    70  	var partDefs []model.PartitionDefinition
    71  	if part := newTable.Partition; part != nil {
    72  		partDefs = part.Definitions
    73  	}
    74  
    75  	reqs := make([]*kv.Request, 0, (len(newTable.Indices)+1)*(len(partDefs)+1))
    76  	var oldTableID int64
    77  	if oldTable != nil {
    78  		oldTableID = oldTable.Info.ID
    79  	}
    80  	rs, err := buildRequest(newTable, newTable.ID, oldTable, oldTableID, startTS, concurrency)
    81  	if err != nil {
    82  		return nil, errors.Trace(err)
    83  	}
    84  	reqs = append(reqs, rs...)
    85  
    86  	for _, partDef := range partDefs {
    87  		var oldPartID int64
    88  		if oldTable != nil {
    89  			for _, oldPartDef := range oldTable.Info.Partition.Definitions {
    90  				if oldPartDef.Name == partDef.Name {
    91  					oldPartID = oldPartDef.ID
    92  				}
    93  			}
    94  		}
    95  		rs, err := buildRequest(newTable, partDef.ID, oldTable, oldPartID, startTS, concurrency)
    96  		if err != nil {
    97  			return nil, errors.Trace(err)
    98  		}
    99  		reqs = append(reqs, rs...)
   100  	}
   101  
   102  	return reqs, nil
   103  }
   104  
   105  func buildRequest(
   106  	tableInfo *model.TableInfo,
   107  	tableID int64,
   108  	oldTable *metautil.Table,
   109  	oldTableID int64,
   110  	startTS uint64,
   111  	concurrency uint,
   112  ) ([]*kv.Request, error) {
   113  	reqs := make([]*kv.Request, 0)
   114  	req, err := buildTableRequest(tableInfo, tableID, oldTable, oldTableID, startTS, concurrency)
   115  	if err != nil {
   116  		return nil, errors.Trace(err)
   117  	}
   118  	reqs = append(reqs, req)
   119  
   120  	for _, indexInfo := range tableInfo.Indices {
   121  		if indexInfo.State != model.StatePublic {
   122  			continue
   123  		}
   124  		var oldIndexInfo *model.IndexInfo
   125  		if oldTable != nil {
   126  			for _, oldIndex := range oldTable.Info.Indices {
   127  				if oldIndex.Name == indexInfo.Name {
   128  					oldIndexInfo = oldIndex
   129  					break
   130  				}
   131  			}
   132  			if oldIndexInfo == nil {
   133  				log.Panic("index not found in origin table, "+
   134  					"please check the restore table has the same index info with origin table",
   135  					zap.Int64("table id", tableID),
   136  					zap.Stringer("table name", tableInfo.Name),
   137  					zap.Int64("origin table id", oldTableID),
   138  					zap.Stringer("origin table name", oldTable.Info.Name),
   139  					zap.Stringer("index name", indexInfo.Name))
   140  			}
   141  		}
   142  		req, err = buildIndexRequest(
   143  			tableID, indexInfo, oldTableID, oldIndexInfo, startTS, concurrency)
   144  		if err != nil {
   145  			return nil, errors.Trace(err)
   146  		}
   147  		reqs = append(reqs, req)
   148  	}
   149  
   150  	return reqs, nil
   151  }
   152  
   153  func buildTableRequest(
   154  	tableInfo *model.TableInfo,
   155  	tableID int64,
   156  	oldTable *metautil.Table,
   157  	oldTableID int64,
   158  	startTS uint64,
   159  	concurrency uint,
   160  ) (*kv.Request, error) {
   161  	var rule *tipb.ChecksumRewriteRule
   162  	if oldTable != nil {
   163  		rule = &tipb.ChecksumRewriteRule{
   164  			OldPrefix: tablecodec.GenTableRecordPrefix(oldTableID),
   165  			NewPrefix: tablecodec.GenTableRecordPrefix(tableID),
   166  		}
   167  	}
   168  
   169  	checksum := &tipb.ChecksumRequest{
   170  		ScanOn:    tipb.ChecksumScanOn_Table,
   171  		Algorithm: tipb.ChecksumAlgorithm_Crc64_Xor,
   172  		Rule:      rule,
   173  	}
   174  
   175  	var ranges []*ranger.Range
   176  	if tableInfo.IsCommonHandle {
   177  		ranges = ranger.FullNotNullRange()
   178  	} else {
   179  		ranges = ranger.FullIntRange(false)
   180  	}
   181  
   182  	var builder distsql.RequestBuilder
   183  	// Use low priority to reducing impact to other requests.
   184  	builder.Request.Priority = kv.PriorityLow
   185  	return builder.SetHandleRanges(nil, tableID, tableInfo.IsCommonHandle, ranges, nil).
   186  		SetStartTS(startTS).
   187  		SetChecksumRequest(checksum).
   188  		SetConcurrency(int(concurrency)).
   189  		Build()
   190  }
   191  
   192  func buildIndexRequest(
   193  	tableID int64,
   194  	indexInfo *model.IndexInfo,
   195  	oldTableID int64,
   196  	oldIndexInfo *model.IndexInfo,
   197  	startTS uint64,
   198  	concurrency uint,
   199  ) (*kv.Request, error) {
   200  	var rule *tipb.ChecksumRewriteRule
   201  	if oldIndexInfo != nil {
   202  		rule = &tipb.ChecksumRewriteRule{
   203  			OldPrefix: tablecodec.EncodeTableIndexPrefix(oldTableID, oldIndexInfo.ID),
   204  			NewPrefix: tablecodec.EncodeTableIndexPrefix(tableID, indexInfo.ID),
   205  		}
   206  	}
   207  	checksum := &tipb.ChecksumRequest{
   208  		ScanOn:    tipb.ChecksumScanOn_Index,
   209  		Algorithm: tipb.ChecksumAlgorithm_Crc64_Xor,
   210  		Rule:      rule,
   211  	}
   212  
   213  	ranges := ranger.FullRange()
   214  
   215  	var builder distsql.RequestBuilder
   216  	// Use low priority to reducing impact to other requests.
   217  	builder.Request.Priority = kv.PriorityLow
   218  	return builder.SetIndexRanges(nil, tableID, indexInfo.ID, ranges).
   219  		SetStartTS(startTS).
   220  		SetChecksumRequest(checksum).
   221  		SetConcurrency(int(concurrency)).
   222  		Build()
   223  }
   224  
   225  func sendChecksumRequest(
   226  	ctx context.Context, client kv.Client, req *kv.Request, vars *kv.Variables,
   227  ) (resp *tipb.ChecksumResponse, err error) {
   228  	res, err := distsql.Checksum(ctx, client, req, vars)
   229  	if err != nil {
   230  		return nil, errors.Trace(err)
   231  	}
   232  	defer func() {
   233  		if err1 := res.Close(); err1 != nil {
   234  			err = err1
   235  		}
   236  	}()
   237  
   238  	resp = &tipb.ChecksumResponse{}
   239  
   240  	for {
   241  		data, err := res.NextRaw(ctx)
   242  		if err != nil {
   243  			return nil, errors.Trace(err)
   244  		}
   245  		if data == nil {
   246  			break
   247  		}
   248  		checksum := &tipb.ChecksumResponse{}
   249  		if err = checksum.Unmarshal(data); err != nil {
   250  			return nil, errors.Trace(err)
   251  		}
   252  		updateChecksumResponse(resp, checksum)
   253  	}
   254  
   255  	return resp, nil
   256  }
   257  
   258  func updateChecksumResponse(resp, update *tipb.ChecksumResponse) {
   259  	resp.Checksum ^= update.Checksum
   260  	resp.TotalKvs += update.TotalKvs
   261  	resp.TotalBytes += update.TotalBytes
   262  }
   263  
   264  // Executor is a checksum executor.
   265  type Executor struct {
   266  	reqs []*kv.Request
   267  }
   268  
   269  // Len returns the total number of checksum requests.
   270  func (exec *Executor) Len() int {
   271  	return len(exec.reqs)
   272  }
   273  
   274  // Each executes the function to each requests in the executor.
   275  func (exec *Executor) Each(f func(*kv.Request) error) error {
   276  	for _, req := range exec.reqs {
   277  		err := f(req)
   278  		if err != nil {
   279  			return errors.Trace(err)
   280  		}
   281  	}
   282  	return nil
   283  }
   284  
   285  // RawRequests extracts the raw requests associated with this executor.
   286  // This is mainly used for debugging only.
   287  func (exec *Executor) RawRequests() ([]*tipb.ChecksumRequest, error) {
   288  	res := make([]*tipb.ChecksumRequest, 0, len(exec.reqs))
   289  	for _, req := range exec.reqs {
   290  		rawReq := new(tipb.ChecksumRequest)
   291  		if err := proto.Unmarshal(req.Data, rawReq); err != nil {
   292  			return nil, errors.Trace(err)
   293  		}
   294  		res = append(res, rawReq)
   295  	}
   296  	return res, nil
   297  }
   298  
   299  // Execute executes a checksum executor.
   300  func (exec *Executor) Execute(
   301  	ctx context.Context,
   302  	client kv.Client,
   303  	updateFn func(),
   304  ) (*tipb.ChecksumResponse, error) {
   305  	checksumResp := &tipb.ChecksumResponse{}
   306  	for _, req := range exec.reqs {
   307  		// Pointer to SessionVars.Killed
   308  		// Killed is a flag to indicate that this query is killed.
   309  		//
   310  		// It is useful in TiDB, however, it's a place holder in BR.
   311  		killed := uint32(0)
   312  		resp, err := sendChecksumRequest(ctx, client, req, kv.NewVariables(&killed))
   313  		if err != nil {
   314  			return nil, errors.Trace(err)
   315  		}
   316  		updateChecksumResponse(checksumResp, resp)
   317  		updateFn()
   318  	}
   319  	return checksumResp, nil
   320  }