github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/store/localstore/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 localstore 15 16 import ( 17 "net/url" 18 "path/filepath" 19 "runtime/debug" 20 "sync" 21 "time" 22 23 "github.com/insionng/yougam/libraries/juju/errors" 24 "github.com/insionng/yougam/libraries/ngaut/log" 25 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 26 "github.com/insionng/yougam/libraries/pingcap/tidb/store/localstore/engine" 27 "github.com/insionng/yougam/libraries/pingcap/tidb/util/segmentmap" 28 "github.com/insionng/yougam/libraries/twinj/uuid" 29 ) 30 31 var ( 32 _ kv.Storage = (*dbStore)(nil) 33 ) 34 35 const ( 36 lowerWaterMark = 10 // second 37 ) 38 39 func (s *dbStore) prepareSeek(startTS uint64) error { 40 for { 41 var conflict bool 42 s.mu.RLock() 43 if s.closed { 44 s.mu.RUnlock() 45 return ErrDBClosed 46 } 47 if s.committingTS != 0 && s.committingTS < startTS { 48 // We not sure if we can read the committing value, 49 conflict = true 50 } else { 51 s.wg.Add(1) 52 } 53 s.mu.RUnlock() 54 if conflict { 55 // Wait for committing to be finished and try again. 56 time.Sleep(time.Microsecond) 57 continue 58 } 59 return nil 60 } 61 } 62 63 // Seek searches for the first key in the engine which is >= key in byte order, returns (nil, nil, ErrNotFound) 64 // if such key is not found. 65 func (s *dbStore) Seek(key []byte, startTS uint64) ([]byte, []byte, error) { 66 err := s.prepareSeek(startTS) 67 if err != nil { 68 return nil, nil, errors.Trace(err) 69 } 70 key, val, err := s.db.Seek(key) 71 s.wg.Done() 72 return key, val, err 73 } 74 75 // SeekReverse searches for the first key in the engine which is less than key in byte order. 76 // Returns (nil, nil, ErrNotFound) if such key is not found. 77 func (s *dbStore) SeekReverse(key []byte, startTS uint64) ([]byte, []byte, error) { 78 err := s.prepareSeek(startTS) 79 if err != nil { 80 return nil, nil, errors.Trace(err) 81 } 82 key, val, err := s.db.SeekReverse(key) 83 s.wg.Done() 84 return key, val, err 85 } 86 87 // Commit writes the changed data in Batch. 88 func (s *dbStore) CommitTxn(txn *dbTxn) error { 89 if len(txn.lockedKeys) == 0 { 90 return nil 91 } 92 return s.doCommit(txn) 93 } 94 95 func (s *dbStore) cleanRecentUpdates(segmentIndex int64) { 96 m, err := s.recentUpdates.GetSegment(segmentIndex) 97 if err != nil { 98 log.Error(err) 99 return 100 } 101 102 now := time.Now().Unix() 103 for k, v := range m { 104 dis := now - version2Second(v.(kv.Version)) 105 if dis > lowerWaterMark { 106 delete(m, k) 107 } 108 } 109 } 110 111 func (s *dbStore) tryLock(txn *dbTxn) (err error) { 112 // check conflict 113 for k := range txn.lockedKeys { 114 if _, ok := s.keysLocked[k]; ok { 115 return errors.Trace(kv.ErrLockConflict) 116 } 117 118 lastVer, ok := s.recentUpdates.Get([]byte(k)) 119 if !ok { 120 continue 121 } 122 // If there's newer version of this key, returns error. 123 if lastVer.(kv.Version).Cmp(kv.Version{Ver: txn.tid}) > 0 { 124 return errors.Trace(kv.ErrConditionNotMatch) 125 } 126 } 127 128 // record 129 for k := range txn.lockedKeys { 130 s.keysLocked[k] = txn.tid 131 } 132 133 return nil 134 } 135 136 func (s *dbStore) doCommit(txn *dbTxn) error { 137 var commitVer kv.Version 138 var err error 139 for { 140 // Atomically get commit version 141 s.mu.Lock() 142 closed := s.closed 143 committing := s.committingTS != 0 144 if !closed && !committing { 145 commitVer, err = globalVersionProvider.CurrentVersion() 146 if err != nil { 147 s.mu.Unlock() 148 return errors.Trace(err) 149 } 150 s.committingTS = commitVer.Ver 151 s.wg.Add(1) 152 } 153 s.mu.Unlock() 154 155 if closed { 156 return ErrDBClosed 157 } 158 if committing { 159 time.Sleep(time.Microsecond) 160 continue 161 } 162 break 163 } 164 defer func() { 165 s.mu.Lock() 166 s.committingTS = 0 167 s.wg.Done() 168 s.mu.Unlock() 169 }() 170 // Here we are sure no concurrent committing happens. 171 err = s.tryLock(txn) 172 if err != nil { 173 return errors.Trace(err) 174 } 175 b := s.db.NewBatch() 176 txn.us.WalkBuffer(func(k kv.Key, value []byte) error { 177 mvccKey := MvccEncodeVersionKey(kv.Key(k), commitVer) 178 if len(value) == 0 { // Deleted marker 179 b.Put(mvccKey, nil) 180 s.compactor.OnDelete(k) 181 } else { 182 b.Put(mvccKey, value) 183 s.compactor.OnSet(k) 184 } 185 return nil 186 }) 187 err = s.writeBatch(b) 188 if err != nil { 189 return errors.Trace(err) 190 } 191 // Update commit version. 192 txn.version = commitVer 193 err = s.unLockKeys(txn) 194 if err != nil { 195 return errors.Trace(err) 196 } 197 198 // Clean recent updates. 199 now := time.Now() 200 if now.Sub(s.lastCleanTime) > time.Second { 201 s.cleanRecentUpdates(s.cleanIdx) 202 s.cleanIdx++ 203 if s.cleanIdx == s.recentUpdates.SegmentCount() { 204 s.cleanIdx = 0 205 } 206 s.lastCleanTime = now 207 } 208 return nil 209 } 210 211 func (s *dbStore) NewBatch() engine.Batch { 212 return s.db.NewBatch() 213 } 214 215 type dbStore struct { 216 db engine.DB 217 218 txns map[uint64]*dbTxn 219 keysLocked map[string]uint64 220 221 recentUpdates *segmentmap.SegmentMap 222 cleanIdx int64 223 lastCleanTime time.Time 224 225 uuid string 226 path string 227 compactor *localstoreCompactor 228 wg sync.WaitGroup 229 230 mu sync.RWMutex 231 closed bool 232 committingTS uint64 233 234 pd localPD 235 } 236 237 type storeCache struct { 238 mu sync.Mutex 239 cache map[string]*dbStore 240 } 241 242 var ( 243 globalVersionProvider kv.VersionProvider 244 mc storeCache 245 246 // ErrDBClosed is the error meaning db is closed and we can use it anymore. 247 ErrDBClosed = errors.New("db is closed") 248 ) 249 250 func init() { 251 mc.cache = make(map[string]*dbStore) 252 globalVersionProvider = &LocalVersionProvider{} 253 } 254 255 // Driver implements kv.Driver interface. 256 type Driver struct { 257 // engine.Driver is the engine driver for different local db engine. 258 engine.Driver 259 } 260 261 // IsLocalStore checks whether a storage is local or not. 262 func IsLocalStore(s kv.Storage) bool { 263 _, ok := s.(*dbStore) 264 return ok 265 } 266 267 // Open opens or creates a storage with specific format for a local engine Driver. 268 // The path should be a URL format which is described in tidb package. 269 func (d Driver) Open(path string) (kv.Storage, error) { 270 mc.mu.Lock() 271 defer mc.mu.Unlock() 272 273 u, err := url.Parse(path) 274 if err != nil { 275 return nil, errors.Trace(err) 276 } 277 278 engineSchema := filepath.Join(u.Host, u.Path) 279 if store, ok := mc.cache[engineSchema]; ok { 280 // TODO: check the cache store has the same engine with this Driver. 281 log.Info("[kv] cache store", engineSchema) 282 return store, nil 283 } 284 285 db, err := d.Driver.Open(engineSchema) 286 if err != nil { 287 return nil, errors.Trace(err) 288 } 289 290 log.Info("[kv] New store", engineSchema) 291 s := &dbStore{ 292 txns: make(map[uint64]*dbTxn), 293 keysLocked: make(map[string]uint64), 294 uuid: uuid.NewV4().String(), 295 path: engineSchema, 296 db: db, 297 compactor: newLocalCompactor(localCompactDefaultPolicy, db), 298 closed: false, 299 } 300 s.recentUpdates, err = segmentmap.NewSegmentMap(100) 301 if err != nil { 302 return nil, errors.Trace(err) 303 } 304 regionServers := buildLocalRegionServers(s) 305 var infos []*regionInfo 306 for _, rs := range regionServers { 307 ri := ®ionInfo{startKey: rs.startKey, endKey: rs.endKey, rs: rs} 308 infos = append(infos, ri) 309 } 310 s.pd.SetRegionInfo(infos) 311 mc.cache[engineSchema] = s 312 s.compactor.Start() 313 return s, nil 314 } 315 316 func (s *dbStore) UUID() string { 317 return s.uuid 318 } 319 320 func (s *dbStore) GetSnapshot(ver kv.Version) (kv.Snapshot, error) { 321 s.mu.RLock() 322 if s.closed { 323 s.mu.RUnlock() 324 return nil, ErrDBClosed 325 } 326 s.mu.RUnlock() 327 328 currentVer, err := globalVersionProvider.CurrentVersion() 329 if err != nil { 330 return nil, errors.Trace(err) 331 } 332 333 if ver.Cmp(currentVer) > 0 { 334 ver = currentVer 335 } 336 337 return &dbSnapshot{ 338 store: s, 339 version: ver, 340 }, nil 341 } 342 343 func (s *dbStore) CurrentVersion() (kv.Version, error) { 344 return globalVersionProvider.CurrentVersion() 345 } 346 347 // Begin transaction 348 func (s *dbStore) Begin() (kv.Transaction, error) { 349 s.mu.RLock() 350 if s.closed { 351 s.mu.RUnlock() 352 return nil, ErrDBClosed 353 } 354 s.mu.RUnlock() 355 356 beginVer, err := globalVersionProvider.CurrentVersion() 357 if err != nil { 358 return nil, errors.Trace(err) 359 } 360 361 return newTxn(s, beginVer), nil 362 } 363 364 func (s *dbStore) Close() error { 365 s.mu.Lock() 366 if s.closed { 367 s.mu.Unlock() 368 return ErrDBClosed 369 } 370 s.closed = true 371 s.mu.Unlock() 372 s.compactor.Stop() 373 s.wg.Wait() 374 delete(mc.cache, s.path) 375 return s.db.Close() 376 } 377 378 func (s *dbStore) writeBatch(b engine.Batch) error { 379 if b.Len() == 0 { 380 return nil 381 } 382 err := s.db.Commit(b) 383 if err != nil { 384 log.Error(err) 385 return errors.Trace(err) 386 } 387 388 return nil 389 } 390 391 func (s *dbStore) newBatch() engine.Batch { 392 return s.db.NewBatch() 393 } 394 395 func (s *dbStore) unLockKeys(txn *dbTxn) error { 396 for k := range txn.lockedKeys { 397 if tid, ok := s.keysLocked[k]; !ok || tid != txn.tid { 398 debug.PrintStack() 399 return errors.Errorf("should never happend:%v, %v", tid, txn.tid) 400 } 401 402 delete(s.keysLocked, k) 403 s.recentUpdates.Set([]byte(k), txn.version, true) 404 } 405 return nil 406 }