func (c *Client) GetKeyTTL(ctx context.Context, key []byte) (*uint64, error) {
	var ttl uint64
	metrics.RawkvSizeHistogramWithKey.Observe(float64(len(key)))
	req := tikvrpc.NewRequest(tikvrpc.CmdGetKeyTTL, &kvrpcpb.RawGetKeyTTLRequest{
		Key: key,
	})
	resp, _, err := c.sendReq(ctx, key, req, false)
	if err != nil {
		return nil, err
	}
	if resp.Resp == nil {
		return nil, errors.WithStack(tikverr.ErrBodyMissing)
	}
	cmdResp := resp.Resp.(*kvrpcpb.RawGetKeyTTLResponse)
	if cmdResp.GetError() != "" {
		return nil, errors.New(cmdResp.GetError())
	}
	if cmdResp.GetNotFound() {
		return nil, nil
	}
	ttl = cmdResp.GetTtl()
	return &ttl, nil
}
GetKeyTTL get the TTL of a raw key from TiKV if key exists
package rawkv
import (
	"bytes"
	"context"
	"time"
	"github.com/pingcap/errors"
	"github.com/pingcap/kvproto/pkg/kvrpcpb"
	"github.com/tikv/client-go/v2/config"
	tikverr "github.com/tikv/client-go/v2/error"
	"github.com/tikv/client-go/v2/internal/client"
	"github.com/tikv/client-go/v2/internal/kvrpc"
	"github.com/tikv/client-go/v2/internal/locate"
	"github.com/tikv/client-go/v2/internal/retry"
	"github.com/tikv/client-go/v2/metrics"
	"github.com/tikv/client-go/v2/tikvrpc"
	pd "github.com/tikv/pd/client"
)
var (
	MaxRawKVScanLimit = 10240
	ErrMaxScanLimitExceeded = errors.New("limit should be less than MaxRawKVScanLimit")
)
const (
	rawBatchPutSize = 16 * 1024
	rawBatchPairCount = 512
)
type Client struct {
	clusterID   uint64
	regionCache *locate.RegionCache
	pdClient    pd.Client
	rpcClient   client.Client
	atomic      bool
}
func (c *Client) SetAtomicForCAS(b bool) *Client {
	c.atomic = b
	return c
}
func NewClient(ctx context.Context, pdAddrs []string, security config.Security, opts ...pd.ClientOption) (*Client, error) {
	pdCli, err := pd.NewClient(pdAddrs, pd.SecurityOption{
		CAPath:   security.ClusterSSLCA,
		CertPath: security.ClusterSSLCert,
		KeyPath:  security.ClusterSSLKey,
	}, opts...)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return &Client{
		clusterID:   pdCli.GetClusterID(ctx),
		regionCache: locate.NewRegionCache(pdCli),
		pdClient:    pdCli,
		rpcClient:   client.NewRPCClient(client.WithSecurity(security)),
	}, nil
}
func (c *Client) Close() error {
	if c.pdClient != nil {
		c.pdClient.Close()
	}
	if c.regionCache != nil {
		c.regionCache.Close()
	}
	if c.rpcClient == nil {
		return nil
	}
	return c.rpcClient.Close()
}
func (c *Client) ClusterID() uint64 {
	return c.clusterID
}
func (c *Client) Get(ctx context.Context, key []byte) ([]byte, error) {
	start := time.Now()
	defer func() { metrics.RawkvCmdHistogramWithGet.Observe(time.Since(start).Seconds()) }()
	req := tikvrpc.NewRequest(tikvrpc.CmdRawGet, &kvrpcpb.RawGetRequest{Key: key})
	resp, _, err := c.sendReq(ctx, key, req, false)
	if err != nil {
		return nil, err
	}
	if resp.Resp == nil {
		return nil, errors.WithStack(tikverr.ErrBodyMissing)
	}
	cmdResp := resp.Resp.(*kvrpcpb.RawGetResponse)
	if cmdResp.GetError() != "" {
		return nil, errors.New(cmdResp.GetError())
	}
	if len(cmdResp.Value) == 0 {
		return nil, nil
	}
	return cmdResp.Value, nil
}
const rawkvMaxBackoff = 20000
func (c *Client) BatchGet(ctx context.Context, keys [][]byte) ([][]byte, error) {
	start := time.Now()
	defer func() {
		metrics.RawkvCmdHistogramWithBatchGet.Observe(time.Since(start).Seconds())
	}()
	bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil)
	resp, err := c.sendBatchReq(bo, keys, tikvrpc.CmdRawBatchGet)
	if err != nil {
		return nil, err
	}
	if resp.Resp == nil {
		return nil, errors.WithStack(tikverr.ErrBodyMissing)
	}
	cmdResp := resp.Resp.(*kvrpcpb.RawBatchGetResponse)
	keyToValue := make(map[string][]byte, len(keys))
	for _, pair := range cmdResp.Pairs {
		keyToValue[string(pair.Key)] = pair.Value
	}
	values := make([][]byte, len(keys))
	for i, key := range keys {
		values[i] = keyToValue[string(key)]
	}
	return values, nil
}
func (c *Client) PutWithTTL(ctx context.Context, key, value []byte, ttl uint64) error {
	start := time.Now()
	defer func() { metrics.RawkvCmdHistogramWithBatchPut.Observe(time.Since(start).Seconds()) }()
	metrics.RawkvSizeHistogramWithKey.Observe(float64(len(key)))
	metrics.RawkvSizeHistogramWithValue.Observe(float64(len(value)))
	if len(value) == 0 {
		return errors.New("empty value is not supported")
	}
	req := tikvrpc.NewRequest(tikvrpc.CmdRawPut, &kvrpcpb.RawPutRequest{
		Key:    key,
		Value:  value,
		Ttl:    ttl,
		ForCas: c.atomic,
	})
	resp, _, err := c.sendReq(ctx, key, req, false)
	if err != nil {
		return err
	}
	if resp.Resp == nil {
		return errors.WithStack(tikverr.ErrBodyMissing)
	}
	cmdResp := resp.Resp.(*kvrpcpb.RawPutResponse)
	if cmdResp.GetError() != "" {
		return errors.New(cmdResp.GetError())
	}
	return nil
}