Skip to content
Snippets Groups Projects
Unverified Commit 77814218 authored by Luca Moser's avatar Luca Moser Committed by GitHub
Browse files

Removes periodic badgerdb gc & adds dirty/health flag to db (#526)

* removes periodic badgerdb gc, adds dirty/health flag to db, do badgerdb gc at startup/shutdown

* removes error shadowing

* instruct the user to delete the db folder if the db can't be opened

* addresses review comments
parent c44d9b88
No related branches found
No related tags found
No related merge requests found
...@@ -7,5 +7,5 @@ const ( ...@@ -7,5 +7,5 @@ const (
DBPrefixTransactionMetadata DBPrefixTransactionMetadata
DBPrefixAddressTransactions DBPrefixAddressTransactions
DBPrefixAutoPeering DBPrefixAutoPeering
DBPrefixDatabaseVersion DBPrefixHealth
) )
...@@ -16,5 +16,4 @@ const ( ...@@ -16,5 +16,4 @@ const (
PrioritySynchronization PrioritySynchronization
PriorityBootstrap PriorityBootstrap
PrioritySpammer PrioritySpammer
PriorityBadgerGarbageCollection
) )
package database
import (
"errors"
"fmt"
"github.com/iotaledger/goshimmer/packages/database/prefix"
"github.com/iotaledger/hive.go/kvstore"
)
var (
healthStore kvstore.KVStore
healthKey = []byte("db_health")
)
func configureHealthStore(store kvstore.KVStore) {
healthStore = store.WithRealm([]byte{prefix.DBPrefixHealth})
}
// MarkDatabaseUnhealthy marks the database as not healthy, meaning
// that it wasn't shutdown properly.
func MarkDatabaseUnhealthy() {
if err := healthStore.Set(healthKey, []byte{}); err != nil {
panic(fmt.Errorf("failed to set database health state: %w", err))
}
}
// MarkDatabaseHealthy marks the database as healthy, respectively correctly closed.
func MarkDatabaseHealthy() {
if err := healthStore.Delete(healthKey); err != nil && !errors.Is(err, kvstore.ErrKeyNotFound) {
panic(fmt.Errorf("failed to set database health state: %w", err))
}
}
// IsDatabaseUnhealthy tells whether the database is unhealthy, meaning not shutdown properly.
func IsDatabaseUnhealthy() bool {
contains, err := healthStore.Has(healthKey)
if err != nil {
panic(fmt.Errorf("failed to set database health state: %w", err))
}
return contains
}
...@@ -7,14 +7,12 @@ import ( ...@@ -7,14 +7,12 @@ import (
"time" "time"
"github.com/iotaledger/goshimmer/packages/database" "github.com/iotaledger/goshimmer/packages/database"
"github.com/iotaledger/goshimmer/packages/database/prefix"
"github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/packages/shutdown"
"github.com/iotaledger/goshimmer/plugins/config" "github.com/iotaledger/goshimmer/plugins/config"
"github.com/iotaledger/hive.go/daemon" "github.com/iotaledger/hive.go/daemon"
"github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/kvstore"
"github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/logger"
"github.com/iotaledger/hive.go/node" "github.com/iotaledger/hive.go/node"
"github.com/iotaledger/hive.go/timeutil"
) )
// PluginName is the name of the database plugin. // PluginName is the name of the database plugin.
...@@ -22,7 +20,7 @@ const PluginName = "Database" ...@@ -22,7 +20,7 @@ const PluginName = "Database"
var ( var (
// Plugin is the plugin instance of the database plugin. // Plugin is the plugin instance of the database plugin.
Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) Plugin = node.NewPlugin(PluginName, node.Enabled, configure)
log *logger.Logger log *logger.Logger
db database.DB db database.DB
...@@ -52,7 +50,7 @@ func createStore() { ...@@ -52,7 +50,7 @@ func createStore() {
db, err = database.NewDB(dbDir) db, err = database.NewDB(dbDir)
} }
if err != nil { if err != nil {
log.Fatal(err) log.Fatal("Unable to open the database, please delete the database folder. Error: %s", err)
} }
store = db.NewStore() store = db.NewStore()
...@@ -61,30 +59,38 @@ func createStore() { ...@@ -61,30 +59,38 @@ func createStore() {
func configure(_ *node.Plugin) { func configure(_ *node.Plugin) {
// assure that the store is initialized // assure that the store is initialized
store := Store() store := Store()
configureHealthStore(store)
err := checkDatabaseVersion(store.WithRealm([]byte{prefix.DBPrefixDatabaseVersion})) if err := checkDatabaseVersion(healthStore); err != nil {
if errors.Is(err, ErrDBVersionIncompatible) { if errors.Is(err, ErrDBVersionIncompatible) {
log.Panicf("The database scheme was updated. Please delete the database folder.\n%s", err) log.Fatalf("The database scheme was updated. Please delete the database folder. %s", err)
}
log.Fatalf("Failed to check database version: %s", err)
} }
if err != nil {
log.Panicf("Failed to check database version: %s", err) if IsDatabaseUnhealthy() {
log.Fatal("The database is marked as not properly shutdown/corrupted, please delete the database folder and restart.")
} }
// we open the database in the configure, so we must also make sure it's closed here // we open the database in the configure, so we must also make sure it's closed here
err = daemon.BackgroundWorker(PluginName, closeDB, shutdown.PriorityDatabase) if err := daemon.BackgroundWorker(PluginName, manageDBLifetime, shutdown.PriorityDatabase); err != nil {
if err != nil { log.Fatalf("Failed to start as daemon: %s", err)
log.Panicf("Failed to start as daemon: %s", err)
} }
}
func run(_ *node.Plugin) { // run GC up on startup
if err := daemon.BackgroundWorker(PluginName+"[GC]", runGC, shutdown.PriorityBadgerGarbageCollection); err != nil { runDatabaseGC()
log.Panicf("Failed to start as daemon: %s", err)
}
} }
func closeDB(shutdownSignal <-chan struct{}) { // manageDBLifetime takes care of managing the lifetime of the database. It marks the database as dirty up on
// startup and unmarks it up on shutdown. Up on shutdown it will run the db GC and then close the database.
func manageDBLifetime(shutdownSignal <-chan struct{}) {
// we mark the database only as corrupted from within a background worker, which means
// that we only mark it as dirty, if the node actually started up properly (meaning no termination
// signal was received before all plugins loaded).
MarkDatabaseUnhealthy()
<-shutdownSignal <-shutdownSignal
runDatabaseGC()
MarkDatabaseHealthy()
log.Infof("Syncing database to disk...") log.Infof("Syncing database to disk...")
if err := db.Close(); err != nil { if err := db.Close(); err != nil {
log.Errorf("Failed to flush the database: %s", err) log.Errorf("Failed to flush the database: %s", err)
...@@ -92,14 +98,15 @@ func closeDB(shutdownSignal <-chan struct{}) { ...@@ -92,14 +98,15 @@ func closeDB(shutdownSignal <-chan struct{}) {
log.Infof("Syncing database to disk... done") log.Infof("Syncing database to disk... done")
} }
func runGC(shutdownSignal <-chan struct{}) { func runDatabaseGC() {
if !db.RequiresGC() { if !db.RequiresGC() {
return return
} }
// run the garbage collection with the given interval log.Info("Running database garbage collection...")
timeutil.Ticker(func() { s := time.Now()
if err := db.GC(); err != nil { if err := db.GC(); err != nil {
log.Warnf("Garbage collection failed: %s", err) log.Warnf("Database garbage collection failed: %s", err)
} return
}, 5*time.Minute, shutdownSignal) }
log.Infof("Database garbage collection done, took %v...", time.Since(s))
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment