Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core/rawdb, eth, les,node: implemented update for unclean shutdown marker #22974

Closed
wants to merge 22 commits into from
Closed
74 changes: 71 additions & 3 deletions core/rawdb/accessors_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,47 @@ type crashList struct {

const crashesToKeep = 10

// PushUncleanShutdownMarker appends a new unclean shutdown marker and returns
// the previous data
func StartUncleanShutdownMarker(name string, db ethdb.KeyValueStore) {
switch name {
case "les.client":
db.(*nofreezedb).isChainDb = false
return
case "lightchaindata":
db.(*nofreezedb).isChainDb = true
case "chaindata":
break
case "":
break
default:
return
}
if uncleanShutdowns, discards, err := PushUncleanShutdownMarker(db); err != nil {
log.Error("Could not update unclean-shutdown-marker list", "error", err)
} else {
if discards > 0 {
log.Warn("Old unclean shutdowns found", "count", discards)
}
for _, tstamp := range uncleanShutdowns {
t := time.Unix(int64(tstamp), 0)
log.Warn("Unclean shutdown detected", "booted", t, "age", common.PrettyAge(t))
}
}
return
}

// PushUncleanShutdownMarker appends a new unclean shutdown marker and starts a goroutine
// to update the unclean shutdown marker every five minutes. It returns
// the previous unclean shutdown
// - a list of timestamps
// - a count of how many old unclean-shutdowns have been discarded
func PushUncleanShutdownMarker(db ethdb.KeyValueStore) ([]uint64, uint64, error) {
var uncleanShutdowns crashList
// Read old data
if data, err := db.Get(uncleanShutdownKey); err != nil {
log.Warn("Error reading unclean shutdown markers", "error", err)
// don't want to warn if there were no unclean shutdowns
if err.Error() != "leveldb: not found" {
log.Warn("Error reading unclean shutdown markers", "error", err)
}
} else if err := rlp.DecodeBytes(data, &uncleanShutdowns); err != nil {
return nil, 0, err
}
Expand All @@ -118,6 +150,11 @@ func PushUncleanShutdownMarker(db ethdb.KeyValueStore) ([]uint64, uint64, error)
log.Warn("Failed to write unclean-shutdown marker", "err", err)
return nil, 0, err
}
if frdb, ok := db.(*freezerdb); ok {
go UpdateUncleanShutdownMarker(db, frdb.stopUncleanMarkerUpdateCh)
} else if nfrdb, ok := db.(*nofreezedb); ok {
go UpdateUncleanShutdownMarker(db, nfrdb.stopUncleanMarkerUpdateCh)
}
return previous, discarded, nil
}

Expand All @@ -138,3 +175,34 @@ func PopUncleanShutdownMarker(db ethdb.KeyValueStore) {
log.Warn("Failed to clear unclean-shutdown marker", "err", err)
}
}

// UpdateUncleanShutdownMarker updates the current unclean shutdown marker
// every 5 minutes
func UpdateUncleanShutdownMarker(db ethdb.KeyValueStore, stopUncleanShutdownUpdateCh chan bool) {
var uncleanShutdowns crashList
// Read old data
if previous, err := db.Get(uncleanShutdownKey); err != nil {
log.Warn("Error reading unclean shutdown markers", "error", err)
} else if err := rlp.DecodeBytes(previous, &uncleanShutdowns); err != nil {
log.Error("Error decoding unclean shutdown markers", "error", err) // Should mos def _not_ happen
}
l := len(uncleanShutdowns.Recent)
if l == 0 {
l++
}
// update marker every five minutes
ticker := time.NewTicker(300 * time.Second)
defer func() { ticker.Stop() }()
for {
select {
case <-ticker.C:
uncleanShutdowns.Recent[l-1] = uint64(time.Now().Unix())
data, _ := rlp.EncodeToBytes(uncleanShutdowns)
if err := db.Put(uncleanShutdownKey, data); err != nil {
log.Warn("Failed to update unclean-shutdown marker", "err", err)
}
case <-stopUncleanShutdownUpdateCh:
return
}
}
}
32 changes: 28 additions & 4 deletions core/rawdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,20 @@ import (
)

// freezerdb is a database wrapper that enabled freezer data retrievals.
// stopUncleanMarkerUpdateCh is used to stop the unclean shutdown marker
// that updates every 5 minutes.
type freezerdb struct {
ethdb.KeyValueStore
ethdb.AncientStore
stopUncleanMarkerUpdateCh chan bool
}

// Close implements io.Closer, closing both the fast key-value store as well as
// the slow ancient tables.
func (frdb *freezerdb) Close() error {
var errs []error
frdb.stopUncleanMarkerUpdateCh <- true
PopUncleanShutdownMarker(frdb.KeyValueStore)
if err := frdb.AncientStore.Close(); err != nil {
errs = append(errs, err)
}
Expand Down Expand Up @@ -75,8 +80,24 @@ func (frdb *freezerdb) Freeze(threshold uint64) error {
}

// nofreezedb is a database wrapper that disables freezer data retrievals.
// stopUncleanMarkerUpdateCh is used to stop the unclean shutdown marker
// that updates every 5 minutes.
type nofreezedb struct {
ethdb.KeyValueStore
stopUncleanMarkerUpdateCh chan bool
isChainDb bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very un-intuitive IMO. Would be better to have the channel be nil if it's not needed, instead of having a separate boolean on-the-side to tell whether it's used or not.

}

func (db *nofreezedb) Close() error {
if db.isChainDb {
db.stopUncleanMarkerUpdateCh <- true
PopUncleanShutdownMarker(db.KeyValueStore)
}
if err := db.KeyValueStore.Close(); err != nil {
return err
}
return nil

}

// HasAncient returns an error as we don't have a backing chain freezer.
Expand Down Expand Up @@ -117,9 +138,11 @@ func (db *nofreezedb) Sync() error {
// NewDatabase creates a high level database on top of a given key-value data
// store without a freezer moving immutable chain segments into cold storage.
func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
return &nofreezedb{
KeyValueStore: db,
frdb := &nofreezedb{
KeyValueStore: db,
stopUncleanMarkerUpdateCh: make(chan bool),
}
return frdb
}

// NewDatabaseWithFreezer creates a high level database on top of a given key-
Expand Down Expand Up @@ -204,8 +227,9 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace st
}()
}
return &freezerdb{
KeyValueStore: db,
AncientStore: frdb,
KeyValueStore: db,
AncientStore: frdb,
stopUncleanMarkerUpdateCh: make(chan bool),
}, nil
}

Expand Down
14 changes: 0 additions & 14 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,19 +258,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
stack.RegisterAPIs(eth.APIs())
stack.RegisterProtocols(eth.Protocols())
stack.RegisterLifecycle(eth)
// Check for unclean shutdown
if uncleanShutdowns, discards, err := rawdb.PushUncleanShutdownMarker(chainDb); err != nil {
log.Error("Could not update unclean-shutdown-marker list", "error", err)
} else {
if discards > 0 {
log.Warn("Old unclean shutdowns found", "count", discards)
}
for _, tstamp := range uncleanShutdowns {
t := time.Unix(int64(tstamp), 0)
log.Warn("Unclean shutdown detected", "booted", t,
"age", common.PrettyAge(t))
}
}
return eth, nil
}

Expand Down Expand Up @@ -559,7 +546,6 @@ func (s *Ethereum) Stop() error {
s.miner.Stop()
s.blockchain.Stop()
s.engine.Close()
rawdb.PopUncleanShutdownMarker(s.chainDb)
s.chainDb.Close()
s.eventMux.Stop()

Expand Down
15 changes: 0 additions & 15 deletions les/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,20 +181,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
stack.RegisterAPIs(leth.APIs())
stack.RegisterProtocols(leth.Protocols())
stack.RegisterLifecycle(leth)

// Check for unclean shutdown
if uncleanShutdowns, discards, err := rawdb.PushUncleanShutdownMarker(chainDb); err != nil {
log.Error("Could not update unclean-shutdown-marker list", "error", err)
} else {
if discards > 0 {
log.Warn("Old unclean shutdowns found", "count", discards)
}
for _, tstamp := range uncleanShutdowns {
t := time.Unix(int64(tstamp), 0)
log.Warn("Unclean shutdown detected", "booted", t,
"age", common.PrettyAge(t))
}
}
return leth, nil
}

Expand Down Expand Up @@ -383,7 +369,6 @@ func (s *LightEthereum) Stop() error {
s.engine.Close()
s.pruner.close()
s.eventMux.Stop()
rawdb.PopUncleanShutdownMarker(s.chainDb)
s.chainDb.Close()
s.lesDb.Close()
s.wg.Wait()
Expand Down
6 changes: 4 additions & 2 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,12 +556,13 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, r

var db ethdb.Database
var err error
fmt.Println(n.config.DataDir)
if n.config.DataDir == "" {
db = rawdb.NewMemoryDatabase()
} else {
db, err = rawdb.NewLevelDBDatabase(n.ResolvePath(name), cache, handles, namespace, readonly)
}

rawdb.StartUncleanShutdownMarker(name, db)
if err == nil {
db = n.wrapDatabase(db)
}
Expand All @@ -584,6 +585,7 @@ func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer,
var err error
if n.config.DataDir == "" {
db = rawdb.NewMemoryDatabase()
rawdb.StartUncleanShutdownMarker("", db)
} else {
root := n.ResolvePath(name)
switch {
Expand All @@ -593,8 +595,8 @@ func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer,
freezer = n.ResolvePath(freezer)
}
db, err = rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace, readonly)
rawdb.StartUncleanShutdownMarker(name, db)
}

if err == nil {
db = n.wrapDatabase(db)
}
Expand Down