mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
core, ethdb, triedb: use db.sync
This commit is contained in:
parent
b3d0362fa9
commit
a64a85cf9a
6 changed files with 95 additions and 12 deletions
|
|
@ -979,17 +979,16 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
|||
// Ignore the error here since light client won't hit this path
|
||||
frozen, _ := bc.db.Ancients()
|
||||
if num+1 <= frozen {
|
||||
// Truncate all relative data(header, total difficulty, body, receipt
|
||||
// and canonical hash) from ancient store.
|
||||
if _, err := bc.db.TruncateHead(num); err != nil {
|
||||
log.Crit("Failed to truncate ancient data", "number", num, "err", err)
|
||||
}
|
||||
// Remove the hash <-> number mapping from the active store.
|
||||
rawdb.DeleteHeaderNumber(db, hash)
|
||||
// The chain segment, such as the block header, canonical hash,
|
||||
// body, and receipt, will be removed from the ancient store
|
||||
// in one go.
|
||||
//
|
||||
// The hash-to-number mapping in the key-value store will be
|
||||
// removed by the hc.SetHead function.
|
||||
} else {
|
||||
// Remove relative body and receipts from the active store.
|
||||
// The header, total difficulty and canonical hash will be
|
||||
// removed in the hc.SetHead function.
|
||||
// Remove the associated body and receipts from the key-value store.
|
||||
// The header, hash-to-number mapping, and canonical hash will be
|
||||
// removed by the hc.SetHead function.
|
||||
rawdb.DeleteBody(db, hash, num)
|
||||
rawdb.DeleteReceipts(db, hash, num)
|
||||
}
|
||||
|
|
@ -2627,6 +2626,7 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
|
||||
if err := bc.db.SyncAncient(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -591,17 +591,45 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
|||
hashes = append(hashes, hdr.Hash())
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
// Remove the associated block body and receipts if required.
|
||||
//
|
||||
// If the block is in the chain freezer, then this delete operation
|
||||
// is actually ineffective.
|
||||
if delFn != nil {
|
||||
delFn(batch, hash, num)
|
||||
}
|
||||
// Remove the hash->number mapping along with the header itself
|
||||
rawdb.DeleteHeader(batch, hash, num)
|
||||
}
|
||||
// Remove the number->hash mapping
|
||||
rawdb.DeleteCanonicalHash(batch, num)
|
||||
}
|
||||
}
|
||||
// Flush all accumulated deletions.
|
||||
if err := batch.Write(); err != nil {
|
||||
log.Crit("Failed to rewind block", "error", err)
|
||||
log.Crit("Failed to commit batch in setHead", "err", err)
|
||||
}
|
||||
// Explicitly flush the pending writes in the key-value store to disk, ensuring
|
||||
// data durability of the previous deletions.
|
||||
if err := hc.chainDb.Sync(); err != nil {
|
||||
log.Crit("Failed to sync the key-value store in setHead", "err", err)
|
||||
}
|
||||
// Truncate the excessive chain segments in the ancient store.
|
||||
// These are actually deferred deletions from the loop above.
|
||||
//
|
||||
// This step must be performed after synchronizing the key-value store;
|
||||
// otherwise, in the event of a panic, it's theoretically possible to
|
||||
// lose recent key-value store writes while the ancient store deletions
|
||||
// remain, leading to data inconsistency.
|
||||
if delFn != nil {
|
||||
// Ignore the error here since light client won't hit this path
|
||||
frozen, _ := hc.chainDb.Ancients()
|
||||
if headBlock+1 < frozen {
|
||||
_, err := hc.chainDb.TruncateHead(headBlock + 1)
|
||||
if err != nil {
|
||||
log.Crit("Failed to truncate head block", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clear out any stale content from the caches
|
||||
hc.headerCache.Purge()
|
||||
|
|
|
|||
|
|
@ -327,6 +327,15 @@ func (db *Database) Path() string {
|
|||
// Sync flushes all pending writes in the write-ahead-log to disk, ensuring
|
||||
// data durability up to that point.
|
||||
func (db *Database) Sync() error {
|
||||
// In theory, the WAL (Write-Ahead Log) can be explicitly synchronized using
|
||||
// a write operation with SYNC=true. However, there is no dedicated key reserved
|
||||
// for this purpose, and even a nil key (key=nil) is considered a valid database entry.
|
||||
//
|
||||
// In LevelDB, writes are blocked until the data is written to the WAL, meaning
|
||||
// recent writes won't be lost unless a power failure or system crash occurs.
|
||||
// Additionally, LevelDB is no longer the default database engine and is likely
|
||||
// only used by hash-mode archive nodes. Given this, the durability guarantees
|
||||
// without explicit sync are acceptable in the context of LevelDB.
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -227,8 +227,16 @@ func New(file string, cache int, handles int, namespace string, readonly bool) (
|
|||
WriteStallBegin: db.onWriteStallBegin,
|
||||
WriteStallEnd: db.onWriteStallEnd,
|
||||
},
|
||||
Logger: panicLogger{}, // TODO(karalabe): Delete when this is upstreamed in Pebble
|
||||
|
||||
// Pebble is configured to use asynchronous write mode, meaning write operations
|
||||
// return as soon as the data is cached in memory, without waiting for the WAL
|
||||
// to be written. This mode offers better write performance but risks losing
|
||||
// recent writes if the application crashes or a power failure/system crash occurs.
|
||||
//
|
||||
// By setting the WALBytesPerSync, the cached WAL writes will be periodically
|
||||
// flushed at the background if the accumulated size exceeds this threshold.
|
||||
WALBytesPerSync: 5 * ethdb.IdealBatchSize,
|
||||
Logger: panicLogger{}, // TODO(karalabe): Delete when this is upstreamed in Pebble
|
||||
}
|
||||
// Disable seek compaction explicitly. Check https://github.com/ethereum/go-ethereum/pull/20130
|
||||
// for more details.
|
||||
|
|
@ -419,6 +427,11 @@ func (d *Database) Path() string {
|
|||
// data durability up to that point.
|
||||
func (d *Database) Sync() error {
|
||||
b := d.db.NewBatch()
|
||||
|
||||
// The entry (value=nil) is not written to the database; it is only
|
||||
// added to the WAL. Writing this special log entry in sync mode
|
||||
// automatically flushes all previous writes, ensuring database
|
||||
// durability up to this point.
|
||||
b.LogData(nil, nil)
|
||||
return d.db.Apply(b, pebble.Sync)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package pebble
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/cockroachdb/pebble"
|
||||
|
|
@ -54,3 +55,26 @@ func BenchmarkPebbleDB(b *testing.B) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPebbleLogData(t *testing.T) {
|
||||
db, err := pebble.Open("", &pebble.Options{
|
||||
FS: vfs.NewMem(),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, _, err = db.Get(nil)
|
||||
if !errors.Is(err, pebble.ErrNotFound) {
|
||||
t.Fatal("Unknown database entry")
|
||||
}
|
||||
|
||||
b := db.NewBatch()
|
||||
b.LogData(nil, nil)
|
||||
db.Apply(b, pebble.Sync)
|
||||
|
||||
_, _, err = db.Get(nil)
|
||||
if !errors.Is(err, pebble.ErrNotFound) {
|
||||
t.Fatal("Unknown database entry")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -454,6 +454,15 @@ func (db *Database) Recover(root common.Hash) error {
|
|||
db.tree.reset(dl)
|
||||
}
|
||||
rawdb.DeleteTrieJournal(db.diskdb)
|
||||
|
||||
// Explicitly sync the key-value store to ensure all recent writes are
|
||||
// flushed to disk. This step is crucial to prevent a scenario where
|
||||
// recent key-value writes are lost due to an application panic, while
|
||||
// the associated state histories have already been removed, resulting
|
||||
// in the inability to perform a state rollback.
|
||||
if err := db.diskdb.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := truncateFromHead(db.diskdb, db.freezer, dl.stateID())
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
Loading…
Reference in a new issue