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 := &regionInfo{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  }