This repository has been archived by the owner on Jun 17, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 72
Gossip DB fault tolerance #344
Merged
devintegral
merged 28 commits into
Fantom-foundation:scope2
from
sfxdxdev:gossip-fault-tolerance
Oct 9, 2019
Merged
Changes from 10 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
ce61db8
poset db tests stability
sfxdxdev 4487cf6
Store.Commit() panics on error
sfxdxdev f11a47b
gossip.Store commit and dirty flag
sfxdxdev d1c780a
commit epoch store
sfxdxdev a46a259
gossip.Service.ApplyBlock -> onNewBlock
sfxdxdev 6b43aae
wip
devintegral2 f88e92b
check dbs are synced
devintegral2 b437828
super db
devintegral2 efdde51
fixes
devintegral2 e49a514
sync
devintegral2 a0b5215
kvdb.SuperDb --> flushable.SyncedPool
sfxdxdev 775de82
deadlock fix
sfxdxdev 24f7a3c
kvdb.DbProducer makes real db, flushable.Flushable manages mem cache,…
sfxdxdev 6ef9330
memorydb producer
sfxdxdev 70466a5
flushable: lazy db creation
sfxdxdev 836be6a
leveldb producer
sfxdxdev 487370d
kvdb/fallible: no custom onClose, onDrop
sfxdxdev 99cb467
flushable.SyncedPool
sfxdxdev 5cf38a3
poset.Store uses flushable.SyncedPool
sfxdxdev d87338a
gossip.Store uses flushable.SyncedPool
sfxdxdev d6c4788
integration uses flushable.SyncedPool
sfxdxdev 2dc9d53
fix flushable/iterator.Next()
sfxdxdev 3c6b92d
fix flushable/SyncedPool callbacks
sfxdxdev 82f1f03
flushable.SyncedPool: no GetLastDb()/GetDbByIndex()
sfxdxdev c620df3
poset.Store: flush if needed
sfxdxdev 1af3142
uniq namespace if tests count > 1
sfxdxdev 5154a58
poset.TestRestore temporary fix
sfxdxdev 5c7ec30
golangcibot fixes
sfxdxdev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"github.com/Fantom-foundation/go-lachesis/hash" | ||
"github.com/Fantom-foundation/go-lachesis/kvdb" | ||
"github.com/Fantom-foundation/go-lachesis/kvdb/flushable" | ||
"time" | ||
) | ||
|
||
type RegisteredDbs struct { | ||
wrappers map[string]kvdb.FlushableKeyValueStore | ||
bareDbs map[string]kvdb.KeyValueStore | ||
|
||
queuedDrops map[string]struct{} | ||
|
||
prevFlushTime time.Time | ||
} | ||
|
||
func (dbs *RegisteredDbs) Drop(name string) { | ||
dbs.queuedDrops[name] = struct{}{} | ||
} | ||
|
||
func (dbs *RegisteredDbs) Register(name string, db kvdb.KeyValueStore) { | ||
wrapper := flushable.New(db) | ||
wrapper.SetDropper(func() { | ||
dbs.Drop(name) | ||
}) | ||
|
||
dbs.bareDbs[name] = db | ||
dbs.wrappers[name] = wrapper | ||
delete(dbs.queuedDrops, name) | ||
} | ||
|
||
func (dbs *RegisteredDbs) Flush(id hash.Event) error { | ||
key := []byte("mark") | ||
|
||
// dirty flag | ||
for _, db := range dbs.bareDbs { | ||
marker := bytes.NewBuffer(nil) | ||
prev, err := db.Get(key) | ||
if err != nil { | ||
return err | ||
} | ||
if prev == nil { | ||
return errors.New("not found prev flushed state marker") | ||
} | ||
|
||
marker.Write([]byte("dirty")) | ||
marker.Write(prev) | ||
marker.Write([]byte("->")) | ||
marker.Write(id.Bytes()) | ||
err = db.Put(key, marker.Bytes()) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
// flush (along with clean flag) | ||
for _, db := range dbs.wrappers { | ||
db.Flush() // flush data | ||
err := db.Put(key, id.Bytes()) | ||
if err != nil { | ||
return err | ||
} | ||
db.Flush() // flush clean flag | ||
} | ||
|
||
// drop old DBs | ||
for name := range dbs.queuedDrops { | ||
db := dbs.bareDbs[name] | ||
if db == nil { | ||
// ???? we should register all the DBs at startup | ||
continue | ||
} | ||
db.Drop() | ||
} | ||
|
||
dbs.prevFlushTime = time.Now() | ||
return nil | ||
} | ||
|
||
func (dbs *RegisteredDbs) FlushIfNeeded(id hash.Event) bool { | ||
if time.Since(dbs.prevFlushTime) > 10 * time.Minute { | ||
dbs.Flush(id) | ||
return true | ||
} | ||
|
||
totalNotFlushed := 0 | ||
for _, db := range dbs.wrappers { | ||
totalNotFlushed += db.NotFlushedSizeEst() | ||
} | ||
|
||
if totalNotFlushed > 100 * 1024 * 1024 { | ||
dbs.Flush(id) | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
// call on startup, after all dbs are registered | ||
func (dbs *RegisteredDbs) CheckDbsSynced() error { | ||
key := []byte("mark") | ||
var prevId *hash.Event | ||
for _, db := range dbs.bareDbs { | ||
mark, err := db.Get(key) | ||
if err != nil { | ||
return err | ||
} | ||
if bytes.HasPrefix(mark, []byte("dirty")) { | ||
return errors.New("dirty") | ||
} | ||
eventId := hash.BytesToEvent(mark) | ||
if prevId == nil { | ||
prevId = &eventId | ||
} | ||
if eventId != *prevId { | ||
return errors.New("not synced") | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,9 @@ import ( | |
"github.com/ethereum/go-ethereum/ethdb" | ||
"github.com/ethereum/go-ethereum/rlp" | ||
|
||
"github.com/Fantom-foundation/go-lachesis/inter/idx" | ||
"github.com/Fantom-foundation/go-lachesis/kvdb" | ||
"github.com/Fantom-foundation/go-lachesis/kvdb/flushable" | ||
"github.com/Fantom-foundation/go-lachesis/kvdb/memorydb" | ||
"github.com/Fantom-foundation/go-lachesis/kvdb/no_key_is_err" | ||
"github.com/Fantom-foundation/go-lachesis/kvdb/table" | ||
|
@@ -16,9 +18,10 @@ import ( | |
|
||
// Store is a node persistent storage working over physical key-value database. | ||
type Store struct { | ||
persistentDB kvdb.KeyValueStore | ||
bareDb kvdb.KeyValueStore | ||
|
||
table struct { | ||
mainDb *flushable.Flushable | ||
table struct { | ||
Peers kvdb.KeyValueStore `table:"peer_"` | ||
Events kvdb.KeyValueStore `table:"event_"` | ||
Blocks kvdb.KeyValueStore `table:"block_"` | ||
|
@@ -47,14 +50,19 @@ type Store struct { | |
// NewStore creates store over key-value db. | ||
func NewStore(db kvdb.KeyValueStore, makeDb func(name string) kvdb.KeyValueStore) *Store { | ||
s := &Store{ | ||
persistentDB: db, | ||
makeDb: makeDb, | ||
Instance: logger.MakeInstance(), | ||
bareDb: db, | ||
mainDb: flushable.New(db), | ||
makeDb: makeDb, | ||
Instance: logger.MakeInstance(), | ||
} | ||
|
||
table.MigrateTables(&s.table, s.persistentDB) | ||
table.MigrateTables(&s.table, s.mainDb) | ||
|
||
evmTable := no_key_is_err.Wrap(table.New(s.persistentDB, []byte("evm_"))) // ETH expects that "not found" is an error | ||
if s.isDirty() { | ||
s.Log.Crit("Service DB is possible inconsistent. Recreate it.") | ||
} | ||
|
||
evmTable := no_key_is_err.Wrap(table.New(s.mainDb, []byte("evm_"))) // ETH expects that "not found" is an error | ||
s.table.Evm = rawdb.NewDatabase(evmTable) | ||
s.table.EvmState = state.NewDatabase(s.table.Evm) | ||
|
||
|
@@ -74,7 +82,48 @@ func NewMemStore() *Store { | |
// Close leaves underlying database. | ||
func (s *Store) Close() { | ||
table.MigrateTables(&s.table, nil) | ||
s.persistentDB.Close() | ||
s.mainDb.Close() | ||
} | ||
|
||
// Commit changes. | ||
func (s *Store) Commit(epoch idx.Epoch) { | ||
s.setDirty(true) | ||
defer s.setDirty(false) | ||
|
||
err := s.commitEpochStore(epoch) | ||
if err != nil { | ||
s.Log.Crit("epoch DB commit", "err", err) | ||
} | ||
|
||
err = s.mainDb.Flush() | ||
if err != nil { | ||
s.Log.Crit("main DB commit", "err", err) | ||
} | ||
} | ||
|
||
// setDirty sets dirty flag. | ||
func (s *Store) setDirty(flag bool) { | ||
key := []byte("is_dirty") | ||
val := make([]byte, 1, 1) | ||
if flag { | ||
val[0] = 1 | ||
} | ||
|
||
err := s.bareDb.Put(key, val) | ||
if err != nil { | ||
s.Log.Crit("Failed to put key-value", "err", err) | ||
} | ||
} | ||
|
||
// isDirty gets dirty flag. | ||
func (s *Store) isDirty() bool { | ||
key := []byte("is_dirty") | ||
val, err := s.bareDb.Get(key) | ||
if err != nil { | ||
s.Log.Crit("Failed to get value", "err", err) | ||
} | ||
|
||
return len(val) > 1 && val[0] != 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. len(val) > 0 && val[0] ==1 |
||
} | ||
|
||
// StateDB returns state database. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
S1019: should use make([]byte, 1) instead (from
gosimple
)