github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/store/tikv/kv.go (about) 1 // Copyright 2016 PingCAP, Inc. 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package tikv 15 16 import ( 17 "fmt" 18 "math/rand" 19 "net/url" 20 "strconv" 21 "strings" 22 "sync" 23 "time" 24 25 "github.com/insionng/yougam/libraries/juju/errors" 26 "github.com/insionng/yougam/libraries/ngaut/log" 27 "github.com/insionng/yougam/libraries/pingcap/kvproto/pkg/errorpb" 28 pb "github.com/insionng/yougam/libraries/pingcap/kvproto/pkg/kvrpcpb" 29 "github.com/insionng/yougam/libraries/pingcap/pd/pd-client" 30 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 31 "github.com/insionng/yougam/libraries/pingcap/tidb/store/tikv/mock-tikv" 32 "github.com/insionng/yougam/libraries/pingcap/tidb/store/tikv/oracle" 33 "github.com/insionng/yougam/libraries/pingcap/tidb/store/tikv/oracle/oracles" 34 ) 35 36 type storeCache struct { 37 mu sync.Mutex 38 cache map[string]*tikvStore 39 } 40 41 var mc storeCache 42 43 // Driver implements engine Driver. 44 type Driver struct { 45 } 46 47 // Open opens or creates an TiKV storage with given path. 48 // Path example: tikv://etcd-node1:port,etcd-node2:port/pd-path?cluster=1 49 func (d Driver) Open(path string) (kv.Storage, error) { 50 mc.mu.Lock() 51 defer mc.mu.Unlock() 52 53 etcdAddrs, pdPath, clusterID, err := parsePath(path) 54 if err != nil { 55 return nil, errors.Trace(err) 56 } 57 // FIXME: uuid will be a very long and ugly string, simplify it. 58 uuid := fmt.Sprintf("tikv-%v-%v-%v", etcdAddrs, pdPath, clusterID) 59 if store, ok := mc.cache[uuid]; ok { 60 return store, nil 61 } 62 63 pdCli, err := pd.NewClient(etcdAddrs, pdPath, clusterID) 64 if err != nil { 65 return nil, errors.Trace(err) 66 } 67 s := newTikvStore(uuid, &codecPDClient{pdCli}, NewRPCClient) 68 mc.cache[uuid] = s 69 return s, nil 70 } 71 72 type tikvStore struct { 73 mu sync.Mutex 74 uuid string 75 oracle oracle.Oracle 76 // addr => *Client 77 clients map[string]Client 78 clientFactory ClientFactory 79 clientLock sync.RWMutex 80 regionCache *RegionCache 81 } 82 83 func newTikvStore(uuid string, pdClient pd.Client, factory ClientFactory) *tikvStore { 84 return &tikvStore{ 85 uuid: uuid, 86 oracle: oracles.NewPdOracle(pdClient), 87 clients: make(map[string]Client), 88 clientFactory: factory, 89 regionCache: NewRegionCache(pdClient), 90 } 91 } 92 93 // NewMockTikvStore creates a mocked tikv store. 94 func NewMockTikvStore() kv.Storage { 95 cluster := mocktikv.NewCluster() 96 mocktikv.BootstrapWithSingleStore(cluster) 97 mvccStore := mocktikv.NewMvccStore() 98 clientFactory := mockClientFactory(cluster, mvccStore) 99 uuid := fmt.Sprintf("mock-tikv-store-:%v", time.Now().Unix()) 100 return newTikvStore(uuid, mocktikv.NewPDClient(cluster), clientFactory) 101 } 102 103 func mockClientFactory(cluster *mocktikv.Cluster, mvccStore *mocktikv.MvccStore) ClientFactory { 104 return func(addr string) (Client, error) { 105 return mocktikv.NewRPCClient(cluster, mvccStore, addr), nil 106 } 107 } 108 109 func (s *tikvStore) getClient(addr string) (Client, error) { 110 s.clientLock.RLock() 111 client, ok := s.clients[addr] 112 s.clientLock.RUnlock() 113 if ok { 114 return client, nil 115 } 116 117 s.clientLock.Lock() 118 defer s.clientLock.Unlock() 119 120 client, err := s.clientFactory(addr) 121 if err != nil { 122 return nil, errors.Trace(err) 123 } 124 125 s.clients[addr] = client 126 return client, nil 127 } 128 129 func (s *tikvStore) Begin() (kv.Transaction, error) { 130 s.mu.Lock() 131 defer s.mu.Unlock() 132 133 txn, err := newTiKVTxn(s) 134 if err != nil { 135 return nil, errors.Trace(err) 136 } 137 return txn, nil 138 } 139 140 func (s *tikvStore) GetSnapshot(ver kv.Version) (kv.Snapshot, error) { 141 snapshot := newTiKVSnapshot(s, ver) 142 return snapshot, nil 143 } 144 145 func (s *tikvStore) Close() error { 146 mc.mu.Lock() 147 defer mc.mu.Unlock() 148 149 delete(mc.cache, s.uuid) 150 151 var lastErr error 152 for addr, client := range s.clients { 153 err := client.Close() 154 if err != nil { 155 log.Errorf("Close client error[%s]", err) 156 lastErr = err 157 } 158 delete(s.clients, addr) 159 } 160 return lastErr 161 } 162 163 func (s *tikvStore) UUID() string { 164 return s.uuid 165 } 166 167 func (s *tikvStore) CurrentVersion() (kv.Version, error) { 168 startTS, err := s.oracle.GetTimestamp() 169 if err != nil { 170 return kv.NewVersion(0), errors.Trace(err) 171 } 172 173 return kv.NewVersion(startTS), nil 174 } 175 176 // sendKVReq sends req to tikv server. It will retry internally to find the right 177 // region leader if i) fails to establish a connection to server or ii) server 178 // returns `NotLeader`. 179 func (s *tikvStore) SendKVReq(req *pb.Request, regionID RegionVerID) (*pb.Response, error) { 180 var backoffErr error 181 for backoff := rpcBackoff(); backoffErr == nil; backoffErr = backoff() { 182 region := s.regionCache.GetRegionByVerID(regionID) 183 if region == nil { 184 // If the region is not found in cache, it must be out 185 // of date and already be cleaned up. We can skip the 186 // RPC by returning RegionError directly. 187 return &pb.Response{ 188 Type: req.GetType().Enum(), 189 RegionError: &errorpb.Error{StaleEpoch: &errorpb.StaleEpoch{}}, 190 }, nil 191 } 192 client, err := s.getClient(region.GetAddress()) 193 if err != nil { 194 return nil, errors.Trace(err) 195 } 196 req.Context = region.GetContext() 197 resp, err := client.SendKVReq(req) 198 if err != nil { 199 log.Warnf("send tikv request error: %v, try next peer later", err) 200 s.regionCache.NextPeer(region.VerID()) 201 continue 202 } 203 if regionErr := resp.GetRegionError(); regionErr != nil { 204 // Retry if error is `NotLeader`. 205 if notLeader := regionErr.GetNotLeader(); notLeader != nil { 206 log.Warnf("tikv reports `NotLeader`: %s, retry later", notLeader.String()) 207 s.regionCache.UpdateLeader(region.VerID(), notLeader.GetLeader().GetId()) 208 continue 209 } 210 // For other errors, we only drop cache here. 211 // Because caller may need to re-split the request. 212 log.Warnf("tikv reports region error: %v", resp.GetRegionError()) 213 s.regionCache.DropRegion(region.VerID()) 214 return resp, nil 215 } 216 if resp.GetType() != req.GetType() { 217 return nil, errors.Trace(errMismatch(resp, req)) 218 } 219 return resp, nil 220 } 221 return nil, errors.Trace(backoffErr) 222 } 223 224 func parsePath(path string) (etcdAddrs []string, pdPath string, clusterID uint64, err error) { 225 var u *url.URL 226 u, err = url.Parse(path) 227 if err != nil { 228 err = errors.Trace(err) 229 return 230 } 231 if strings.ToLower(u.Scheme) != "tikv" { 232 log.Errorf("Uri scheme expected[tikv] but found [%s]", u.Scheme) 233 err = errors.Trace(err) 234 return 235 } 236 clusterID, err = strconv.ParseUint(u.Query().Get("cluster"), 10, 64) 237 if err != nil { 238 log.Errorf("Parse clusterID error [%s]", err) 239 err = errors.Trace(err) 240 return 241 } 242 etcdAddrs = strings.Split(u.Host, ",") 243 pdPath = u.Path 244 return 245 } 246 247 func init() { 248 mc.cache = make(map[string]*tikvStore) 249 rand.Seed(time.Now().UnixNano()) 250 }