github.com/KinWaiYuen/client-go/v2@v2.5.4/rawkv/rawkv.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/rawkv.go
    19  //
    20  
    21  // Copyright 2016 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 rawkv
    36  
    37  import (
    38  	"bytes"
    39  	"context"
    40  	"time"
    41  
    42  	"github.com/KinWaiYuen/client-go/v2/config"
    43  	tikverr "github.com/KinWaiYuen/client-go/v2/error"
    44  	"github.com/KinWaiYuen/client-go/v2/internal/client"
    45  	"github.com/KinWaiYuen/client-go/v2/internal/kvrpc"
    46  	"github.com/KinWaiYuen/client-go/v2/internal/locate"
    47  	"github.com/KinWaiYuen/client-go/v2/internal/logutil"
    48  	"github.com/KinWaiYuen/client-go/v2/internal/retry"
    49  	"github.com/KinWaiYuen/client-go/v2/metrics"
    50  	"github.com/KinWaiYuen/client-go/v2/tikvrpc"
    51  	"github.com/pingcap/errors"
    52  	"github.com/pingcap/kvproto/pkg/kvrpcpb"
    53  	pd "github.com/tikv/pd/client"
    54  	"go.uber.org/zap"
    55  )
    56  
    57  var (
    58  	// MaxRawKVScanLimit is the maximum scan limit for rawkv Scan.
    59  	MaxRawKVScanLimit = 10240
    60  	// ErrMaxScanLimitExceeded is returned when the limit for rawkv Scan is to large.
    61  	ErrMaxScanLimitExceeded = errors.New("limit should be less than MaxRawKVScanLimit")
    62  )
    63  
    64  const (
    65  	// rawBatchPutSize is the maximum size limit for rawkv each batch put request.
    66  	rawBatchPutSize = 16 * 1024
    67  	// rawBatchPairCount is the maximum limit for rawkv each batch get/delete request.
    68  	rawBatchPairCount = 512
    69  )
    70  
    71  // Client is a client of TiKV server which is used as a key-value storage,
    72  // only GET/PUT/DELETE commands are supported.
    73  type Client struct {
    74  	clusterID   uint64
    75  	regionCache *locate.RegionCache
    76  	pdClient    pd.Client
    77  	rpcClient   client.Client
    78  	atomic      bool
    79  }
    80  
    81  // SetAtomicForCAS sets atomic mode for CompareAndSwap
    82  func (c *Client) SetAtomicForCAS(b bool) *Client {
    83  	c.atomic = b
    84  	return c
    85  }
    86  
    87  // NewClient creates a client with PD cluster addrs.
    88  func NewClient(ctx context.Context, pdAddrs []string, security config.Security, opts ...pd.ClientOption) (*Client, error) {
    89  	pdCli, err := pd.NewClient(pdAddrs, pd.SecurityOption{
    90  		CAPath:   security.ClusterSSLCA,
    91  		CertPath: security.ClusterSSLCert,
    92  		KeyPath:  security.ClusterSSLKey,
    93  	}, opts...)
    94  	if err != nil {
    95  		return nil, errors.Trace(err)
    96  	}
    97  	return &Client{
    98  		clusterID:   pdCli.GetClusterID(ctx),
    99  		regionCache: locate.NewRegionCache(pdCli),
   100  		pdClient:    pdCli,
   101  		rpcClient:   client.NewRPCClient(client.WithSecurity(security)),
   102  	}, nil
   103  }
   104  
   105  // Close closes the client.
   106  func (c *Client) Close() error {
   107  	if c.pdClient != nil {
   108  		c.pdClient.Close()
   109  	}
   110  	if c.regionCache != nil {
   111  		c.regionCache.Close()
   112  	}
   113  	if c.rpcClient == nil {
   114  		return nil
   115  	}
   116  	return c.rpcClient.Close()
   117  }
   118  
   119  // ClusterID returns the TiKV cluster ID.
   120  func (c *Client) ClusterID() uint64 {
   121  	return c.clusterID
   122  }
   123  
   124  // Get queries value with the key. When the key does not exist, it returns `nil, nil`.
   125  func (c *Client) Get(ctx context.Context, key []byte) ([]byte, error) {
   126  	start := time.Now()
   127  	defer func() { metrics.RawkvCmdHistogramWithGet.Observe(time.Since(start).Seconds()) }()
   128  
   129  	req := tikvrpc.NewRequest(tikvrpc.CmdRawGet, &kvrpcpb.RawGetRequest{Key: key})
   130  	resp, _, err := c.sendReq(ctx, key, req, false)
   131  	if err != nil {
   132  		return nil, errors.Trace(err)
   133  	}
   134  	if resp.Resp == nil {
   135  		return nil, errors.Trace(tikverr.ErrBodyMissing)
   136  	}
   137  	cmdResp := resp.Resp.(*kvrpcpb.RawGetResponse)
   138  	if cmdResp.GetError() != "" {
   139  		return nil, errors.New(cmdResp.GetError())
   140  	}
   141  	if len(cmdResp.Value) == 0 {
   142  		return nil, nil
   143  	}
   144  	return cmdResp.Value, nil
   145  }
   146  
   147  const rawkvMaxBackoff = 20000
   148  
   149  // BatchGet queries values with the keys.
   150  func (c *Client) BatchGet(ctx context.Context, keys [][]byte) ([][]byte, error) {
   151  	start := time.Now()
   152  	defer func() {
   153  		metrics.RawkvCmdHistogramWithBatchGet.Observe(time.Since(start).Seconds())
   154  	}()
   155  
   156  	bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil)
   157  	resp, err := c.sendBatchReq(bo, keys, tikvrpc.CmdRawBatchGet)
   158  	if err != nil {
   159  		return nil, errors.Trace(err)
   160  	}
   161  
   162  	if resp.Resp == nil {
   163  		return nil, errors.Trace(tikverr.ErrBodyMissing)
   164  	}
   165  	cmdResp := resp.Resp.(*kvrpcpb.RawBatchGetResponse)
   166  
   167  	keyToValue := make(map[string][]byte, len(keys))
   168  	for _, pair := range cmdResp.Pairs {
   169  		keyToValue[string(pair.Key)] = pair.Value
   170  	}
   171  
   172  	values := make([][]byte, len(keys))
   173  	for i, key := range keys {
   174  		values[i] = keyToValue[string(key)]
   175  	}
   176  	return values, nil
   177  }
   178  
   179  // PutWithTTL stores a key-value pair to TiKV with a time-to-live duration.
   180  func (c *Client) PutWithTTL(ctx context.Context, key, value []byte, ttl uint64) error {
   181  	start := time.Now()
   182  	defer func() { metrics.RawkvCmdHistogramWithBatchPut.Observe(time.Since(start).Seconds()) }()
   183  	metrics.RawkvSizeHistogramWithKey.Observe(float64(len(key)))
   184  	metrics.RawkvSizeHistogramWithValue.Observe(float64(len(value)))
   185  
   186  	if len(value) == 0 {
   187  		return errors.New("empty value is not supported")
   188  	}
   189  
   190  	req := tikvrpc.NewRequest(tikvrpc.CmdRawPut, &kvrpcpb.RawPutRequest{
   191  		Key:    key,
   192  		Value:  value,
   193  		Ttl:    ttl,
   194  		ForCas: c.atomic,
   195  	})
   196  	resp, _, err := c.sendReq(ctx, key, req, false)
   197  	if err != nil {
   198  		return errors.Trace(err)
   199  	}
   200  	if resp.Resp == nil {
   201  		return errors.Trace(tikverr.ErrBodyMissing)
   202  	}
   203  	cmdResp := resp.Resp.(*kvrpcpb.RawPutResponse)
   204  	if cmdResp.GetError() != "" {
   205  		return errors.New(cmdResp.GetError())
   206  	}
   207  	return nil
   208  }
   209  
   210  // GetKeyTTL get the TTL of a raw key from TiKV if key exists
   211  func (c *Client) GetKeyTTL(ctx context.Context, key []byte) (*uint64, error) {
   212  	var ttl uint64
   213  	metrics.RawkvSizeHistogramWithKey.Observe(float64(len(key)))
   214  	req := tikvrpc.NewRequest(tikvrpc.CmdGetKeyTTL, &kvrpcpb.RawGetKeyTTLRequest{
   215  		Key: key,
   216  	})
   217  	resp, _, err := c.sendReq(ctx, key, req, false)
   218  
   219  	if err != nil {
   220  		return nil, errors.Trace(err)
   221  	}
   222  	if resp.Resp == nil {
   223  		return nil, errors.Trace(tikverr.ErrBodyMissing)
   224  	}
   225  
   226  	cmdResp := resp.Resp.(*kvrpcpb.RawGetKeyTTLResponse)
   227  	if cmdResp.GetError() != "" {
   228  		return nil, errors.New(cmdResp.GetError())
   229  	}
   230  
   231  	if cmdResp.GetNotFound() {
   232  		return nil, nil
   233  	}
   234  
   235  	ttl = cmdResp.GetTtl()
   236  	return &ttl, nil
   237  }
   238  
   239  // Put stores a key-value pair to TiKV.
   240  func (c *Client) Put(ctx context.Context, key, value []byte) error {
   241  	return c.PutWithTTL(ctx, key, value, 0)
   242  }
   243  
   244  // BatchPut stores key-value pairs to TiKV.
   245  func (c *Client) BatchPut(ctx context.Context, keys, values [][]byte, ttls []uint64) error {
   246  	start := time.Now()
   247  	defer func() {
   248  		metrics.RawkvCmdHistogramWithBatchPut.Observe(time.Since(start).Seconds())
   249  	}()
   250  
   251  	if len(keys) != len(values) {
   252  		return errors.New("the len of keys is not equal to the len of values")
   253  	}
   254  	if len(ttls) > 0 && len(keys) != len(ttls) {
   255  		return errors.New("the len of ttls is not equal to the len of values")
   256  	}
   257  	for _, value := range values {
   258  		if len(value) == 0 {
   259  			return errors.New("empty value is not supported")
   260  		}
   261  	}
   262  	bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil)
   263  	err := c.sendBatchPut(bo, keys, values, ttls)
   264  	return errors.Trace(err)
   265  }
   266  
   267  // Delete deletes a key-value pair from TiKV.
   268  func (c *Client) Delete(ctx context.Context, key []byte) error {
   269  	start := time.Now()
   270  	defer func() { metrics.RawkvCmdHistogramWithDelete.Observe(time.Since(start).Seconds()) }()
   271  
   272  	req := tikvrpc.NewRequest(tikvrpc.CmdRawDelete, &kvrpcpb.RawDeleteRequest{
   273  		Key:    key,
   274  		ForCas: c.atomic,
   275  	})
   276  	req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds())
   277  	resp, _, err := c.sendReq(ctx, key, req, false)
   278  	if err != nil {
   279  		return errors.Trace(err)
   280  	}
   281  	if resp.Resp == nil {
   282  		return errors.Trace(tikverr.ErrBodyMissing)
   283  	}
   284  	cmdResp := resp.Resp.(*kvrpcpb.RawDeleteResponse)
   285  	if cmdResp.GetError() != "" {
   286  		return errors.New(cmdResp.GetError())
   287  	}
   288  	return nil
   289  }
   290  
   291  // BatchDelete deletes key-value pairs from TiKV.
   292  func (c *Client) BatchDelete(ctx context.Context, keys [][]byte) error {
   293  	start := time.Now()
   294  	defer func() {
   295  		metrics.RawkvCmdHistogramWithBatchDelete.Observe(time.Since(start).Seconds())
   296  	}()
   297  
   298  	bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil)
   299  	resp, err := c.sendBatchReq(bo, keys, tikvrpc.CmdRawBatchDelete)
   300  	if err != nil {
   301  		return errors.Trace(err)
   302  	}
   303  	if resp.Resp == nil {
   304  		return errors.Trace(tikverr.ErrBodyMissing)
   305  	}
   306  	cmdResp := resp.Resp.(*kvrpcpb.RawBatchDeleteResponse)
   307  	if cmdResp.GetError() != "" {
   308  		return errors.New(cmdResp.GetError())
   309  	}
   310  	return nil
   311  }
   312  
   313  // DeleteRange deletes all key-value pairs in a range from TiKV.
   314  func (c *Client) DeleteRange(ctx context.Context, startKey []byte, endKey []byte) error {
   315  	start := time.Now()
   316  	var err error
   317  	defer func() {
   318  		var label = "delete_range"
   319  		if err != nil {
   320  			label += "_error"
   321  		}
   322  		metrics.TiKVRawkvCmdHistogram.WithLabelValues(label).Observe(time.Since(start).Seconds())
   323  	}()
   324  
   325  	// Process each affected region respectively
   326  	for !bytes.Equal(startKey, endKey) {
   327  		var resp *tikvrpc.Response
   328  		var actualEndKey []byte
   329  		resp, actualEndKey, err = c.sendDeleteRangeReq(ctx, startKey, endKey)
   330  		if err != nil {
   331  			return errors.Trace(err)
   332  		}
   333  		if resp.Resp == nil {
   334  			return errors.Trace(tikverr.ErrBodyMissing)
   335  		}
   336  		cmdResp := resp.Resp.(*kvrpcpb.RawDeleteRangeResponse)
   337  		if cmdResp.GetError() != "" {
   338  			return errors.New(cmdResp.GetError())
   339  		}
   340  		startKey = actualEndKey
   341  	}
   342  
   343  	return nil
   344  }
   345  
   346  // DeleteRange deletes all key-value pairs in a range from TiKV.
   347  func (c *Client) BatchDeleteRange(ctx context.Context, startKey []byte, endKey []byte, batchSize int) error {
   348  	start := time.Now()
   349  	tmpEndKey := endKey
   350  	tmpStartKey := startKey
   351  	var err error
   352  	defer func() {
   353  		var label = "delete_range_batch"
   354  		if err != nil {
   355  			label += "_error"
   356  		}
   357  		metrics.TiKVRawkvCmdHistogram.WithLabelValues(label).Observe(time.Since(start).Seconds())
   358  	}()
   359  	bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil)
   360  
   361  	var i int = 0
   362  	var keyLocations []locate.KeyLocation = make([]locate.KeyLocation, batchSize)
   363  	for !bytes.Equal(tmpStartKey, tmpEndKey) {
   364  		for i = 0; i < batchSize; i++ {
   365  			loc, err := c.regionCache.LocateKey(bo, tmpStartKey)
   366  			if err != nil {
   367  				return err
   368  			}
   369  
   370  			paramRegion := locate.NewRegionVerID(loc.Region.GetID(), loc.Region.GetConfVer(), loc.Region.GetVer())
   371  			paramStartKey := loc.StartKey
   372  			paramEndKey := loc.EndKey
   373  			logutil.BgLogger().Info("doBatchDeleteRangeReq in channel err", zap.Any("loc", paramRegion), zap.ByteString("start", paramStartKey), zap.ByteString("end", paramEndKey),
   374  				zap.Uint64("loc id", loc.Region.GetID()), zap.Uint64("loc confver", loc.Region.GetConfVer()), zap.Uint64("ver", loc.Region.GetVer()), zap.String("content", paramRegion.String()))
   375  			if len(loc.EndKey) > 0 && bytes.Compare(loc.EndKey, tmpEndKey) < 0 {
   376  				keyLocations = append(keyLocations, locate.KeyLocation{StartKey: paramStartKey, EndKey: paramEndKey, Region: paramRegion})
   377  				tmpStartKey = loc.EndKey
   378  			} else {
   379  				keyLocations = append(keyLocations, locate.KeyLocation{StartKey: tmpStartKey, EndKey: tmpEndKey, Region: paramRegion})
   380  				tmpStartKey = tmpEndKey
   381  				break
   382  			}
   383  		}
   384  
   385  		bo, cancel := bo.Fork()
   386  		ch := make(chan error, len(keyLocations))
   387  		for _, batch := range keyLocations {
   388  			batch1 := batch
   389  			go func() {
   390  				batch2 := batch1
   391  				singleBatchBackoffer, singleBatchCancel := bo.Fork()
   392  				defer singleBatchCancel()
   393  				ch <- c.doBatchDeleteRangeReq(singleBatchBackoffer, batch2)
   394  			}()
   395  		}
   396  
   397  		for i := 0; i < len(keyLocations); i++ {
   398  			if e := <-ch; e != nil {
   399  				cancel()
   400  				// catch the first error
   401  				if err == nil {
   402  					err = errors.WithStack(e)
   403  				}
   404  			}
   405  		}
   406  		if err != nil {
   407  			logutil.BgLogger().Error("doBatchDeleteRangeReq in channel err", zap.Error(err))
   408  		}
   409  	}
   410  
   411  	return nil
   412  }
   413  
   414  func (c *Client) doBatchDeleteRangeReq(bo *retry.Backoffer, request locate.KeyLocation) error {
   415  	logutil.BgLogger().Info("doBatchDeleteRangeReq", zap.Any("request", request), zap.String("content", request.String()))
   416  	if request.StartKey == nil || request.EndKey == nil {
   417  		return nil
   418  	}
   419  	var req *tikvrpc.Request = tikvrpc.NewRequest(tikvrpc.CmdRawDeleteRange, &kvrpcpb.RawDeleteRangeRequest{
   420  		StartKey: request.StartKey,
   421  		EndKey:   request.EndKey,
   422  	})
   423  
   424  	sender := locate.NewRegionRequestSender(c.regionCache, c.rpcClient)
   425  
   426  	req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds())
   427  	// resp, err := sender.SendReq(bo, req, batch.RegionID, client.ReadTimeoutShort)
   428  	resp, err := sender.SendReq(bo, req, request.Region, client.ReadTimeoutShort)
   429  	if err != nil {
   430  		logutil.BgLogger().Error("snendReq err", zap.Any("req", request), zap.Error(err))
   431  		return err
   432  	}
   433  	regionErr, err := resp.GetRegionError()
   434  	if err != nil {
   435  		logutil.BgLogger().Error("getRegion err", zap.Any("req", request), zap.Error(err))
   436  		return err
   437  	}
   438  	if regionErr != nil {
   439  		err := bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String()))
   440  		if err != nil {
   441  			logutil.BgLogger().Error("backoff err", zap.Any("req", request), zap.Error(err))
   442  			return err
   443  		}
   444  		return c.doBatchDeleteRangeReq(bo, request)
   445  	}
   446  	return nil
   447  }
   448  
   449  // Scan queries continuous kv pairs in range [startKey, endKey), up to limit pairs.
   450  // If endKey is empty, it means unbounded.
   451  // If you want to exclude the startKey or include the endKey, push a '\0' to the key. For example, to scan
   452  // (startKey, endKey], you can write:
   453  // `Scan(ctx, push(startKey, '\0'), push(endKey, '\0'), limit)`.
   454  func (c *Client) Scan(ctx context.Context, startKey, endKey []byte, limit int) (keys [][]byte, values [][]byte, err error) {
   455  	start := time.Now()
   456  	defer func() { metrics.RawkvCmdHistogramWithRawScan.Observe(time.Since(start).Seconds()) }()
   457  
   458  	if limit > MaxRawKVScanLimit {
   459  		return nil, nil, errors.Trace(ErrMaxScanLimitExceeded)
   460  	}
   461  
   462  	for len(keys) < limit && (len(endKey) == 0 || bytes.Compare(startKey, endKey) < 0) {
   463  		req := tikvrpc.NewRequest(tikvrpc.CmdRawScan, &kvrpcpb.RawScanRequest{
   464  			StartKey: startKey,
   465  			EndKey:   endKey,
   466  			Limit:    uint32(limit - len(keys)),
   467  		})
   468  		resp, loc, err := c.sendReq(ctx, startKey, req, false)
   469  		if err != nil {
   470  			return nil, nil, errors.Trace(err)
   471  		}
   472  		if resp.Resp == nil {
   473  			return nil, nil, errors.Trace(tikverr.ErrBodyMissing)
   474  		}
   475  		cmdResp := resp.Resp.(*kvrpcpb.RawScanResponse)
   476  		for _, pair := range cmdResp.Kvs {
   477  			keys = append(keys, pair.Key)
   478  			values = append(values, pair.Value)
   479  		}
   480  		startKey = loc.EndKey
   481  		if len(startKey) == 0 {
   482  			break
   483  		}
   484  	}
   485  	return
   486  }
   487  
   488  // ReverseScan queries continuous kv pairs in range [endKey, startKey), up to limit pairs.
   489  // Direction is different from Scan, upper to lower.
   490  // If endKey is empty, it means unbounded.
   491  // If you want to include the startKey or exclude the endKey, push a '\0' to the key. For example, to scan
   492  // (endKey, startKey], you can write:
   493  // `ReverseScan(ctx, push(startKey, '\0'), push(endKey, '\0'), limit)`.
   494  // It doesn't support Scanning from "", because locating the last Region is not yet implemented.
   495  func (c *Client) ReverseScan(ctx context.Context, startKey, endKey []byte, limit int) (keys [][]byte, values [][]byte, err error) {
   496  	start := time.Now()
   497  	defer func() {
   498  		metrics.RawkvCmdHistogramWithRawReversScan.Observe(time.Since(start).Seconds())
   499  	}()
   500  
   501  	if limit > MaxRawKVScanLimit {
   502  		return nil, nil, errors.Trace(ErrMaxScanLimitExceeded)
   503  	}
   504  
   505  	for len(keys) < limit && bytes.Compare(startKey, endKey) > 0 {
   506  		req := tikvrpc.NewRequest(tikvrpc.CmdRawScan, &kvrpcpb.RawScanRequest{
   507  			StartKey: startKey,
   508  			EndKey:   endKey,
   509  			Limit:    uint32(limit - len(keys)),
   510  			Reverse:  true,
   511  		})
   512  		resp, loc, err := c.sendReq(ctx, startKey, req, true)
   513  		if err != nil {
   514  			return nil, nil, errors.Trace(err)
   515  		}
   516  		if resp.Resp == nil {
   517  			return nil, nil, errors.Trace(tikverr.ErrBodyMissing)
   518  		}
   519  		cmdResp := resp.Resp.(*kvrpcpb.RawScanResponse)
   520  		for _, pair := range cmdResp.Kvs {
   521  			keys = append(keys, pair.Key)
   522  			values = append(values, pair.Value)
   523  		}
   524  		startKey = loc.StartKey
   525  		if len(startKey) == 0 {
   526  			break
   527  		}
   528  	}
   529  	return
   530  }
   531  
   532  // CompareAndSwap results in an atomic compare-and-set operation for the given key while SetAtomicForCAS(true)
   533  // If the value retrieved is equal to previousValue, newValue is written.
   534  // It returns the previous value and whether the value is successfully swapped.
   535  //
   536  // If SetAtomicForCAS(false), it will returns an error because
   537  // CAS operations enforce the client should operate in atomic mode.
   538  //
   539  // NOTE: This feature is experimental. It depends on the single-row transaction mechanism of TiKV which is conflict
   540  // with the normal write operation in rawkv mode. If multiple clients exist, it's up to the clients the sync the atomic mode flag.
   541  // If some clients write in atomic mode but the other don't, the linearizability of TiKV will be violated.
   542  func (c *Client) CompareAndSwap(ctx context.Context, key, previousValue, newValue []byte, ttl uint64) ([]byte, bool, error) {
   543  	if !c.atomic {
   544  		return nil, false, errors.Trace(errors.New("using CompareAndSwap without enable atomic mode"))
   545  	}
   546  
   547  	if len(newValue) == 0 {
   548  		return nil, false, errors.New("empty value is not supported")
   549  	}
   550  
   551  	reqArgs := kvrpcpb.RawCASRequest{
   552  		Key:   key,
   553  		Value: newValue,
   554  	}
   555  	if previousValue == nil {
   556  		reqArgs.PreviousNotExist = true
   557  	} else {
   558  		reqArgs.PreviousValue = previousValue
   559  	}
   560  
   561  	if ttl > 0 {
   562  		reqArgs.Ttl = ttl
   563  	}
   564  
   565  	req := tikvrpc.NewRequest(tikvrpc.CmdRawCompareAndSwap, &reqArgs)
   566  	req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds())
   567  	resp, _, err := c.sendReq(ctx, key, req, false)
   568  	if err != nil {
   569  		return nil, false, errors.Trace(err)
   570  	}
   571  	if resp.Resp == nil {
   572  		return nil, false, errors.Trace(tikverr.ErrBodyMissing)
   573  	}
   574  
   575  	cmdResp := resp.Resp.(*kvrpcpb.RawCASResponse)
   576  	if cmdResp.GetError() != "" {
   577  		return nil, false, errors.New(cmdResp.GetError())
   578  	}
   579  
   580  	if cmdResp.PreviousNotExist {
   581  		return nil, cmdResp.Succeed, nil
   582  	}
   583  	return cmdResp.PreviousValue, cmdResp.Succeed, nil
   584  }
   585  
   586  func (c *Client) sendReq(ctx context.Context, key []byte, req *tikvrpc.Request, reverse bool) (*tikvrpc.Response, *locate.KeyLocation, error) {
   587  	bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil)
   588  	sender := locate.NewRegionRequestSender(c.regionCache, c.rpcClient)
   589  	for {
   590  		var loc *locate.KeyLocation
   591  		var err error
   592  		if reverse {
   593  			loc, err = c.regionCache.LocateEndKey(bo, key)
   594  		} else {
   595  			loc, err = c.regionCache.LocateKey(bo, key)
   596  		}
   597  		if err != nil {
   598  			return nil, nil, errors.Trace(err)
   599  		}
   600  		resp, err := sender.SendReq(bo, req, loc.Region, client.ReadTimeoutShort)
   601  		if err != nil {
   602  			return nil, nil, errors.Trace(err)
   603  		}
   604  		regionErr, err := resp.GetRegionError()
   605  		if err != nil {
   606  			return nil, nil, errors.Trace(err)
   607  		}
   608  		if regionErr != nil {
   609  			err := bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String()))
   610  			if err != nil {
   611  				return nil, nil, errors.Trace(err)
   612  			}
   613  			continue
   614  		}
   615  		return resp, loc, nil
   616  	}
   617  }
   618  
   619  func (c *Client) sendBatchReq(bo *retry.Backoffer, keys [][]byte, cmdType tikvrpc.CmdType) (*tikvrpc.Response, error) { // split the keys
   620  	groups, _, err := c.regionCache.GroupKeysByRegion(bo, keys, nil)
   621  	if err != nil {
   622  		return nil, errors.Trace(err)
   623  	}
   624  
   625  	var batches []kvrpc.Batch
   626  	for regionID, groupKeys := range groups {
   627  		batches = kvrpc.AppendKeyBatches(batches, regionID, groupKeys, rawBatchPairCount)
   628  	}
   629  	bo, cancel := bo.Fork()
   630  	ches := make(chan kvrpc.BatchResult, len(batches))
   631  	for _, batch := range batches {
   632  		batch1 := batch
   633  		go func() {
   634  			singleBatchBackoffer, singleBatchCancel := bo.Fork()
   635  			defer singleBatchCancel()
   636  			ches <- c.doBatchReq(singleBatchBackoffer, batch1, cmdType)
   637  		}()
   638  	}
   639  
   640  	var firstError error
   641  	var resp *tikvrpc.Response
   642  	switch cmdType {
   643  	case tikvrpc.CmdRawBatchGet:
   644  		resp = &tikvrpc.Response{Resp: &kvrpcpb.RawBatchGetResponse{}}
   645  	case tikvrpc.CmdRawBatchDelete:
   646  		resp = &tikvrpc.Response{Resp: &kvrpcpb.RawBatchDeleteResponse{}}
   647  	}
   648  	for i := 0; i < len(batches); i++ {
   649  		singleResp, ok := <-ches
   650  		if ok {
   651  			if singleResp.Error != nil {
   652  				cancel()
   653  				if firstError == nil {
   654  					firstError = singleResp.Error
   655  				}
   656  			} else if cmdType == tikvrpc.CmdRawBatchGet {
   657  				cmdResp := singleResp.Resp.(*kvrpcpb.RawBatchGetResponse)
   658  				resp.Resp.(*kvrpcpb.RawBatchGetResponse).Pairs = append(resp.Resp.(*kvrpcpb.RawBatchGetResponse).Pairs, cmdResp.Pairs...)
   659  			}
   660  		}
   661  	}
   662  
   663  	return resp, firstError
   664  }
   665  
   666  func (c *Client) doBatchReq(bo *retry.Backoffer, batch kvrpc.Batch, cmdType tikvrpc.CmdType) kvrpc.BatchResult {
   667  	var req *tikvrpc.Request
   668  	switch cmdType {
   669  	case tikvrpc.CmdRawBatchGet:
   670  		req = tikvrpc.NewRequest(cmdType, &kvrpcpb.RawBatchGetRequest{
   671  			Keys: batch.Keys,
   672  		})
   673  	case tikvrpc.CmdRawBatchDelete:
   674  		req = tikvrpc.NewRequest(cmdType, &kvrpcpb.RawBatchDeleteRequest{
   675  			Keys:   batch.Keys,
   676  			ForCas: c.atomic,
   677  		})
   678  	}
   679  
   680  	sender := locate.NewRegionRequestSender(c.regionCache, c.rpcClient)
   681  	req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds())
   682  	resp, err := sender.SendReq(bo, req, batch.RegionID, client.ReadTimeoutShort)
   683  
   684  	batchResp := kvrpc.BatchResult{}
   685  	if err != nil {
   686  		batchResp.Error = errors.Trace(err)
   687  		return batchResp
   688  	}
   689  	regionErr, err := resp.GetRegionError()
   690  	if err != nil {
   691  		batchResp.Error = errors.Trace(err)
   692  		return batchResp
   693  	}
   694  	if regionErr != nil {
   695  		err := bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String()))
   696  		if err != nil {
   697  			batchResp.Error = errors.Trace(err)
   698  			return batchResp
   699  		}
   700  		resp, err = c.sendBatchReq(bo, batch.Keys, cmdType)
   701  		batchResp.Response = resp
   702  		batchResp.Error = err
   703  		return batchResp
   704  	}
   705  
   706  	switch cmdType {
   707  	case tikvrpc.CmdRawBatchGet:
   708  		batchResp.Response = resp
   709  	case tikvrpc.CmdRawBatchDelete:
   710  		if resp.Resp == nil {
   711  			batchResp.Error = errors.Trace(tikverr.ErrBodyMissing)
   712  			return batchResp
   713  		}
   714  		cmdResp := resp.Resp.(*kvrpcpb.RawBatchDeleteResponse)
   715  		if cmdResp.GetError() != "" {
   716  			batchResp.Error = errors.New(cmdResp.GetError())
   717  			return batchResp
   718  		}
   719  		batchResp.Response = resp
   720  	}
   721  	return batchResp
   722  }
   723  
   724  // sendDeleteRangeReq sends a raw delete range request and returns the response and the actual endKey.
   725  // If the given range spans over more than one regions, the actual endKey is the end of the first region.
   726  // We can't use sendReq directly, because we need to know the end of the region before we send the request
   727  // TODO: Is there any better way to avoid duplicating code with func `sendReq` ?
   728  func (c *Client) sendDeleteRangeReq(ctx context.Context, startKey []byte, endKey []byte) (*tikvrpc.Response, []byte, error) {
   729  	bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil)
   730  	sender := locate.NewRegionRequestSender(c.regionCache, c.rpcClient)
   731  	for {
   732  		loc, err := c.regionCache.LocateKey(bo, startKey)
   733  		if err != nil {
   734  			return nil, nil, errors.Trace(err)
   735  		}
   736  
   737  		actualEndKey := endKey
   738  		if len(loc.EndKey) > 0 && bytes.Compare(loc.EndKey, endKey) < 0 {
   739  			actualEndKey = loc.EndKey
   740  		}
   741  
   742  		req := tikvrpc.NewRequest(tikvrpc.CmdRawDeleteRange, &kvrpcpb.RawDeleteRangeRequest{
   743  			StartKey: startKey,
   744  			EndKey:   actualEndKey,
   745  		})
   746  
   747  		req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds())
   748  		resp, err := sender.SendReq(bo, req, loc.Region, client.ReadTimeoutShort)
   749  		if err != nil {
   750  			return nil, nil, errors.Trace(err)
   751  		}
   752  		regionErr, err := resp.GetRegionError()
   753  		if err != nil {
   754  			return nil, nil, errors.Trace(err)
   755  		}
   756  		if regionErr != nil {
   757  			err := bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String()))
   758  			if err != nil {
   759  				return nil, nil, errors.Trace(err)
   760  			}
   761  			continue
   762  		}
   763  		return resp, actualEndKey, nil
   764  	}
   765  }
   766  
   767  func (c *Client) sendBatchPut(bo *retry.Backoffer, keys, values [][]byte, ttls []uint64) error {
   768  	keyToValue := make(map[string][]byte, len(keys))
   769  	keyTottl := make(map[string]uint64, len(keys))
   770  	for i, key := range keys {
   771  		keyToValue[string(key)] = values[i]
   772  		if len(ttls) > 0 {
   773  			keyTottl[string(key)] = ttls[i]
   774  		}
   775  	}
   776  	groups, _, err := c.regionCache.GroupKeysByRegion(bo, keys, nil)
   777  	if err != nil {
   778  		return errors.Trace(err)
   779  	}
   780  	var batches []kvrpc.Batch
   781  	// split the keys by size and RegionVerID
   782  	for regionID, groupKeys := range groups {
   783  		batches = kvrpc.AppendBatches(batches, regionID, groupKeys, keyToValue, keyTottl, rawBatchPutSize)
   784  	}
   785  	bo, cancel := bo.Fork()
   786  	ch := make(chan error, len(batches))
   787  	for _, batch := range batches {
   788  		batch1 := batch
   789  		go func() {
   790  			singleBatchBackoffer, singleBatchCancel := bo.Fork()
   791  			defer singleBatchCancel()
   792  			ch <- c.doBatchPut(singleBatchBackoffer, batch1)
   793  		}()
   794  	}
   795  
   796  	for i := 0; i < len(batches); i++ {
   797  		if e := <-ch; e != nil {
   798  			cancel()
   799  			// catch the first error
   800  			if err == nil {
   801  				err = e
   802  			}
   803  		}
   804  	}
   805  	return errors.Trace(err)
   806  }
   807  
   808  func (c *Client) doBatchPut(bo *retry.Backoffer, batch kvrpc.Batch) error {
   809  	kvPair := make([]*kvrpcpb.KvPair, 0, len(batch.Keys))
   810  	for i, key := range batch.Keys {
   811  		kvPair = append(kvPair, &kvrpcpb.KvPair{Key: key, Value: batch.Values[i]})
   812  	}
   813  
   814  	req := tikvrpc.NewRequest(tikvrpc.CmdRawBatchPut,
   815  		&kvrpcpb.RawBatchPutRequest{Pairs: kvPair, ForCas: c.atomic, Ttls: batch.TTLs})
   816  
   817  	sender := locate.NewRegionRequestSender(c.regionCache, c.rpcClient)
   818  	req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds())
   819  	resp, err := sender.SendReq(bo, req, batch.RegionID, client.ReadTimeoutShort)
   820  	if err != nil {
   821  		return errors.Trace(err)
   822  	}
   823  	regionErr, err := resp.GetRegionError()
   824  	if err != nil {
   825  		return errors.Trace(err)
   826  	}
   827  	if regionErr != nil {
   828  		err := bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String()))
   829  		if err != nil {
   830  			return errors.Trace(err)
   831  		}
   832  		// recursive call
   833  		return c.sendBatchPut(bo, batch.Keys, batch.Values, batch.TTLs)
   834  	}
   835  
   836  	if resp.Resp == nil {
   837  		return errors.Trace(tikverr.ErrBodyMissing)
   838  	}
   839  	cmdResp := resp.Resp.(*kvrpcpb.RawBatchPutResponse)
   840  	if cmdResp.GetError() != "" {
   841  		return errors.New(cmdResp.GetError())
   842  	}
   843  	return nil
   844  }