github.com/KinWaiYuen/client-go/v2@v2.5.4/tikv/safepoint.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/safepoint.go
    19  //
    20  
    21  // Copyright 2017 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 tikv
    36  
    37  import (
    38  	"context"
    39  	"crypto/tls"
    40  	"strconv"
    41  	"strings"
    42  	"sync"
    43  	"time"
    44  
    45  	"github.com/KinWaiYuen/client-go/v2/internal/logutil"
    46  	"github.com/pingcap/errors"
    47  	"go.etcd.io/etcd/clientv3"
    48  	"go.etcd.io/etcd/mvcc/mvccpb"
    49  	"go.uber.org/zap"
    50  )
    51  
    52  // Safe point constants.
    53  const (
    54  	// This is almost the same as 'tikv_gc_safe_point' in the table 'mysql.tidb',
    55  	// save this to pd instead of tikv, because we can't use interface of table
    56  	// if the safepoint on tidb is expired.
    57  	GcSavedSafePoint = "/tidb/store/gcworker/saved_safe_point"
    58  
    59  	GcSafePointCacheInterval       = time.Second * 100
    60  	gcCPUTimeInaccuracyBound       = time.Second
    61  	gcSafePointUpdateInterval      = time.Second * 10
    62  	gcSafePointQuickRepeatInterval = time.Second
    63  )
    64  
    65  // SafePointKV is used for a seamingless integration for mockTest and runtime.
    66  type SafePointKV interface {
    67  	Put(k string, v string) error
    68  	Get(k string) (string, error)
    69  	GetWithPrefix(k string) ([]*mvccpb.KeyValue, error)
    70  	Close() error
    71  }
    72  
    73  // MockSafePointKV implements SafePointKV at mock test
    74  type MockSafePointKV struct {
    75  	store    map[string]string
    76  	mockLock sync.RWMutex
    77  }
    78  
    79  // NewMockSafePointKV creates an instance of MockSafePointKV
    80  func NewMockSafePointKV() *MockSafePointKV {
    81  	return &MockSafePointKV{
    82  		store: make(map[string]string),
    83  	}
    84  }
    85  
    86  // Put implements the Put method for SafePointKV
    87  func (w *MockSafePointKV) Put(k string, v string) error {
    88  	w.mockLock.Lock()
    89  	defer w.mockLock.Unlock()
    90  	w.store[k] = v
    91  	return nil
    92  }
    93  
    94  // Get implements the Get method for SafePointKV
    95  func (w *MockSafePointKV) Get(k string) (string, error) {
    96  	w.mockLock.RLock()
    97  	defer w.mockLock.RUnlock()
    98  	elem := w.store[k]
    99  	return elem, nil
   100  }
   101  
   102  // GetWithPrefix implements the Get method for SafePointKV
   103  func (w *MockSafePointKV) GetWithPrefix(prefix string) ([]*mvccpb.KeyValue, error) {
   104  	w.mockLock.RLock()
   105  	defer w.mockLock.RUnlock()
   106  	kvs := make([]*mvccpb.KeyValue, 0, len(w.store))
   107  	for k, v := range w.store {
   108  		if strings.HasPrefix(k, prefix) {
   109  			kvs = append(kvs, &mvccpb.KeyValue{Key: []byte(k), Value: []byte(v)})
   110  		}
   111  	}
   112  	return kvs, nil
   113  }
   114  
   115  // Close implements the Close method for SafePointKV
   116  func (w *MockSafePointKV) Close() error {
   117  	return nil
   118  }
   119  
   120  // EtcdSafePointKV implements SafePointKV at runtime
   121  type EtcdSafePointKV struct {
   122  	cli *clientv3.Client
   123  }
   124  
   125  // NewEtcdSafePointKV creates an instance of EtcdSafePointKV
   126  func NewEtcdSafePointKV(addrs []string, tlsConfig *tls.Config) (*EtcdSafePointKV, error) {
   127  	etcdCli, err := createEtcdKV(addrs, tlsConfig)
   128  	if err != nil {
   129  		return nil, errors.Trace(err)
   130  	}
   131  	return &EtcdSafePointKV{cli: etcdCli}, nil
   132  }
   133  
   134  // Put implements the Put method for SafePointKV
   135  func (w *EtcdSafePointKV) Put(k string, v string) error {
   136  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
   137  	_, err := w.cli.Put(ctx, k, v)
   138  	cancel()
   139  	if err != nil {
   140  		return errors.Trace(err)
   141  	}
   142  	return nil
   143  }
   144  
   145  // Get implements the Get method for SafePointKV
   146  func (w *EtcdSafePointKV) Get(k string) (string, error) {
   147  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
   148  	resp, err := w.cli.Get(ctx, k)
   149  	cancel()
   150  	if err != nil {
   151  		return "", errors.Trace(err)
   152  	}
   153  	if len(resp.Kvs) > 0 {
   154  		return string(resp.Kvs[0].Value), nil
   155  	}
   156  	return "", nil
   157  }
   158  
   159  // GetWithPrefix implements the GetWithPrefix for SafePointKV
   160  func (w *EtcdSafePointKV) GetWithPrefix(k string) ([]*mvccpb.KeyValue, error) {
   161  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
   162  	resp, err := w.cli.Get(ctx, k, clientv3.WithPrefix())
   163  	cancel()
   164  	if err != nil {
   165  		return nil, errors.Trace(err)
   166  	}
   167  	return resp.Kvs, nil
   168  }
   169  
   170  // Close implements the Close for SafePointKV
   171  func (w *EtcdSafePointKV) Close() error {
   172  	return errors.Trace(w.cli.Close())
   173  }
   174  
   175  func saveSafePoint(kv SafePointKV, t uint64) error {
   176  	s := strconv.FormatUint(t, 10)
   177  	err := kv.Put(GcSavedSafePoint, s)
   178  	if err != nil {
   179  		logutil.BgLogger().Error("save safepoint failed", zap.Error(err))
   180  		return errors.Trace(err)
   181  	}
   182  	return nil
   183  }
   184  
   185  func loadSafePoint(kv SafePointKV) (uint64, error) {
   186  	str, err := kv.Get(GcSavedSafePoint)
   187  
   188  	if err != nil {
   189  		return 0, errors.Trace(err)
   190  	}
   191  
   192  	if str == "" {
   193  		return 0, nil
   194  	}
   195  
   196  	t, err := strconv.ParseUint(str, 10, 64)
   197  	if err != nil {
   198  		return 0, errors.Trace(err)
   199  	}
   200  	return t, nil
   201  }