github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/store_epoch.go (about)

     1  package gossip
     2  
     3  /*
     4  	In LRU cache data stored like pointer
     5  */
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"sync/atomic"
    11  
    12  	"github.com/unicornultrafoundation/go-helios/native/idx"
    13  	"github.com/unicornultrafoundation/go-helios/u2udb"
    14  	"github.com/unicornultrafoundation/go-helios/u2udb/skiperrors"
    15  	"github.com/unicornultrafoundation/go-helios/u2udb/table"
    16  
    17  	"github.com/unicornultrafoundation/go-u2u/logger"
    18  )
    19  
    20  var (
    21  	errDBClosed = errors.New("database closed")
    22  )
    23  
    24  type (
    25  	epochStore struct {
    26  		epoch idx.Epoch
    27  		db    u2udb.Store
    28  		table struct {
    29  			LastEvents u2udb.Store `table:"t"`
    30  			Heads      u2udb.Store `table:"H"`
    31  			DagIndex   u2udb.Store `table:"v"`
    32  		}
    33  		cache struct {
    34  			Heads      atomic.Value
    35  			LastEvents atomic.Value
    36  		}
    37  
    38  		logger.Instance
    39  	}
    40  )
    41  
    42  func newEpochStore(epoch idx.Epoch, db u2udb.Store) *epochStore {
    43  	es := &epochStore{
    44  		epoch:    epoch,
    45  		db:       db,
    46  		Instance: logger.New("epoch-store"),
    47  	}
    48  	table.MigrateTables(&es.table, db)
    49  
    50  	// wrap with skiperrors to skip errors on reading from a dropped DB
    51  	es.table.LastEvents = skiperrors.Wrap(es.table.LastEvents, errDBClosed)
    52  	es.table.Heads = skiperrors.Wrap(es.table.Heads, errDBClosed)
    53  
    54  	// load the cache to avoid a race condition
    55  	es.GetHeads()
    56  	es.GetLastEvents()
    57  
    58  	return es
    59  }
    60  
    61  func (s *Store) getAnyEpochStore() *epochStore {
    62  	_es := s.epochStore.Load()
    63  	if _es == nil {
    64  		return nil
    65  	}
    66  	return _es.(*epochStore)
    67  }
    68  
    69  // getEpochStore is safe for concurrent use.
    70  func (s *Store) getEpochStore(epoch idx.Epoch) *epochStore {
    71  	es := s.getAnyEpochStore()
    72  	if es.epoch != epoch {
    73  		return nil
    74  	}
    75  	return es
    76  }
    77  
    78  func (s *Store) resetEpochStore(newEpoch idx.Epoch) {
    79  	oldEs := s.epochStore.Load()
    80  	// create new DB
    81  	s.createEpochStore(newEpoch)
    82  	// drop previous DB
    83  	// there may be race condition with threads which hold this DB, so wrap tables with skiperrors
    84  	if oldEs != nil {
    85  		err := oldEs.(*epochStore).db.Close()
    86  		if err != nil {
    87  			s.Log.Error("Failed to close epoch DB", "err", err)
    88  			return
    89  		}
    90  		oldEs.(*epochStore).db.Drop()
    91  	}
    92  }
    93  
    94  func (s *Store) loadEpochStore(epoch idx.Epoch) {
    95  	if s.epochStore.Load() != nil {
    96  		return
    97  	}
    98  	s.createEpochStore(epoch)
    99  }
   100  
   101  func (s *Store) closeEpochStore() error {
   102  	es := s.getAnyEpochStore()
   103  	if es == nil {
   104  		return nil
   105  	}
   106  	return es.db.Close()
   107  }
   108  
   109  func (s *Store) createEpochStore(epoch idx.Epoch) {
   110  	// create new DB
   111  	name := fmt.Sprintf("gossip-%d", epoch)
   112  	db, err := s.dbs.OpenDB(name)
   113  	if err != nil {
   114  		s.Log.Crit("Filed to open DB", "name", name, "err", err)
   115  	}
   116  	s.epochStore.Store(newEpochStore(epoch, db))
   117  }