github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/index_merge_reader.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  	"runtime/trace"
    19  	"sync"
    20  	"sync/atomic"
    21  	"unsafe"
    22  
    23  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    24  	"github.com/whtcorpsinc/BerolinaSQL/terror"
    25  	"github.com/whtcorpsinc/errors"
    26  	"github.com/whtcorpsinc/failpoint"
    27  	"github.com/whtcorpsinc/fidelpb/go-fidelpb"
    28  	"github.com/whtcorpsinc/milevadb/allegrosql"
    29  	"github.com/whtcorpsinc/milevadb/causet"
    30  	causetembedded "github.com/whtcorpsinc/milevadb/causet/embedded"
    31  	"github.com/whtcorpsinc/milevadb/ekv"
    32  	"github.com/whtcorpsinc/milevadb/memex"
    33  	"github.com/whtcorpsinc/milevadb/soliton"
    34  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    35  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    36  	"github.com/whtcorpsinc/milevadb/soliton/memory"
    37  	"github.com/whtcorpsinc/milevadb/soliton/ranger"
    38  	"github.com/whtcorpsinc/milevadb/statistics"
    39  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    40  	"go.uber.org/zap"
    41  )
    42  
    43  var (
    44  	_ InterlockingDirectorate = &IndexMergeReaderInterlockingDirectorate{}
    45  )
    46  
    47  // IndexMergeReaderInterlockingDirectorate accesses a causet with multiple index/causet scan.
    48  // There are three types of workers:
    49  // 1. partialBlockWorker/partialIndexWorker, which are used to fetch the handles
    50  // 2. indexMergeProcessWorker, which is used to do the `Union` operation.
    51  // 3. indexMergeBlockScanWorker, which is used to get the causet tuples with the given handles.
    52  //
    53  // The execution flow is really like IndexLookUpReader. However, it uses multiple index scans
    54  // or causet scans to get the handles:
    55  // 1. use the partialBlockWorkers and partialIndexWorkers to fetch the handles (a batch per time)
    56  //    and send them to the indexMergeProcessWorker.
    57  // 2. indexMergeProcessWorker do the `Union` operation for a batch of handles it have got.
    58  //    For every handle in the batch:
    59  //    1. check whether it has been accessed.
    60  //    2. if not, record it and send it to the indexMergeBlockScanWorker.
    61  //    3. if accessed, just ignore it.
    62  type IndexMergeReaderInterlockingDirectorate struct {
    63  	baseInterlockingDirectorate
    64  
    65  	causet           causet.Block
    66  	indexes          []*perceptron.IndexInfo
    67  	descs            []bool
    68  	ranges           [][]*ranger.Range
    69  	posetPosetDagPBs []*fidelpb.PosetDagRequest
    70  	startTS          uint64
    71  	blockRequest     *fidelpb.PosetDagRequest
    72  	// defCausumns are only required by union scan.
    73  	defCausumns       []*perceptron.DeferredCausetInfo
    74  	partialStreamings []bool
    75  	blockStreaming    bool
    76  	*dataReaderBuilder
    77  	// All fields above are immublock.
    78  
    79  	tblWorkerWg    sync.WaitGroup
    80  	processWokerWg sync.WaitGroup
    81  	finished       chan struct{}
    82  
    83  	workerStarted bool
    84  	keyRanges     [][]ekv.KeyRange
    85  
    86  	resultCh   chan *lookupBlockTask
    87  	resultCurr *lookupBlockTask
    88  	feedbacks  []*statistics.QueryFeedback
    89  
    90  	// memTracker is used to track the memory usage of this interlock.
    91  	memTracker *memory.Tracker
    92  
    93  	// checHoTTexValue is used to check the consistency of the index data.
    94  	*checHoTTexValue
    95  
    96  	corDefCausInIdxSide bool
    97  	partialCausets      [][]causetembedded.PhysicalCauset
    98  	corDefCausInTblSide bool
    99  	tblCausets          []causetembedded.PhysicalCauset
   100  	corDefCausInAccess  bool
   101  	idxDefCauss         [][]*memex.DeferredCauset
   102  	defCausLens         [][]int
   103  
   104  	handleDefCauss causetembedded.HandleDefCauss
   105  }
   106  
   107  // Open implements the InterlockingDirectorate Open interface
   108  func (e *IndexMergeReaderInterlockingDirectorate) Open(ctx context.Context) error {
   109  	e.keyRanges = make([][]ekv.KeyRange, 0, len(e.partialCausets))
   110  	for i, plan := range e.partialCausets {
   111  		_, ok := plan[0].(*causetembedded.PhysicalIndexScan)
   112  		if !ok {
   113  			if e.causet.Meta().IsCommonHandle {
   114  				keyRanges, err := allegrosql.CommonHandleRangesToKVRanges(e.ctx.GetStochastikVars().StmtCtx, getPhysicalBlockID(e.causet), e.ranges[i])
   115  				if err != nil {
   116  					return err
   117  				}
   118  				e.keyRanges = append(e.keyRanges, keyRanges)
   119  			} else {
   120  				e.keyRanges = append(e.keyRanges, nil)
   121  			}
   122  			continue
   123  		}
   124  		keyRange, err := allegrosql.IndexRangesToKVRanges(e.ctx.GetStochastikVars().StmtCtx, getPhysicalBlockID(e.causet), e.indexes[i].ID, e.ranges[i], e.feedbacks[i])
   125  		if err != nil {
   126  			return err
   127  		}
   128  		e.keyRanges = append(e.keyRanges, keyRange)
   129  	}
   130  	e.finished = make(chan struct{})
   131  	e.resultCh = make(chan *lookupBlockTask, atomic.LoadInt32(&LookupBlockTaskChannelSize))
   132  	return nil
   133  }
   134  
   135  func (e *IndexMergeReaderInterlockingDirectorate) startWorkers(ctx context.Context) error {
   136  	exitCh := make(chan struct{})
   137  	workCh := make(chan *lookupBlockTask, 1)
   138  	fetchCh := make(chan *lookupBlockTask, len(e.keyRanges))
   139  
   140  	e.startIndexMergeProcessWorker(ctx, workCh, fetchCh)
   141  
   142  	var err error
   143  	var partialWorkerWg sync.WaitGroup
   144  	for i := 0; i < len(e.keyRanges); i++ {
   145  		partialWorkerWg.Add(1)
   146  		if e.indexes[i] != nil {
   147  			err = e.startPartialIndexWorker(ctx, exitCh, fetchCh, i, &partialWorkerWg, e.keyRanges[i])
   148  		} else {
   149  			err = e.startPartialBlockWorker(ctx, exitCh, fetchCh, i, &partialWorkerWg)
   150  		}
   151  		if err != nil {
   152  			partialWorkerWg.Done()
   153  			break
   154  		}
   155  	}
   156  	go e.waitPartialWorkersAndCloseFetchChan(&partialWorkerWg, fetchCh)
   157  	if err != nil {
   158  		close(exitCh)
   159  		return err
   160  	}
   161  	e.startIndexMergeBlockScanWorker(ctx, workCh)
   162  	e.workerStarted = true
   163  	return nil
   164  }
   165  
   166  func (e *IndexMergeReaderInterlockingDirectorate) waitPartialWorkersAndCloseFetchChan(partialWorkerWg *sync.WaitGroup, fetchCh chan *lookupBlockTask) {
   167  	partialWorkerWg.Wait()
   168  	close(fetchCh)
   169  }
   170  
   171  func (e *IndexMergeReaderInterlockingDirectorate) startIndexMergeProcessWorker(ctx context.Context, workCh chan<- *lookupBlockTask, fetch <-chan *lookupBlockTask) {
   172  	idxMergeProcessWorker := &indexMergeProcessWorker{}
   173  	e.processWokerWg.Add(1)
   174  	go func() {
   175  		defer trace.StartRegion(ctx, "IndexMergeProcessWorker").End()
   176  		soliton.WithRecovery(
   177  			func() {
   178  				idxMergeProcessWorker.fetchLoop(ctx, fetch, workCh, e.resultCh, e.finished)
   179  			},
   180  			idxMergeProcessWorker.handleLoopFetcherPanic(ctx, e.resultCh),
   181  		)
   182  		e.processWokerWg.Done()
   183  	}()
   184  }
   185  
   186  func (e *IndexMergeReaderInterlockingDirectorate) startPartialIndexWorker(ctx context.Context, exitCh <-chan struct{}, fetchCh chan<- *lookupBlockTask, workID int, partialWorkerWg *sync.WaitGroup, keyRange []ekv.KeyRange) error {
   187  	if e.runtimeStats != nil {
   188  		defCauslInterDirc := true
   189  		e.posetPosetDagPBs[workID].DefCauslectInterDircutionSummaries = &defCauslInterDirc
   190  	}
   191  
   192  	var builder allegrosql.RequestBuilder
   193  	ekvReq, err := builder.SetKeyRanges(keyRange).
   194  		SetPosetDagRequest(e.posetPosetDagPBs[workID]).
   195  		SetStartTS(e.startTS).
   196  		SetDesc(e.descs[workID]).
   197  		SetKeepOrder(false).
   198  		SetStreaming(e.partialStreamings[workID]).
   199  		SetFromStochastikVars(e.ctx.GetStochastikVars()).
   200  		SetMemTracker(e.memTracker).
   201  		Build()
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	result, err := allegrosql.SelectWithRuntimeStats(ctx, e.ctx, ekvReq, e.handleDefCauss.GetFieldsTypes(), e.feedbacks[workID], getPhysicalCausetIDs(e.partialCausets[workID]), e.id)
   207  	if err != nil {
   208  		return err
   209  	}
   210  
   211  	result.Fetch(ctx)
   212  	worker := &partialIndexWorker{
   213  		sc:           e.ctx,
   214  		batchSize:    e.maxChunkSize,
   215  		maxBatchSize: e.ctx.GetStochastikVars().IndexLookupSize,
   216  		maxChunkSize: e.maxChunkSize,
   217  	}
   218  
   219  	if worker.batchSize > worker.maxBatchSize {
   220  		worker.batchSize = worker.maxBatchSize
   221  	}
   222  
   223  	failpoint.Inject("startPartialIndexWorkerErr", func() error {
   224  		return errors.New("inject an error before start partialIndexWorker")
   225  	})
   226  
   227  	go func() {
   228  		defer trace.StartRegion(ctx, "IndexMergePartialIndexWorker").End()
   229  		defer partialWorkerWg.Done()
   230  		ctx1, cancel := context.WithCancel(ctx)
   231  		var err error
   232  		soliton.WithRecovery(
   233  			func() {
   234  				_, err = worker.fetchHandles(ctx1, result, exitCh, fetchCh, e.resultCh, e.finished, e.handleDefCauss)
   235  			},
   236  			e.handleHandlesFetcherPanic(ctx, e.resultCh, "partialIndexWorker"),
   237  		)
   238  		if err != nil {
   239  			e.feedbacks[workID].Invalidate()
   240  		}
   241  		cancel()
   242  		if err := result.Close(); err != nil {
   243  			logutil.Logger(ctx).Error("close Select result failed:", zap.Error(err))
   244  		}
   245  		e.ctx.StoreQueryFeedback(e.feedbacks[workID])
   246  	}()
   247  
   248  	return nil
   249  }
   250  
   251  func (e *IndexMergeReaderInterlockingDirectorate) buildPartialBlockReader(ctx context.Context, workID int) InterlockingDirectorate {
   252  	blockReaderInterDirc := &BlockReaderInterlockingDirectorate{
   253  		baseInterlockingDirectorate: newBaseInterlockingDirectorate(e.ctx, e.schemaReplicant, 0),
   254  		causet:                      e.causet,
   255  		posetPosetDagPB:             e.posetPosetDagPBs[workID],
   256  		startTS:                     e.startTS,
   257  		streaming:                   e.partialStreamings[workID],
   258  		feedback:                    statistics.NewQueryFeedback(0, nil, 0, false),
   259  		plans:                       e.partialCausets[workID],
   260  		ranges:                      e.ranges[workID],
   261  	}
   262  	return blockReaderInterDirc
   263  }
   264  
   265  func (e *IndexMergeReaderInterlockingDirectorate) startPartialBlockWorker(ctx context.Context, exitCh <-chan struct{}, fetchCh chan<- *lookupBlockTask, workID int,
   266  	partialWorkerWg *sync.WaitGroup) error {
   267  	partialBlockReader := e.buildPartialBlockReader(ctx, workID)
   268  	err := partialBlockReader.Open(ctx)
   269  	if err != nil {
   270  		logutil.Logger(ctx).Error("open Select result failed:", zap.Error(err))
   271  		return err
   272  	}
   273  	blockInfo := e.partialCausets[workID][0].(*causetembedded.PhysicalBlockScan).Block
   274  	worker := &partialBlockWorker{
   275  		sc:           e.ctx,
   276  		batchSize:    e.maxChunkSize,
   277  		maxBatchSize: e.ctx.GetStochastikVars().IndexLookupSize,
   278  		maxChunkSize: e.maxChunkSize,
   279  		blockReader:  partialBlockReader,
   280  		blockInfo:    blockInfo,
   281  	}
   282  
   283  	if worker.batchSize > worker.maxBatchSize {
   284  		worker.batchSize = worker.maxBatchSize
   285  	}
   286  	go func() {
   287  		defer trace.StartRegion(ctx, "IndexMergePartialBlockWorker").End()
   288  		defer partialWorkerWg.Done()
   289  		ctx1, cancel := context.WithCancel(ctx)
   290  		var err error
   291  		soliton.WithRecovery(
   292  			func() {
   293  				_, err = worker.fetchHandles(ctx1, exitCh, fetchCh, e.resultCh, e.finished, e.handleDefCauss)
   294  			},
   295  			e.handleHandlesFetcherPanic(ctx, e.resultCh, "partialBlockWorker"),
   296  		)
   297  		if err != nil {
   298  			e.feedbacks[workID].Invalidate()
   299  		}
   300  		cancel()
   301  		if err := worker.blockReader.Close(); err != nil {
   302  			logutil.Logger(ctx).Error("close Select result failed:", zap.Error(err))
   303  		}
   304  		e.ctx.StoreQueryFeedback(e.feedbacks[workID])
   305  	}()
   306  	return nil
   307  }
   308  
   309  type partialBlockWorker struct {
   310  	sc           stochastikctx.Context
   311  	batchSize    int
   312  	maxBatchSize int
   313  	maxChunkSize int
   314  	blockReader  InterlockingDirectorate
   315  	blockInfo    *perceptron.BlockInfo
   316  }
   317  
   318  func (w *partialBlockWorker) fetchHandles(ctx context.Context, exitCh <-chan struct{}, fetchCh chan<- *lookupBlockTask, resultCh chan<- *lookupBlockTask,
   319  	finished <-chan struct{}, handleDefCauss causetembedded.HandleDefCauss) (count int64, err error) {
   320  	chk := chunk.NewChunkWithCapacity(retTypes(w.blockReader), w.maxChunkSize)
   321  	for {
   322  		handles, retChunk, err := w.extractTaskHandles(ctx, chk, handleDefCauss)
   323  		if err != nil {
   324  			doneCh := make(chan error, 1)
   325  			doneCh <- err
   326  			resultCh <- &lookupBlockTask{
   327  				doneCh: doneCh,
   328  			}
   329  			return count, err
   330  		}
   331  		if len(handles) == 0 {
   332  			return count, nil
   333  		}
   334  		count += int64(len(handles))
   335  		task := w.buildBlockTask(handles, retChunk)
   336  		select {
   337  		case <-ctx.Done():
   338  			return count, ctx.Err()
   339  		case <-exitCh:
   340  			return count, nil
   341  		case <-finished:
   342  			return count, nil
   343  		case fetchCh <- task:
   344  		}
   345  	}
   346  }
   347  
   348  func (w *partialBlockWorker) extractTaskHandles(ctx context.Context, chk *chunk.Chunk, handleDefCauss causetembedded.HandleDefCauss) (
   349  	handles []ekv.Handle, retChk *chunk.Chunk, err error) {
   350  	handles = make([]ekv.Handle, 0, w.batchSize)
   351  	for len(handles) < w.batchSize {
   352  		chk.SetRequiredEvents(w.batchSize-len(handles), w.maxChunkSize)
   353  		err = errors.Trace(w.blockReader.Next(ctx, chk))
   354  		if err != nil {
   355  			return handles, nil, err
   356  		}
   357  		if chk.NumEvents() == 0 {
   358  			return handles, retChk, nil
   359  		}
   360  		for i := 0; i < chk.NumEvents(); i++ {
   361  			handle, err := handleDefCauss.BuildHandle(chk.GetEvent(i))
   362  			if err != nil {
   363  				return nil, nil, err
   364  			}
   365  			handles = append(handles, handle)
   366  		}
   367  	}
   368  	w.batchSize *= 2
   369  	if w.batchSize > w.maxBatchSize {
   370  		w.batchSize = w.maxBatchSize
   371  	}
   372  	return handles, retChk, nil
   373  }
   374  
   375  func (w *partialBlockWorker) buildBlockTask(handles []ekv.Handle, retChk *chunk.Chunk) *lookupBlockTask {
   376  	task := &lookupBlockTask{
   377  		handles:   handles,
   378  		idxEvents: retChk,
   379  	}
   380  
   381  	task.doneCh = make(chan error, 1)
   382  	return task
   383  }
   384  
   385  func (e *IndexMergeReaderInterlockingDirectorate) startIndexMergeBlockScanWorker(ctx context.Context, workCh <-chan *lookupBlockTask) {
   386  	lookupConcurrencyLimit := e.ctx.GetStochastikVars().IndexLookupConcurrency()
   387  	e.tblWorkerWg.Add(lookupConcurrencyLimit)
   388  	for i := 0; i < lookupConcurrencyLimit; i++ {
   389  		worker := &indexMergeBlockScanWorker{
   390  			workCh:         workCh,
   391  			finished:       e.finished,
   392  			buildTblReader: e.buildFinalBlockReader,
   393  			tblCausets:     e.tblCausets,
   394  			memTracker:     memory.NewTracker(memory.LabelForSimpleTask, -1),
   395  		}
   396  		ctx1, cancel := context.WithCancel(ctx)
   397  		go func() {
   398  			defer trace.StartRegion(ctx, "IndexMergeBlockScanWorker").End()
   399  			var task *lookupBlockTask
   400  			soliton.WithRecovery(
   401  				func() { task = worker.pickAndInterDircTask(ctx1) },
   402  				worker.handlePickAndInterDircTaskPanic(ctx1, task),
   403  			)
   404  			cancel()
   405  			e.tblWorkerWg.Done()
   406  		}()
   407  	}
   408  }
   409  
   410  func (e *IndexMergeReaderInterlockingDirectorate) buildFinalBlockReader(ctx context.Context, handles []ekv.Handle) (InterlockingDirectorate, error) {
   411  	blockReaderInterDirc := &BlockReaderInterlockingDirectorate{
   412  		baseInterlockingDirectorate: newBaseInterlockingDirectorate(e.ctx, e.schemaReplicant, 0),
   413  		causet:                      e.causet,
   414  		posetPosetDagPB:             e.blockRequest,
   415  		startTS:                     e.startTS,
   416  		streaming:                   e.blockStreaming,
   417  		defCausumns:                 e.defCausumns,
   418  		feedback:                    statistics.NewQueryFeedback(0, nil, 0, false),
   419  		plans:                       e.tblCausets,
   420  	}
   421  	blockReaderInterDirc.buildVirtualDeferredCausetInfo()
   422  	blockReader, err := e.dataReaderBuilder.buildBlockReaderFromHandles(ctx, blockReaderInterDirc, handles)
   423  	if err != nil {
   424  		logutil.Logger(ctx).Error("build causet reader from handles failed", zap.Error(err))
   425  		return nil, err
   426  	}
   427  	return blockReader, nil
   428  }
   429  
   430  // Next implements InterlockingDirectorate Next interface.
   431  func (e *IndexMergeReaderInterlockingDirectorate) Next(ctx context.Context, req *chunk.Chunk) error {
   432  	if !e.workerStarted {
   433  		if err := e.startWorkers(ctx); err != nil {
   434  			return err
   435  		}
   436  	}
   437  
   438  	req.Reset()
   439  	for {
   440  		resultTask, err := e.getResultTask()
   441  		if err != nil {
   442  			return errors.Trace(err)
   443  		}
   444  		if resultTask == nil {
   445  			return nil
   446  		}
   447  		for resultTask.cursor < len(resultTask.rows) {
   448  			req.AppendEvent(resultTask.rows[resultTask.cursor])
   449  			resultTask.cursor++
   450  			if req.NumEvents() >= e.maxChunkSize {
   451  				return nil
   452  			}
   453  		}
   454  	}
   455  }
   456  
   457  func (e *IndexMergeReaderInterlockingDirectorate) getResultTask() (*lookupBlockTask, error) {
   458  	if e.resultCurr != nil && e.resultCurr.cursor < len(e.resultCurr.rows) {
   459  		return e.resultCurr, nil
   460  	}
   461  	task, ok := <-e.resultCh
   462  	if !ok {
   463  		return nil, nil
   464  	}
   465  	if err := <-task.doneCh; err != nil {
   466  		return nil, errors.Trace(err)
   467  	}
   468  
   469  	// Release the memory usage of last task before we handle a new task.
   470  	if e.resultCurr != nil {
   471  		e.resultCurr.memTracker.Consume(-e.resultCurr.memUsage)
   472  	}
   473  	e.resultCurr = task
   474  	return e.resultCurr, nil
   475  }
   476  
   477  func (e *IndexMergeReaderInterlockingDirectorate) handleHandlesFetcherPanic(ctx context.Context, resultCh chan<- *lookupBlockTask, worker string) func(r interface{}) {
   478  	return func(r interface{}) {
   479  		if r == nil {
   480  			return
   481  		}
   482  
   483  		err4Panic := errors.Errorf("panic in IndexMergeReaderInterlockingDirectorate %s: %v", worker, r)
   484  		logutil.Logger(ctx).Error(err4Panic.Error())
   485  		doneCh := make(chan error, 1)
   486  		doneCh <- err4Panic
   487  		resultCh <- &lookupBlockTask{
   488  			doneCh: doneCh,
   489  		}
   490  	}
   491  }
   492  
   493  // Close implements InterDirc Close interface.
   494  func (e *IndexMergeReaderInterlockingDirectorate) Close() error {
   495  	if e.finished == nil {
   496  		return nil
   497  	}
   498  	close(e.finished)
   499  	e.processWokerWg.Wait()
   500  	e.tblWorkerWg.Wait()
   501  	e.finished = nil
   502  	e.workerStarted = false
   503  	// TODO: how to causetstore e.feedbacks
   504  	return nil
   505  }
   506  
   507  type indexMergeProcessWorker struct {
   508  }
   509  
   510  func (w *indexMergeProcessWorker) fetchLoop(ctx context.Context, fetchCh <-chan *lookupBlockTask,
   511  	workCh chan<- *lookupBlockTask, resultCh chan<- *lookupBlockTask, finished <-chan struct{}) {
   512  	defer func() {
   513  		close(workCh)
   514  		close(resultCh)
   515  	}()
   516  
   517  	distinctHandles := ekv.NewHandleMap()
   518  
   519  	for task := range fetchCh {
   520  		handles := task.handles
   521  		fhs := make([]ekv.Handle, 0, 8)
   522  		for _, h := range handles {
   523  			if _, ok := distinctHandles.Get(h); !ok {
   524  				fhs = append(fhs, h)
   525  				distinctHandles.Set(h, true)
   526  			}
   527  		}
   528  		if len(fhs) == 0 {
   529  			continue
   530  		}
   531  		task := &lookupBlockTask{
   532  			handles: fhs,
   533  			doneCh:  make(chan error, 1),
   534  		}
   535  		select {
   536  		case <-ctx.Done():
   537  			return
   538  		case <-finished:
   539  			return
   540  		case workCh <- task:
   541  			resultCh <- task
   542  		}
   543  	}
   544  }
   545  
   546  func (w *indexMergeProcessWorker) handleLoopFetcherPanic(ctx context.Context, resultCh chan<- *lookupBlockTask) func(r interface{}) {
   547  	return func(r interface{}) {
   548  		if r == nil {
   549  			return
   550  		}
   551  
   552  		err4Panic := errors.Errorf("panic in IndexMergeReaderInterlockingDirectorate indexMergeBlockWorker: %v", r)
   553  		logutil.Logger(ctx).Error(err4Panic.Error())
   554  		doneCh := make(chan error, 1)
   555  		doneCh <- err4Panic
   556  		resultCh <- &lookupBlockTask{
   557  			doneCh: doneCh,
   558  		}
   559  	}
   560  }
   561  
   562  type partialIndexWorker struct {
   563  	sc           stochastikctx.Context
   564  	batchSize    int
   565  	maxBatchSize int
   566  	maxChunkSize int
   567  }
   568  
   569  func (w *partialIndexWorker) fetchHandles(
   570  	ctx context.Context,
   571  	result allegrosql.SelectResult,
   572  	exitCh <-chan struct{},
   573  	fetchCh chan<- *lookupBlockTask,
   574  	resultCh chan<- *lookupBlockTask,
   575  	finished <-chan struct{},
   576  	handleDefCauss causetembedded.HandleDefCauss) (count int64, err error) {
   577  	chk := chunk.NewChunkWithCapacity(handleDefCauss.GetFieldsTypes(), w.maxChunkSize)
   578  	for {
   579  		handles, retChunk, err := w.extractTaskHandles(ctx, chk, result, handleDefCauss)
   580  		if err != nil {
   581  			doneCh := make(chan error, 1)
   582  			doneCh <- err
   583  			resultCh <- &lookupBlockTask{
   584  				doneCh: doneCh,
   585  			}
   586  			return count, err
   587  		}
   588  		if len(handles) == 0 {
   589  			return count, nil
   590  		}
   591  		count += int64(len(handles))
   592  		task := w.buildBlockTask(handles, retChunk)
   593  		select {
   594  		case <-ctx.Done():
   595  			return count, ctx.Err()
   596  		case <-exitCh:
   597  			return count, nil
   598  		case <-finished:
   599  			return count, nil
   600  		case fetchCh <- task:
   601  		}
   602  	}
   603  }
   604  
   605  func (w *partialIndexWorker) extractTaskHandles(ctx context.Context, chk *chunk.Chunk, idxResult allegrosql.SelectResult, handleDefCauss causetembedded.HandleDefCauss) (
   606  	handles []ekv.Handle, retChk *chunk.Chunk, err error) {
   607  	handles = make([]ekv.Handle, 0, w.batchSize)
   608  	for len(handles) < w.batchSize {
   609  		chk.SetRequiredEvents(w.batchSize-len(handles), w.maxChunkSize)
   610  		err = errors.Trace(idxResult.Next(ctx, chk))
   611  		if err != nil {
   612  			return handles, nil, err
   613  		}
   614  		if chk.NumEvents() == 0 {
   615  			return handles, retChk, nil
   616  		}
   617  		for i := 0; i < chk.NumEvents(); i++ {
   618  			handle, err := handleDefCauss.BuildHandleFromIndexEvent(chk.GetEvent(i))
   619  			if err != nil {
   620  				return nil, nil, err
   621  			}
   622  			handles = append(handles, handle)
   623  		}
   624  	}
   625  	w.batchSize *= 2
   626  	if w.batchSize > w.maxBatchSize {
   627  		w.batchSize = w.maxBatchSize
   628  	}
   629  	return handles, retChk, nil
   630  }
   631  
   632  func (w *partialIndexWorker) buildBlockTask(handles []ekv.Handle, retChk *chunk.Chunk) *lookupBlockTask {
   633  	task := &lookupBlockTask{
   634  		handles:   handles,
   635  		idxEvents: retChk,
   636  	}
   637  
   638  	task.doneCh = make(chan error, 1)
   639  	return task
   640  }
   641  
   642  type indexMergeBlockScanWorker struct {
   643  	workCh         <-chan *lookupBlockTask
   644  	finished       <-chan struct{}
   645  	buildTblReader func(ctx context.Context, handles []ekv.Handle) (InterlockingDirectorate, error)
   646  	tblCausets     []causetembedded.PhysicalCauset
   647  
   648  	// memTracker is used to track the memory usage of this interlock.
   649  	memTracker *memory.Tracker
   650  }
   651  
   652  func (w *indexMergeBlockScanWorker) pickAndInterDircTask(ctx context.Context) (task *lookupBlockTask) {
   653  	var ok bool
   654  	for {
   655  		select {
   656  		case task, ok = <-w.workCh:
   657  			if !ok {
   658  				return
   659  			}
   660  		case <-w.finished:
   661  			return
   662  		}
   663  		err := w.executeTask(ctx, task)
   664  		task.doneCh <- err
   665  	}
   666  }
   667  
   668  func (w *indexMergeBlockScanWorker) handlePickAndInterDircTaskPanic(ctx context.Context, task *lookupBlockTask) func(r interface{}) {
   669  	return func(r interface{}) {
   670  		if r == nil {
   671  			return
   672  		}
   673  
   674  		err4Panic := errors.Errorf("panic in IndexMergeReaderInterlockingDirectorate indexMergeBlockWorker: %v", r)
   675  		logutil.Logger(ctx).Error(err4Panic.Error())
   676  		task.doneCh <- err4Panic
   677  	}
   678  }
   679  
   680  func (w *indexMergeBlockScanWorker) executeTask(ctx context.Context, task *lookupBlockTask) error {
   681  	blockReader, err := w.buildTblReader(ctx, task.handles)
   682  	if err != nil {
   683  		logutil.Logger(ctx).Error("build causet reader failed", zap.Error(err))
   684  		return err
   685  	}
   686  	defer terror.Call(blockReader.Close)
   687  	task.memTracker = w.memTracker
   688  	memUsage := int64(cap(task.handles) * 8)
   689  	task.memUsage = memUsage
   690  	task.memTracker.Consume(memUsage)
   691  	handleCnt := len(task.handles)
   692  	task.rows = make([]chunk.Event, 0, handleCnt)
   693  	for {
   694  		chk := newFirstChunk(blockReader)
   695  		err = Next(ctx, blockReader, chk)
   696  		if err != nil {
   697  			logutil.Logger(ctx).Error("causet reader fetch next chunk failed", zap.Error(err))
   698  			return err
   699  		}
   700  		if chk.NumEvents() == 0 {
   701  			break
   702  		}
   703  		memUsage = chk.MemoryUsage()
   704  		task.memUsage += memUsage
   705  		task.memTracker.Consume(memUsage)
   706  		iter := chunk.NewIterator4Chunk(chk)
   707  		for event := iter.Begin(); event != iter.End(); event = iter.Next() {
   708  			task.rows = append(task.rows, event)
   709  		}
   710  	}
   711  
   712  	memUsage = int64(cap(task.rows)) * int64(unsafe.Sizeof(chunk.Event{}))
   713  	task.memUsage += memUsage
   714  	task.memTracker.Consume(memUsage)
   715  	if handleCnt != len(task.rows) && len(w.tblCausets) == 1 {
   716  		return errors.Errorf("handle count %d isn't equal to value count %d", handleCnt, len(task.rows))
   717  	}
   718  	return nil
   719  }