github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/interlock/checksum.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 interlock
    15  
    16  import (
    17  	"context"
    18  	"strconv"
    19  
    20  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    21  	"github.com/whtcorpsinc/fidelpb/go-fidelpb"
    22  	"github.com/whtcorpsinc/milevadb/allegrosql"
    23  	"github.com/whtcorpsinc/milevadb/ekv"
    24  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    25  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    26  	"github.com/whtcorpsinc/milevadb/soliton/ranger"
    27  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    28  	"github.com/whtcorpsinc/milevadb/stochastikctx/variable"
    29  	"go.uber.org/zap"
    30  )
    31  
    32  var _ InterlockingDirectorate = &ChecksumBlockInterDirc{}
    33  
    34  // ChecksumBlockInterDirc represents ChecksumBlock interlock.
    35  type ChecksumBlockInterDirc struct {
    36  	baseInterlockingDirectorate
    37  
    38  	blocks map[int64]*checksumContext
    39  	done   bool
    40  }
    41  
    42  // Open implements the InterlockingDirectorate Open interface.
    43  func (e *ChecksumBlockInterDirc) Open(ctx context.Context) error {
    44  	if err := e.baseInterlockingDirectorate.Open(ctx); err != nil {
    45  		return err
    46  	}
    47  
    48  	concurrency, err := getChecksumBlockConcurrency(e.ctx)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	tasks, err := e.buildTasks()
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	taskCh := make(chan *checksumTask, len(tasks))
    59  	resultCh := make(chan *checksumResult, len(tasks))
    60  	for i := 0; i < concurrency; i++ {
    61  		go e.checksumWorker(taskCh, resultCh)
    62  	}
    63  
    64  	for _, task := range tasks {
    65  		taskCh <- task
    66  	}
    67  	close(taskCh)
    68  
    69  	for i := 0; i < len(tasks); i++ {
    70  		result := <-resultCh
    71  		if result.Error != nil {
    72  			err = result.Error
    73  			logutil.Logger(ctx).Error("checksum failed", zap.Error(err))
    74  			continue
    75  		}
    76  		e.handleResult(result)
    77  	}
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	return nil
    83  }
    84  
    85  // Next implements the InterlockingDirectorate Next interface.
    86  func (e *ChecksumBlockInterDirc) Next(ctx context.Context, req *chunk.Chunk) error {
    87  	req.Reset()
    88  	if e.done {
    89  		return nil
    90  	}
    91  	for _, t := range e.blocks {
    92  		req.AppendString(0, t.DBInfo.Name.O)
    93  		req.AppendString(1, t.BlockInfo.Name.O)
    94  		req.AppendUint64(2, t.Response.Checksum)
    95  		req.AppendUint64(3, t.Response.TotalEkvs)
    96  		req.AppendUint64(4, t.Response.TotalBytes)
    97  	}
    98  	e.done = true
    99  	return nil
   100  }
   101  
   102  func (e *ChecksumBlockInterDirc) buildTasks() ([]*checksumTask, error) {
   103  	var tasks []*checksumTask
   104  	for id, t := range e.blocks {
   105  		reqs, err := t.BuildRequests(e.ctx)
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  		for _, req := range reqs {
   110  			tasks = append(tasks, &checksumTask{id, req})
   111  		}
   112  	}
   113  	return tasks, nil
   114  }
   115  
   116  func (e *ChecksumBlockInterDirc) handleResult(result *checksumResult) {
   117  	causet := e.blocks[result.BlockID]
   118  	causet.HandleResponse(result.Response)
   119  }
   120  
   121  func (e *ChecksumBlockInterDirc) checksumWorker(taskCh <-chan *checksumTask, resultCh chan<- *checksumResult) {
   122  	for task := range taskCh {
   123  		result := &checksumResult{BlockID: task.BlockID}
   124  		result.Response, result.Error = e.handleChecksumRequest(task.Request)
   125  		resultCh <- result
   126  	}
   127  }
   128  
   129  func (e *ChecksumBlockInterDirc) handleChecksumRequest(req *ekv.Request) (resp *fidelpb.ChecksumResponse, err error) {
   130  	ctx := context.TODO()
   131  	res, err := allegrosql.Checksum(ctx, e.ctx.GetClient(), req, e.ctx.GetStochastikVars().KVVars)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	res.Fetch(ctx)
   136  	defer func() {
   137  		if err1 := res.Close(); err1 != nil {
   138  			err = err1
   139  		}
   140  	}()
   141  
   142  	resp = &fidelpb.ChecksumResponse{}
   143  
   144  	for {
   145  		data, err := res.NextRaw(ctx)
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  		if data == nil {
   150  			break
   151  		}
   152  		checksum := &fidelpb.ChecksumResponse{}
   153  		if err = checksum.Unmarshal(data); err != nil {
   154  			return nil, err
   155  		}
   156  		uFIDelateChecksumResponse(resp, checksum)
   157  	}
   158  
   159  	return resp, nil
   160  }
   161  
   162  type checksumTask struct {
   163  	BlockID int64
   164  	Request *ekv.Request
   165  }
   166  
   167  type checksumResult struct {
   168  	Error    error
   169  	BlockID  int64
   170  	Response *fidelpb.ChecksumResponse
   171  }
   172  
   173  type checksumContext struct {
   174  	DBInfo    *perceptron.DBInfo
   175  	BlockInfo *perceptron.BlockInfo
   176  	StartTs   uint64
   177  	Response  *fidelpb.ChecksumResponse
   178  }
   179  
   180  func newChecksumContext(EDB *perceptron.DBInfo, causet *perceptron.BlockInfo, startTs uint64) *checksumContext {
   181  	return &checksumContext{
   182  		DBInfo:    EDB,
   183  		BlockInfo: causet,
   184  		StartTs:   startTs,
   185  		Response:  &fidelpb.ChecksumResponse{},
   186  	}
   187  }
   188  
   189  func (c *checksumContext) BuildRequests(ctx stochastikctx.Context) ([]*ekv.Request, error) {
   190  	var partDefs []perceptron.PartitionDefinition
   191  	if part := c.BlockInfo.Partition; part != nil {
   192  		partDefs = part.Definitions
   193  	}
   194  
   195  	reqs := make([]*ekv.Request, 0, (len(c.BlockInfo.Indices)+1)*(len(partDefs)+1))
   196  	if err := c.appendRequest(ctx, c.BlockInfo.ID, &reqs); err != nil {
   197  		return nil, err
   198  	}
   199  
   200  	for _, partDef := range partDefs {
   201  		if err := c.appendRequest(ctx, partDef.ID, &reqs); err != nil {
   202  			return nil, err
   203  		}
   204  	}
   205  
   206  	return reqs, nil
   207  }
   208  
   209  func (c *checksumContext) appendRequest(ctx stochastikctx.Context, blockID int64, reqs *[]*ekv.Request) error {
   210  	req, err := c.buildBlockRequest(ctx, blockID)
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	*reqs = append(*reqs, req)
   216  	for _, indexInfo := range c.BlockInfo.Indices {
   217  		if indexInfo.State != perceptron.StatePublic {
   218  			continue
   219  		}
   220  		req, err = c.buildIndexRequest(ctx, blockID, indexInfo)
   221  		if err != nil {
   222  			return err
   223  		}
   224  		*reqs = append(*reqs, req)
   225  	}
   226  
   227  	return nil
   228  }
   229  
   230  func (c *checksumContext) buildBlockRequest(ctx stochastikctx.Context, blockID int64) (*ekv.Request, error) {
   231  	checksum := &fidelpb.ChecksumRequest{
   232  		ScanOn:    fidelpb.ChecksumScanOn_Block,
   233  		Algorithm: fidelpb.ChecksumAlgorithm_Crc64_Xor,
   234  	}
   235  
   236  	ranges := ranger.FullIntRange(false)
   237  
   238  	var builder allegrosql.RequestBuilder
   239  	return builder.SetBlockRanges(blockID, ranges, nil).
   240  		SetChecksumRequest(checksum).
   241  		SetStartTS(c.StartTs).
   242  		SetConcurrency(ctx.GetStochastikVars().DistALLEGROSQLScanConcurrency()).
   243  		Build()
   244  }
   245  
   246  func (c *checksumContext) buildIndexRequest(ctx stochastikctx.Context, blockID int64, indexInfo *perceptron.IndexInfo) (*ekv.Request, error) {
   247  	checksum := &fidelpb.ChecksumRequest{
   248  		ScanOn:    fidelpb.ChecksumScanOn_Index,
   249  		Algorithm: fidelpb.ChecksumAlgorithm_Crc64_Xor,
   250  	}
   251  
   252  	ranges := ranger.FullRange()
   253  
   254  	var builder allegrosql.RequestBuilder
   255  	return builder.SetIndexRanges(ctx.GetStochastikVars().StmtCtx, blockID, indexInfo.ID, ranges).
   256  		SetChecksumRequest(checksum).
   257  		SetStartTS(c.StartTs).
   258  		SetConcurrency(ctx.GetStochastikVars().DistALLEGROSQLScanConcurrency()).
   259  		Build()
   260  }
   261  
   262  func (c *checksumContext) HandleResponse(uFIDelate *fidelpb.ChecksumResponse) {
   263  	uFIDelateChecksumResponse(c.Response, uFIDelate)
   264  }
   265  
   266  func getChecksumBlockConcurrency(ctx stochastikctx.Context) (int, error) {
   267  	stochastikVars := ctx.GetStochastikVars()
   268  	concurrency, err := variable.GetStochastikSystemVar(stochastikVars, variable.MilevaDBChecksumBlockConcurrency)
   269  	if err != nil {
   270  		return 0, err
   271  	}
   272  	c, err := strconv.ParseInt(concurrency, 10, 64)
   273  	return int(c), err
   274  }
   275  
   276  func uFIDelateChecksumResponse(resp, uFIDelate *fidelpb.ChecksumResponse) {
   277  	resp.Checksum ^= uFIDelate.Checksum
   278  	resp.TotalEkvs += uFIDelate.TotalEkvs
   279  	resp.TotalBytes += uFIDelate.TotalBytes
   280  }