github.com/ronaksoft/rony@v0.16.26-0.20230807065236-1743dbfe6959/store/store.go (about)

     1  package store
     2  
     3  import (
     4  	"path/filepath"
     5  	"time"
     6  
     7  	"github.com/dgraph-io/badger/v3"
     8  	"github.com/ronaksoft/rony"
     9  	"github.com/ronaksoft/rony/di"
    10  	"github.com/ronaksoft/rony/internal/metrics"
    11  	"github.com/ronaksoft/rony/tools"
    12  )
    13  
    14  /*
    15     Creation Time: 2021 - May - 16
    16     Created by:  (ehsan)
    17     Maintainers:
    18        1.  Ehsan N. Moosa (E2)
    19     Auditor: Ehsan N. Moosa (E2)
    20     Copyright Ronak Software Group 2020
    21  */
    22  
    23  var (
    24  	vlogTicker          *time.Ticker // runs every 1m, check size of vlog and run GC conditionally.
    25  	mandatoryVlogTicker *time.Ticker // runs every 10m, we always run vlog GC.
    26  )
    27  
    28  type Store struct {
    29  	db *badger.DB
    30  }
    31  
    32  func New(cfg Config) (*Store, error) {
    33  	st := &Store{}
    34  	db, err := newDB(cfg)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	st.db = db
    39  
    40  	vlogTicker = time.NewTicker(time.Minute)
    41  	mandatoryVlogTicker = time.NewTicker(time.Minute * 10)
    42  	go runVlogGC(db, 1<<30)
    43  
    44  	return st, nil
    45  }
    46  
    47  func ProvideDI(cfg Config) {
    48  	di.MustProvide(func() (*Store, error) {
    49  		return New(cfg)
    50  	})
    51  }
    52  
    53  func newDB(config Config) (*badger.DB, error) {
    54  	opt := badger.DefaultOptions(filepath.Join(config.DirPath, "badger"))
    55  	if config.InMemory {
    56  		opt.InMemory = config.InMemory
    57  		opt.ValueDir = ""
    58  		opt.Dir = ""
    59  	}
    60  
    61  	opt.Logger = nil
    62  
    63  	return badger.Open(opt)
    64  }
    65  
    66  func runVlogGC(db *badger.DB, threshold int64) {
    67  	// Get initial size on start.
    68  	_, lastVlogSize := db.Size()
    69  
    70  	runGC := func() {
    71  		var err error
    72  		for err == nil {
    73  			// If a GC is successful, immediately run it again.
    74  			err = db.RunValueLogGC(0.7)
    75  		}
    76  		_, lastVlogSize = db.Size()
    77  	}
    78  
    79  	for {
    80  		select {
    81  		case <-vlogTicker.C:
    82  			_, currentVlogSize := db.Size()
    83  			if currentVlogSize < lastVlogSize+threshold {
    84  				continue
    85  			}
    86  			runGC()
    87  		case <-mandatoryVlogTicker.C:
    88  			runGC()
    89  		}
    90  	}
    91  }
    92  
    93  func (s *Store) View(fn func(txn *rony.StoreTxn) error) error {
    94  	retry := defaultConflictRetries
    95  Retry:
    96  	err := s.db.View(fn)
    97  	if err == badger.ErrConflict {
    98  		if retry--; retry > 0 {
    99  			metrics.IncCounter(metrics.CntStoreConflicts)
   100  			time.Sleep(time.Duration(tools.RandomInt64(int64(defaultMaxInterval))))
   101  
   102  			goto Retry
   103  		}
   104  	}
   105  
   106  	return err
   107  }
   108  
   109  func (s *Store) Update(fn func(txn *rony.StoreTxn) error) error {
   110  	retry := defaultConflictRetries
   111  Retry:
   112  	err := s.db.Update(fn)
   113  	if err == badger.ErrConflict {
   114  		if retry--; retry > 0 {
   115  			metrics.IncCounter(metrics.CntStoreConflicts)
   116  			time.Sleep(time.Duration(tools.RandomInt64(int64(defaultMaxInterval))))
   117  
   118  			goto Retry
   119  		}
   120  	}
   121  
   122  	return err
   123  }
   124  
   125  func (s *Store) Shutdown() {
   126  	if vlogTicker != nil {
   127  		vlogTicker.Stop()
   128  	}
   129  	if mandatoryVlogTicker != nil {
   130  		mandatoryVlogTicker.Stop()
   131  	}
   132  	if s.db != nil {
   133  		_ = s.db.Close()
   134  	}
   135  }
   136  
   137  func (s *Store) LocalDB() *LocalDB {
   138  	return s.db
   139  }