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 }