github.com/KinWaiYuen/client-go/v2@v2.5.4/txnkv/txnsnapshot/client_helper.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/client_helper.go
    19  //
    20  
    21  // Copyright 2021 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 txnsnapshot
    36  
    37  import (
    38  	"time"
    39  
    40  	"github.com/KinWaiYuen/client-go/v2/internal/client"
    41  	"github.com/KinWaiYuen/client-go/v2/internal/locate"
    42  	"github.com/KinWaiYuen/client-go/v2/internal/retry"
    43  	"github.com/KinWaiYuen/client-go/v2/tikvrpc"
    44  	"github.com/KinWaiYuen/client-go/v2/txnkv/txnlock"
    45  	"github.com/KinWaiYuen/client-go/v2/util"
    46  )
    47  
    48  // ClientHelper wraps LockResolver and RegionRequestSender.
    49  // It's introduced to support the new lock resolving pattern in the large transaction.
    50  // In the large transaction protocol, sending requests and resolving locks are
    51  // context-dependent. For example, when a send request meets a secondary lock, we'll
    52  // call ResolveLock, and if the lock belongs to a large transaction, we may retry
    53  // the request. If there is no context information about the resolved locks, we'll
    54  // meet the secondary lock again and run into a deadloop.
    55  type ClientHelper struct {
    56  	lockResolver  *txnlock.LockResolver
    57  	regionCache   *locate.RegionCache
    58  	resolvedLocks *util.TSSet
    59  	client        client.Client
    60  	resolveLite   bool
    61  	locate.RegionRequestRuntimeStats
    62  }
    63  
    64  // NewClientHelper creates a helper instance.
    65  func NewClientHelper(store kvstore, resolvedLocks *util.TSSet, resolveLite bool) *ClientHelper {
    66  	return &ClientHelper{
    67  		lockResolver:  store.GetLockResolver(),
    68  		regionCache:   store.GetRegionCache(),
    69  		resolvedLocks: resolvedLocks,
    70  		client:        store.GetTiKVClient(),
    71  		resolveLite:   resolveLite,
    72  	}
    73  }
    74  
    75  // ResolveLocks wraps the ResolveLocks function and store the resolved result.
    76  func (ch *ClientHelper) ResolveLocks(bo *retry.Backoffer, callerStartTS uint64, locks []*txnlock.Lock) (int64, error) {
    77  	var err error
    78  	var resolvedLocks []uint64
    79  	var msBeforeTxnExpired int64
    80  	if ch.Stats != nil {
    81  		defer func(start time.Time) {
    82  			locate.RecordRegionRequestRuntimeStats(ch.Stats, tikvrpc.CmdResolveLock, time.Since(start))
    83  		}(time.Now())
    84  	}
    85  	if ch.resolveLite {
    86  		msBeforeTxnExpired, resolvedLocks, err = ch.lockResolver.ResolveLocksLite(bo, callerStartTS, locks)
    87  	} else {
    88  		msBeforeTxnExpired, resolvedLocks, err = ch.lockResolver.ResolveLocks(bo, callerStartTS, locks)
    89  	}
    90  	if err != nil {
    91  		return msBeforeTxnExpired, err
    92  	}
    93  	if len(resolvedLocks) > 0 {
    94  		ch.resolvedLocks.Put(resolvedLocks...)
    95  		return 0, nil
    96  	}
    97  	return msBeforeTxnExpired, nil
    98  }
    99  
   100  // SendReqCtx wraps the SendReqCtx function and use the resolved lock result in the kvrpcpb.Context.
   101  func (ch *ClientHelper) SendReqCtx(bo *retry.Backoffer, req *tikvrpc.Request, regionID locate.RegionVerID, timeout time.Duration, et tikvrpc.EndpointType, directStoreAddr string, opts ...locate.StoreSelectorOption) (*tikvrpc.Response, *locate.RPCContext, string, error) {
   102  	sender := locate.NewRegionRequestSender(ch.regionCache, ch.client)
   103  	if len(directStoreAddr) > 0 {
   104  		sender.SetStoreAddr(directStoreAddr)
   105  	}
   106  	sender.Stats = ch.Stats
   107  	req.Context.ResolvedLocks = ch.resolvedLocks.GetAll()
   108  	resp, ctx, err := sender.SendReqCtx(bo, req, regionID, timeout, et, opts...)
   109  	return resp, ctx, sender.GetStoreAddr(), err
   110  }