github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/milevadb-server/einsteindb/delete_range.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 einsteindb
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  
    20  	"github.com/whtcorpsinc/ekvproto/pkg/ekvrpcpb"
    21  	"github.com/whtcorpsinc/errors"
    22  	"github.com/whtcorpsinc/milevadb/causetstore/einsteindb/einsteindbrpc"
    23  	"github.com/whtcorpsinc/milevadb/ekv"
    24  )
    25  
    26  // DeleteRangeTask is used to delete all keys in a range. After
    27  // performing DeleteRange, it keeps how many ranges it affects and
    28  // if the task was canceled or not.
    29  type DeleteRangeTask struct {
    30  	completedRegions int
    31  	causetstore      CausetStorage
    32  	startKey         []byte
    33  	endKey           []byte
    34  	notifyOnly       bool
    35  	concurrency      int
    36  }
    37  
    38  // NewDeleteRangeTask creates a DeleteRangeTask. Deleting will be performed when `InterDircute` method is invoked.
    39  // Be careful while using this API. This API doesn't keep recent MVCC versions, but will delete all versions of all keys
    40  // in the range immediately. Also notice that frequent invocation to this API may cause performance problems to EinsteinDB.
    41  func NewDeleteRangeTask(causetstore CausetStorage, startKey []byte, endKey []byte, concurrency int) *DeleteRangeTask {
    42  	return &DeleteRangeTask{
    43  		completedRegions: 0,
    44  		causetstore:      causetstore,
    45  		startKey:         startKey,
    46  		endKey:           endKey,
    47  		notifyOnly:       false,
    48  		concurrency:      concurrency,
    49  	}
    50  }
    51  
    52  // NewNotifyDeleteRangeTask creates a task that sends delete range requests to all regions in the range, but with the
    53  // flag `notifyOnly` set. EinsteinDB will not actually delete the range after receiving request, but it will be replicated via
    54  // raft. This is used to notify the involved regions before sending UnsafeDestroyRange requests.
    55  func NewNotifyDeleteRangeTask(causetstore CausetStorage, startKey []byte, endKey []byte, concurrency int) *DeleteRangeTask {
    56  	task := NewDeleteRangeTask(causetstore, startKey, endKey, concurrency)
    57  	task.notifyOnly = true
    58  	return task
    59  }
    60  
    61  // getRunnerName returns a name for RangeTaskRunner.
    62  func (t *DeleteRangeTask) getRunnerName() string {
    63  	if t.notifyOnly {
    64  		return "delete-range-notify"
    65  	}
    66  	return "delete-range"
    67  }
    68  
    69  // InterDircute performs the delete range operation.
    70  func (t *DeleteRangeTask) InterDircute(ctx context.Context) error {
    71  	runnerName := t.getRunnerName()
    72  
    73  	runner := NewRangeTaskRunner(runnerName, t.causetstore, t.concurrency, t.sendReqOnRange)
    74  	err := runner.RunOnRange(ctx, t.startKey, t.endKey)
    75  	t.completedRegions = runner.CompletedRegions()
    76  
    77  	return err
    78  }
    79  
    80  // InterDircute performs the delete range operation.
    81  func (t *DeleteRangeTask) sendReqOnRange(ctx context.Context, r ekv.KeyRange) (RangeTaskStat, error) {
    82  	startKey, rangeEndKey := r.StartKey, r.EndKey
    83  	var stat RangeTaskStat
    84  	for {
    85  		select {
    86  		case <-ctx.Done():
    87  			return stat, errors.Trace(ctx.Err())
    88  		default:
    89  		}
    90  
    91  		if bytes.Compare(startKey, rangeEndKey) >= 0 {
    92  			break
    93  		}
    94  
    95  		bo := NewBackofferWithVars(ctx, deleteRangeOneRegionMaxBackoff, nil)
    96  		loc, err := t.causetstore.GetRegionCache().LocateKey(bo, startKey)
    97  		if err != nil {
    98  			return stat, errors.Trace(err)
    99  		}
   100  
   101  		// Delete to the end of the region, except if it's the last region overlapping the range
   102  		endKey := loc.EndKey
   103  		// If it is the last region
   104  		if loc.Contains(rangeEndKey) {
   105  			endKey = rangeEndKey
   106  		}
   107  
   108  		req := einsteindbrpc.NewRequest(einsteindbrpc.CmdDeleteRange, &ekvrpcpb.DeleteRangeRequest{
   109  			StartKey:   startKey,
   110  			EndKey:     endKey,
   111  			NotifyOnly: t.notifyOnly,
   112  		})
   113  
   114  		resp, err := t.causetstore.SendReq(bo, req, loc.Region, ReadTimeoutMedium)
   115  		if err != nil {
   116  			return stat, errors.Trace(err)
   117  		}
   118  		regionErr, err := resp.GetRegionError()
   119  		if err != nil {
   120  			return stat, errors.Trace(err)
   121  		}
   122  		if regionErr != nil {
   123  			err = bo.Backoff(BoRegionMiss, errors.New(regionErr.String()))
   124  			if err != nil {
   125  				return stat, errors.Trace(err)
   126  			}
   127  			continue
   128  		}
   129  		if resp.Resp == nil {
   130  			return stat, errors.Trace(ErrBodyMissing)
   131  		}
   132  		deleteRangeResp := resp.Resp.(*ekvrpcpb.DeleteRangeResponse)
   133  		if err := deleteRangeResp.GetError(); err != "" {
   134  			return stat, errors.Errorf("unexpected delete range err: %v", err)
   135  		}
   136  		stat.CompletedRegions++
   137  		startKey = endKey
   138  	}
   139  
   140  	return stat, nil
   141  }
   142  
   143  // CompletedRegions returns the number of regions that are affected by this delete range task
   144  func (t *DeleteRangeTask) CompletedRegions() int {
   145  	return t.completedRegions
   146  }