github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/utils/dbutil/autocompact/store.go (about)

     1  package autocompact
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/status-im/keycard-go/hexutils"
     7  
     8  	"github.com/unicornultrafoundation/go-u2u/log"
     9  
    10  	"github.com/unicornultrafoundation/go-helios/u2udb"
    11  )
    12  
    13  // Store implements automatic compacting of recently inserted/erased data according to provided strategy
    14  type Store struct {
    15  	u2udb.Store
    16  	limit   uint64
    17  	cont    ContainerI
    18  	newCont func() ContainerI
    19  	compMu  sync.Mutex
    20  	name    string
    21  }
    22  
    23  type Batch struct {
    24  	u2udb.Batch
    25  	store *Store
    26  	cont  ContainerI
    27  }
    28  
    29  func Wrap(s u2udb.Store, limit uint64, strategy func() ContainerI, name string) *Store {
    30  	return &Store{
    31  		Store:   s,
    32  		limit:   limit,
    33  		newCont: strategy,
    34  		cont:    strategy(),
    35  		name:    name,
    36  	}
    37  }
    38  
    39  func Wrap2(s u2udb.Store, limit1 uint64, limit2 uint64, strategy func() ContainerI, name string) *Store {
    40  	return Wrap(Wrap(s, limit1, strategy, name), limit2, strategy, name)
    41  }
    42  
    43  func Wrap2M(s u2udb.Store, limit1 uint64, limit2 uint64, forward bool, name string) *Store {
    44  	strategy := NewBackwardsCont
    45  	if forward {
    46  		strategy = NewForwardCont
    47  	}
    48  	return Wrap2(s, limit1, limit2, strategy, name)
    49  }
    50  
    51  func estSize(keyLen int, valLen int) uint64 {
    52  	// Storage overheads, related to adding/deleting a record,
    53  	//wouldn't be only proportional to length of key and value.
    54  	//E.g. if one adds 10 records with length of 2, it will be more expensive than 1 record with length 20
    55  	// Now, 64 wasn't really calculated but is rather a guesstimation
    56  	return uint64(keyLen + valLen + 64)
    57  }
    58  
    59  func (s *Store) onWrite(key []byte, size uint64) {
    60  	s.compMu.Lock()
    61  	defer s.compMu.Unlock()
    62  	if key != nil {
    63  		s.cont.Add(key, size)
    64  	}
    65  	s.mayCompact(false)
    66  }
    67  
    68  func (s *Store) onBatchWrite(batchCont ContainerI) {
    69  	s.compMu.Lock()
    70  	defer s.compMu.Unlock()
    71  	s.cont.Merge(batchCont)
    72  	s.mayCompact(false)
    73  }
    74  
    75  func (s *Store) compact() {
    76  	s.compMu.Lock()
    77  	defer s.compMu.Unlock()
    78  	s.mayCompact(true)
    79  }
    80  
    81  func (s *Store) mayCompact(force bool) {
    82  	// error handling
    83  	err := s.cont.Error()
    84  	if err != nil {
    85  		s.cont.Reset()
    86  		s.newCont = NewDevnullCont
    87  		s.cont = s.newCont()
    88  		log.Warn("Autocompaction failed, which may lead to performance issues", "name", s.name, "err", err)
    89  	}
    90  
    91  	if force || s.cont.Size() > s.limit {
    92  		for _, r := range s.cont.Ranges() {
    93  			log.Debug("Autocompact", "name", s.name, "from", hexutils.BytesToHex(r.minKey), "to", hexutils.BytesToHex(r.maxKey))
    94  			_ = s.Store.Compact(r.minKey, r.maxKey)
    95  		}
    96  		s.cont.Reset()
    97  	}
    98  }
    99  
   100  func (s *Store) Put(key []byte, value []byte) error {
   101  	defer s.onWrite(key, estSize(len(key), len(value)))
   102  	return s.Store.Put(key, value)
   103  }
   104  
   105  func (s *Store) Delete(key []byte) error {
   106  	defer s.onWrite(key, estSize(len(key), 0))
   107  	return s.Store.Delete(key)
   108  }
   109  
   110  func (s *Store) Close() error {
   111  	s.compact()
   112  	return s.Store.Close()
   113  }
   114  
   115  func (s *Store) NewBatch() u2udb.Batch {
   116  	batch := s.Store.NewBatch()
   117  	if batch == nil {
   118  		return nil
   119  	}
   120  	return &Batch{
   121  		Batch: batch,
   122  		store: s,
   123  		cont:  s.newCont(),
   124  	}
   125  }
   126  
   127  func (s *Batch) Put(key []byte, value []byte) error {
   128  	s.cont.Add(key, estSize(len(key), len(value)))
   129  	return s.Batch.Put(key, value)
   130  }
   131  
   132  func (s *Batch) Delete(key []byte) error {
   133  	s.cont.Add(key, estSize(len(key), 0))
   134  	return s.Batch.Delete(key)
   135  }
   136  
   137  func (s *Batch) Reset() {
   138  	s.cont.Reset()
   139  	s.Batch.Reset()
   140  }
   141  
   142  func (s *Batch) Write() error {
   143  	defer s.store.onBatchWrite(s.cont)
   144  	return s.Batch.Write()
   145  }