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 }