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  }