github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/store/hbase/kv.go (about) 1 // Copyright 2015 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 hbasekv 15 16 import ( 17 "fmt" 18 "math/rand" 19 "net/url" 20 "path/filepath" 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/go-hbase" 28 "github.com/insionng/yougam/libraries/pingcap/go-themis" 29 "github.com/insionng/yougam/libraries/pingcap/go-themis/oracle" 30 "github.com/insionng/yougam/libraries/pingcap/go-themis/oracle/oracles" 31 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 32 ) 33 34 const ( 35 // hbaseColFamily is the hbase column family name. 36 hbaseColFamily = "f" 37 // hbaseQualifier is the hbase column name. 38 hbaseQualifier = "q" 39 // hbaseFmlAndQual is a shortcut. 40 hbaseFmlAndQual = hbaseColFamily + ":" + hbaseQualifier 41 // fix length conn pool 42 hbaseConnPoolSize = 10 43 ) 44 45 var ( 46 hbaseColFamilyBytes = []byte(hbaseColFamily) 47 hbaseQualifierBytes = []byte(hbaseQualifier) 48 ) 49 50 var ( 51 _ kv.Storage = (*hbaseStore)(nil) 52 ) 53 54 var ( 55 // ErrInvalidDSN is returned when store dsn is invalid. 56 ErrInvalidDSN = errors.New("invalid dsn") 57 ) 58 59 type storeCache struct { 60 mu sync.Mutex 61 cache map[string]*hbaseStore 62 } 63 64 var mc storeCache 65 66 func init() { 67 mc.cache = make(map[string]*hbaseStore) 68 rand.Seed(time.Now().UnixNano()) 69 } 70 71 type hbaseStore struct { 72 mu sync.Mutex 73 uuid string 74 storeName string 75 oracle oracle.Oracle 76 conns []hbase.HBaseClient 77 } 78 79 func (s *hbaseStore) getHBaseClient() hbase.HBaseClient { 80 // return hbase connection randomly 81 return s.conns[rand.Intn(hbaseConnPoolSize)] 82 } 83 84 func (s *hbaseStore) Begin() (kv.Transaction, error) { 85 s.mu.Lock() 86 defer s.mu.Unlock() 87 hbaseCli := s.getHBaseClient() 88 t, err := themis.NewTxn(hbaseCli, s.oracle) 89 if err != nil { 90 return nil, errors.Trace(err) 91 } 92 txn := newHbaseTxn(t, s.storeName) 93 return txn, nil 94 } 95 96 func (s *hbaseStore) GetSnapshot(ver kv.Version) (kv.Snapshot, error) { 97 hbaseCli := s.getHBaseClient() 98 t, err := themis.NewTxn(hbaseCli, s.oracle) 99 if err != nil { 100 return nil, errors.Trace(err) 101 } 102 return newHbaseSnapshot(t, s.storeName), nil 103 } 104 105 func (s *hbaseStore) Close() error { 106 mc.mu.Lock() 107 defer mc.mu.Unlock() 108 109 delete(mc.cache, s.uuid) 110 111 var err error 112 for _, conn := range s.conns { 113 err = conn.Close() 114 if err != nil { 115 log.Error(err) 116 } 117 } 118 // return last error 119 return err 120 } 121 122 func (s *hbaseStore) UUID() string { 123 return s.uuid 124 } 125 126 func (s *hbaseStore) CurrentVersion() (kv.Version, error) { 127 hbaseCli := s.getHBaseClient() 128 t, err := themis.NewTxn(hbaseCli, s.oracle) 129 if err != nil { 130 return kv.Version{Ver: 0}, errors.Trace(err) 131 } 132 defer t.Release() 133 134 return kv.Version{Ver: t.GetStartTS()}, nil 135 } 136 137 // Driver implements engine Driver. 138 type Driver struct { 139 } 140 141 const ( 142 tsoTypeLocal = "local" 143 tsoTypeZK = "zk" 144 145 tsoZKPath = "/zk/tso" 146 ) 147 148 // Open opens or creates an HBase storage with given path. 149 // 150 // The format of path should be 'hbase://zk1,zk2,zk3/table[?tso=local|zk]'. 151 // If tso is not provided, it will use a local oracle instead. (for test only) 152 func (d Driver) Open(path string) (kv.Storage, error) { 153 mc.mu.Lock() 154 defer mc.mu.Unlock() 155 156 zks, tso, tableName, err := parsePath(path) 157 if err != nil { 158 return nil, errors.Trace(err) 159 } 160 if tso != tsoTypeLocal && tso != tsoTypeZK { 161 return nil, errors.Trace(ErrInvalidDSN) 162 } 163 164 uuid := fmt.Sprintf("hbase-%v-%v", zks, tableName) 165 if tso == tsoTypeLocal { 166 log.Warnf("hbase: store(%s) is using local oracle(for test only)", uuid) 167 } 168 if store, ok := mc.cache[uuid]; ok { 169 return store, nil 170 } 171 172 // create buffered HBase connections, HBaseClient is goroutine-safe, so 173 // it's OK to redistribute to transactions. 174 conns := make([]hbase.HBaseClient, 0, hbaseConnPoolSize) 175 for i := 0; i < hbaseConnPoolSize; i++ { 176 var c hbase.HBaseClient 177 c, err = hbase.NewClient(strings.Split(zks, ","), "/hbase") 178 if err != nil { 179 return nil, errors.Trace(err) 180 } 181 conns = append(conns, c) 182 } 183 184 c := conns[0] 185 var b bool 186 b, err = c.TableExists(tableName) 187 if err != nil { 188 return nil, errors.Trace(err) 189 } 190 if !b { 191 // Create new hbase table for store. 192 t := hbase.NewTableDesciptor(tableName) 193 cf := hbase.NewColumnFamilyDescriptor(hbaseColFamily) 194 cf.AddAttr("THEMIS_ENABLE", "true") 195 t.AddColumnDesc(cf) 196 //TODO: specify split? 197 if err := c.CreateTable(t, nil); err != nil { 198 return nil, errors.Trace(err) 199 } 200 } 201 202 var ora oracle.Oracle 203 switch tso { 204 case tsoTypeLocal: 205 ora = oracles.NewLocalOracle() 206 case tsoTypeZK: 207 ora = oracles.NewRemoteOracle(zks, tsoZKPath) 208 } 209 210 s := &hbaseStore{ 211 uuid: uuid, 212 storeName: tableName, 213 oracle: ora, 214 conns: conns, 215 } 216 mc.cache[uuid] = s 217 return s, nil 218 } 219 220 func parsePath(path string) (zks, tso, tableName string, err error) { 221 u, err := url.Parse(path) 222 if err != nil { 223 return "", "", "", errors.Trace(err) 224 } 225 if strings.ToLower(u.Scheme) != "hbase" { 226 return "", "", "", errors.Trace(ErrInvalidDSN) 227 } 228 p, tableName := filepath.Split(u.Path) 229 if p != "/" { 230 return "", "", "", errors.Trace(ErrInvalidDSN) 231 } 232 zks = u.Host 233 tso = u.Query().Get("tso") 234 if tso == "" { 235 tso = tsoTypeLocal 236 } 237 return zks, tso, tableName, nil 238 }