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 }