github.com/KinWaiYuen/client-go/v2@v2.5.4/txnkv/rangetask/delete_range.go (about) 1 // Copyright 2021 TiKV Authors 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // NOTE: The code in this file is based on code from the 16 // TiDB project, licensed under the Apache License v 2.0 17 // 18 // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/delete_range.go 19 // 20 21 // Copyright 2018 PingCAP, Inc. 22 // 23 // Licensed under the Apache License, Version 2.0 (the "License"); 24 // you may not use this file except in compliance with the License. 25 // You may obtain a copy of the License at 26 // 27 // http://www.apache.org/licenses/LICENSE-2.0 28 // 29 // Unless required by applicable law or agreed to in writing, software 30 // distributed under the License is distributed on an "AS IS" BASIS, 31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package rangetask 36 37 import ( 38 "bytes" 39 "context" 40 "time" 41 42 tikverr "github.com/KinWaiYuen/client-go/v2/error" 43 "github.com/KinWaiYuen/client-go/v2/internal/client" 44 "github.com/KinWaiYuen/client-go/v2/internal/locate" 45 "github.com/KinWaiYuen/client-go/v2/internal/retry" 46 "github.com/KinWaiYuen/client-go/v2/kv" 47 "github.com/KinWaiYuen/client-go/v2/tikvrpc" 48 "github.com/pingcap/errors" 49 "github.com/pingcap/kvproto/pkg/kvrpcpb" 50 ) 51 52 type storage interface { 53 // GetRegionCache gets the RegionCache. 54 GetRegionCache() *locate.RegionCache 55 // SendReq sends a request to TiKV. 56 SendReq(bo *retry.Backoffer, req *tikvrpc.Request, regionID locate.RegionVerID, timeout time.Duration) (*tikvrpc.Response, error) 57 } 58 59 // DeleteRangeTask is used to delete all keys in a range. After 60 // performing DeleteRange, it keeps how many ranges it affects and 61 // if the task was canceled or not. 62 type DeleteRangeTask struct { 63 completedRegions int 64 store storage 65 startKey []byte 66 endKey []byte 67 notifyOnly bool 68 concurrency int 69 } 70 71 // NewDeleteRangeTask creates a DeleteRangeTask. Deleting will be performed when `Execute` method is invoked. 72 // Be careful while using this API. This API doesn't keep recent MVCC versions, but will delete all versions of all keys 73 // in the range immediately. Also notice that frequent invocation to this API may cause performance problems to TiKV. 74 func NewDeleteRangeTask(store storage, startKey []byte, endKey []byte, concurrency int) *DeleteRangeTask { 75 return &DeleteRangeTask{ 76 completedRegions: 0, 77 store: store, 78 startKey: startKey, 79 endKey: endKey, 80 notifyOnly: false, 81 concurrency: concurrency, 82 } 83 } 84 85 // NewNotifyDeleteRangeTask creates a task that sends delete range requests to all regions in the range, but with the 86 // flag `notifyOnly` set. TiKV will not actually delete the range after receiving request, but it will be replicated via 87 // raft. This is used to notify the involved regions before sending UnsafeDestroyRange requests. 88 func NewNotifyDeleteRangeTask(store storage, startKey []byte, endKey []byte, concurrency int) *DeleteRangeTask { 89 task := NewDeleteRangeTask(store, startKey, endKey, concurrency) 90 task.notifyOnly = true 91 return task 92 } 93 94 // getRunnerName returns a name for RangeTaskRunner. 95 func (t *DeleteRangeTask) getRunnerName() string { 96 if t.notifyOnly { 97 return "delete-range-notify" 98 } 99 return "delete-range" 100 } 101 102 // Execute performs the delete range operation. 103 func (t *DeleteRangeTask) Execute(ctx context.Context) error { 104 runnerName := t.getRunnerName() 105 106 runner := NewRangeTaskRunner(runnerName, t.store, t.concurrency, t.sendReqOnRange) 107 err := runner.RunOnRange(ctx, t.startKey, t.endKey) 108 t.completedRegions = runner.CompletedRegions() 109 110 return err 111 } 112 113 const deleteRangeOneRegionMaxBackoff = 100000 114 115 // Execute performs the delete range operation. 116 func (t *DeleteRangeTask) sendReqOnRange(ctx context.Context, r kv.KeyRange) (TaskStat, error) { 117 startKey, rangeEndKey := r.StartKey, r.EndKey 118 var stat TaskStat 119 for { 120 select { 121 case <-ctx.Done(): 122 return stat, errors.Trace(ctx.Err()) 123 default: 124 } 125 126 if bytes.Compare(startKey, rangeEndKey) >= 0 { 127 break 128 } 129 130 bo := retry.NewBackofferWithVars(ctx, deleteRangeOneRegionMaxBackoff, nil) 131 loc, err := t.store.GetRegionCache().LocateKey(bo, startKey) 132 if err != nil { 133 return stat, errors.Trace(err) 134 } 135 136 // Delete to the end of the region, except if it's the last region overlapping the range 137 endKey := loc.EndKey 138 // If it is the last region 139 if loc.Contains(rangeEndKey) { 140 endKey = rangeEndKey 141 } 142 143 req := tikvrpc.NewRequest(tikvrpc.CmdDeleteRange, &kvrpcpb.DeleteRangeRequest{ 144 StartKey: startKey, 145 EndKey: endKey, 146 NotifyOnly: t.notifyOnly, 147 }) 148 149 resp, err := t.store.SendReq(bo, req, loc.Region, client.ReadTimeoutMedium) 150 if err != nil { 151 return stat, errors.Trace(err) 152 } 153 regionErr, err := resp.GetRegionError() 154 if err != nil { 155 return stat, errors.Trace(err) 156 } 157 if regionErr != nil { 158 err = bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String())) 159 if err != nil { 160 return stat, errors.Trace(err) 161 } 162 continue 163 } 164 if resp.Resp == nil { 165 return stat, errors.Trace(tikverr.ErrBodyMissing) 166 } 167 deleteRangeResp := resp.Resp.(*kvrpcpb.DeleteRangeResponse) 168 if err := deleteRangeResp.GetError(); err != "" { 169 return stat, errors.Errorf("unexpected delete range err: %v", err) 170 } 171 stat.CompletedRegions++ 172 startKey = endKey 173 } 174 175 return stat, nil 176 } 177 178 // CompletedRegions returns the number of regions that are affected by this delete range task 179 func (t *DeleteRangeTask) CompletedRegions() int { 180 return t.completedRegions 181 }