github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/pkg/txnutil/lock_resolver.go (about) 1 // Copyright 2020 PingCAP, 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 txnutil 15 16 import ( 17 "bytes" 18 "context" 19 20 "github.com/pingcap/errors" 21 "github.com/pingcap/kvproto/pkg/kvrpcpb" 22 "github.com/pingcap/log" 23 "github.com/pingcap/tidb/store/tikv" 24 "github.com/pingcap/tidb/store/tikv/tikvrpc" 25 "go.uber.org/zap" 26 ) 27 28 // LockResolver resolves lock in the given region. 29 type LockResolver interface { 30 Resolve(ctx context.Context, regionID uint64, maxVersion uint64) error 31 } 32 33 type resolver struct { 34 kvStorage tikv.Storage 35 } 36 37 // NewLockerResolver returns a LockResolver. 38 func NewLockerResolver(kvStorage tikv.Storage) LockResolver { 39 return &resolver{ 40 kvStorage: kvStorage, 41 } 42 } 43 44 const scanLockLimit = 1024 45 46 func (r *resolver) Resolve(ctx context.Context, regionID uint64, maxVersion uint64) error { 47 // TODO test whether this function will kill active transaction 48 49 req := tikvrpc.NewRequest(tikvrpc.CmdScanLock, &kvrpcpb.ScanLockRequest{ 50 MaxVersion: maxVersion, 51 Limit: scanLockLimit, 52 }) 53 54 bo := tikv.NewBackoffer(ctx, tikv.GcResolveLockMaxBackoff) 55 var loc *tikv.KeyLocation 56 var key []byte 57 flushRegion := func() error { 58 var err error 59 loc, err = r.kvStorage.GetRegionCache().LocateRegionByID(bo, regionID) 60 if err != nil { 61 return err 62 } 63 key = loc.StartKey 64 return nil 65 } 66 if err := flushRegion(); err != nil { 67 return errors.Trace(err) 68 } 69 for { 70 select { 71 case <-ctx.Done(): 72 return ctx.Err() 73 default: 74 } 75 req.ScanLock().StartKey = key 76 resp, err := r.kvStorage.SendReq(bo, req, loc.Region, tikv.ReadTimeoutMedium) 77 if err != nil { 78 return errors.Trace(err) 79 } 80 regionErr, err := resp.GetRegionError() 81 if err != nil { 82 return errors.Trace(err) 83 } 84 if regionErr != nil { 85 err = bo.Backoff(tikv.BoRegionMiss, errors.New(regionErr.String())) 86 if err != nil { 87 return errors.Trace(err) 88 } 89 if err := flushRegion(); err != nil { 90 return errors.Trace(err) 91 } 92 continue 93 } 94 if resp.Resp == nil { 95 return errors.Trace(tikv.ErrBodyMissing) 96 } 97 locksResp := resp.Resp.(*kvrpcpb.ScanLockResponse) 98 if locksResp.GetError() != nil { 99 return errors.Errorf("unexpected scanlock error: %s", locksResp) 100 } 101 locksInfo := locksResp.GetLocks() 102 locks := make([]*tikv.Lock, len(locksInfo)) 103 for i := range locksInfo { 104 locks[i] = tikv.NewLock(locksInfo[i]) 105 } 106 107 _, _, err1 := r.kvStorage.GetLockResolver().ResolveLocks(bo, 0, locks) 108 if err1 != nil { 109 return errors.Trace(err1) 110 } 111 if len(locks) < scanLockLimit { 112 key = loc.EndKey 113 } else { 114 key = locks[len(locks)-1].Key 115 } 116 117 if len(key) == 0 || (len(loc.EndKey) != 0 && bytes.Compare(key, loc.EndKey) >= 0) { 118 break 119 } 120 bo = tikv.NewBackoffer(ctx, tikv.GcResolveLockMaxBackoff) 121 } 122 log.Info("resolve lock successfully", zap.Uint64("regionID", regionID), zap.Uint64("maxVersion", maxVersion)) 123 return nil 124 }