github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/store/localstore/compactor.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  	"sync"
    18  	"time"
    19  
    20  	"github.com/insionng/yougam/libraries/juju/errors"
    21  	"github.com/insionng/yougam/libraries/ngaut/log"
    22  	"github.com/insionng/yougam/libraries/pingcap/tidb/kv"
    23  	"github.com/insionng/yougam/libraries/pingcap/tidb/store/localstore/engine"
    24  	"github.com/insionng/yougam/libraries/pingcap/tidb/terror"
    25  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/bytes"
    26  )
    27  
    28  const (
    29  	deleteWorkerCnt = 3
    30  )
    31  
    32  // compactPolicy defines gc policy of MVCC storage.
    33  type compactPolicy struct {
    34  	// SafePoint specifies
    35  	SafePoint int
    36  	// TriggerInterval specifies how often should the compactor
    37  	// scans outdated data.
    38  	TriggerInterval time.Duration
    39  	// BatchDeleteCnt specifies the batch size for
    40  	// deleting outdated data transaction.
    41  	BatchDeleteCnt int
    42  }
    43  
    44  var localCompactDefaultPolicy = compactPolicy{
    45  	SafePoint:       20 * 1000, // in ms
    46  	TriggerInterval: 10 * time.Second,
    47  	BatchDeleteCnt:  100,
    48  }
    49  
    50  type localstoreCompactor struct {
    51  	mu              sync.Mutex
    52  	recentKeys      map[string]struct{}
    53  	stopCh          chan struct{}
    54  	delCh           chan kv.EncodedKey
    55  	workerWaitGroup *sync.WaitGroup
    56  	ticker          *time.Ticker
    57  	db              engine.DB
    58  	policy          compactPolicy
    59  }
    60  
    61  func (gc *localstoreCompactor) OnSet(k kv.Key) {
    62  	gc.mu.Lock()
    63  	defer gc.mu.Unlock()
    64  	gc.recentKeys[string(k)] = struct{}{}
    65  }
    66  
    67  func (gc *localstoreCompactor) OnDelete(k kv.Key) {
    68  	gc.mu.Lock()
    69  	defer gc.mu.Unlock()
    70  	gc.recentKeys[string(k)] = struct{}{}
    71  }
    72  
    73  func (gc *localstoreCompactor) getAllVersions(key kv.Key) ([]kv.EncodedKey, error) {
    74  	var keys []kv.EncodedKey
    75  	k := key
    76  	for ver := kv.MaxVersion; ver.Ver > 0; ver.Ver-- {
    77  		mvccK, _, err := gc.db.Seek(MvccEncodeVersionKey(key, ver))
    78  		if terror.ErrorEqual(err, engine.ErrNotFound) {
    79  			break
    80  		}
    81  		if err != nil {
    82  			return nil, errors.Trace(err)
    83  		}
    84  		k, ver, err = MvccDecode(mvccK)
    85  		if k.Cmp(key) != 0 {
    86  			break
    87  		}
    88  		if err != nil {
    89  			return nil, errors.Trace(err)
    90  		}
    91  		keys = append(keys, bytes.CloneBytes(mvccK))
    92  	}
    93  	return keys, nil
    94  }
    95  
    96  func (gc *localstoreCompactor) deleteWorker() {
    97  	defer gc.workerWaitGroup.Done()
    98  	cnt := 0
    99  	batch := gc.db.NewBatch()
   100  	for {
   101  		select {
   102  		case <-gc.stopCh:
   103  			return
   104  		case key := <-gc.delCh:
   105  			cnt++
   106  			batch.Delete(key)
   107  			// Batch delete.
   108  			if cnt == gc.policy.BatchDeleteCnt {
   109  				log.Debugf("[kv] GC delete commit %d keys", batch.Len())
   110  				err := gc.db.Commit(batch)
   111  				if err != nil {
   112  					log.Error(err)
   113  				}
   114  				batch = gc.db.NewBatch()
   115  				cnt = 0
   116  			}
   117  		}
   118  	}
   119  }
   120  
   121  func (gc *localstoreCompactor) checkExpiredKeysWorker() {
   122  	defer gc.workerWaitGroup.Done()
   123  	for {
   124  		select {
   125  		case <-gc.stopCh:
   126  			log.Debug("[kv] GC stopped")
   127  			return
   128  		case <-gc.ticker.C:
   129  			gc.mu.Lock()
   130  			m := gc.recentKeys
   131  			if len(m) == 0 {
   132  				gc.mu.Unlock()
   133  				continue
   134  			}
   135  			gc.recentKeys = make(map[string]struct{})
   136  			gc.mu.Unlock()
   137  			for k := range m {
   138  				err := gc.Compact([]byte(k))
   139  				if err != nil {
   140  					log.Error(err)
   141  				}
   142  			}
   143  		}
   144  	}
   145  }
   146  
   147  func (gc *localstoreCompactor) filterExpiredKeys(keys []kv.EncodedKey) []kv.EncodedKey {
   148  	var ret []kv.EncodedKey
   149  	first := true
   150  	currentTS := time.Now().UnixNano() / int64(time.Millisecond)
   151  	// keys are always in descending order.
   152  	for _, k := range keys {
   153  		_, ver, err := MvccDecode(k)
   154  		if err != nil {
   155  			// Should not happen.
   156  			panic(err)
   157  		}
   158  		ts := localVersionToTimestamp(ver)
   159  		// Check timeout keys.
   160  		if currentTS-int64(ts) >= int64(gc.policy.SafePoint) {
   161  			// Skip first version.
   162  			if first {
   163  				first = false
   164  				continue
   165  			}
   166  			ret = append(ret, k)
   167  		}
   168  	}
   169  	return ret
   170  }
   171  
   172  func (gc *localstoreCompactor) Compact(k kv.Key) error {
   173  	keys, err := gc.getAllVersions(k)
   174  	if err != nil {
   175  		return errors.Trace(err)
   176  	}
   177  	filteredKeys := gc.filterExpiredKeys(keys)
   178  	if len(filteredKeys) > 0 {
   179  		log.Debugf("[kv] GC send %d keys to delete worker", len(filteredKeys))
   180  	}
   181  	for _, key := range filteredKeys {
   182  		gc.delCh <- key
   183  	}
   184  	return nil
   185  }
   186  
   187  func (gc *localstoreCompactor) Start() {
   188  	// Start workers.
   189  	gc.workerWaitGroup.Add(deleteWorkerCnt)
   190  	for i := 0; i < deleteWorkerCnt; i++ {
   191  		go gc.deleteWorker()
   192  	}
   193  
   194  	gc.workerWaitGroup.Add(1)
   195  	go gc.checkExpiredKeysWorker()
   196  }
   197  
   198  func (gc *localstoreCompactor) Stop() {
   199  	gc.ticker.Stop()
   200  	close(gc.stopCh)
   201  	// Wait for all workers to finish.
   202  	gc.workerWaitGroup.Wait()
   203  }
   204  
   205  func newLocalCompactor(policy compactPolicy, db engine.DB) *localstoreCompactor {
   206  	return &localstoreCompactor{
   207  		recentKeys:      make(map[string]struct{}),
   208  		stopCh:          make(chan struct{}),
   209  		delCh:           make(chan kv.EncodedKey, 100),
   210  		ticker:          time.NewTicker(policy.TriggerInterval),
   211  		policy:          policy,
   212  		db:              db,
   213  		workerWaitGroup: &sync.WaitGroup{},
   214  	}
   215  }