Skip to content

Commit a64a85c

Browse files
committed
core, ethdb, triedb: use db.sync
1 parent b3d0362 commit a64a85c

File tree

6 files changed

+95
-12
lines changed

6 files changed

+95
-12
lines changed

core/blockchain.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -979,17 +979,16 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
979979
// Ignore the error here since light client won't hit this path
980980
frozen, _ := bc.db.Ancients()
981981
if num+1 <= frozen {
982-
// Truncate all relative data(header, total difficulty, body, receipt
983-
// and canonical hash) from ancient store.
984-
if _, err := bc.db.TruncateHead(num); err != nil {
985-
log.Crit("Failed to truncate ancient data", "number", num, "err", err)
986-
}
987-
// Remove the hash <-> number mapping from the active store.
988-
rawdb.DeleteHeaderNumber(db, hash)
982+
// The chain segment, such as the block header, canonical hash,
983+
// body, and receipt, will be removed from the ancient store
984+
// in one go.
985+
//
986+
// The hash-to-number mapping in the key-value store will be
987+
// removed by the hc.SetHead function.
989988
} else {
990-
// Remove relative body and receipts from the active store.
991-
// The header, total difficulty and canonical hash will be
992-
// removed in the hc.SetHead function.
989+
// Remove the associated body and receipts from the key-value store.
990+
// The header, hash-to-number mapping, and canonical hash will be
991+
// removed by the hc.SetHead function.
993992
rawdb.DeleteBody(db, hash, num)
994993
rawdb.DeleteReceipts(db, hash, num)
995994
}
@@ -2627,6 +2626,7 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
26272626
if err != nil {
26282627
return 0, err
26292628
}
2629+
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
26302630
if err := bc.db.SyncAncient(); err != nil {
26312631
return 0, err
26322632
}

core/headerchain.go

+29-1
Original file line numberDiff line numberDiff line change
@@ -591,17 +591,45 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
591591
hashes = append(hashes, hdr.Hash())
592592
}
593593
for _, hash := range hashes {
594+
// Remove the associated block body and receipts if required.
595+
//
596+
// If the block is in the chain freezer, then this delete operation
597+
// is actually ineffective.
594598
if delFn != nil {
595599
delFn(batch, hash, num)
596600
}
601+
// Remove the hash->number mapping along with the header itself
597602
rawdb.DeleteHeader(batch, hash, num)
598603
}
604+
// Remove the number->hash mapping
599605
rawdb.DeleteCanonicalHash(batch, num)
600606
}
601607
}
602608
// Flush all accumulated deletions.
603609
if err := batch.Write(); err != nil {
604-
log.Crit("Failed to rewind block", "error", err)
610+
log.Crit("Failed to commit batch in setHead", "err", err)
611+
}
612+
// Explicitly flush the pending writes in the key-value store to disk, ensuring
613+
// data durability of the previous deletions.
614+
if err := hc.chainDb.Sync(); err != nil {
615+
log.Crit("Failed to sync the key-value store in setHead", "err", err)
616+
}
617+
// Truncate the excessive chain segments in the ancient store.
618+
// These are actually deferred deletions from the loop above.
619+
//
620+
// This step must be performed after synchronizing the key-value store;
621+
// otherwise, in the event of a panic, it's theoretically possible to
622+
// lose recent key-value store writes while the ancient store deletions
623+
// remain, leading to data inconsistency.
624+
if delFn != nil {
625+
// Ignore the error here since light client won't hit this path
626+
frozen, _ := hc.chainDb.Ancients()
627+
if headBlock+1 < frozen {
628+
_, err := hc.chainDb.TruncateHead(headBlock + 1)
629+
if err != nil {
630+
log.Crit("Failed to truncate head block", "err", err)
631+
}
632+
}
605633
}
606634
// Clear out any stale content from the caches
607635
hc.headerCache.Purge()

ethdb/leveldb/leveldb.go

+9
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,15 @@ func (db *Database) Path() string {
327327
// Sync flushes all pending writes in the write-ahead-log to disk, ensuring
328328
// data durability up to that point.
329329
func (db *Database) Sync() error {
330+
// In theory, the WAL (Write-Ahead Log) can be explicitly synchronized using
331+
// a write operation with SYNC=true. However, there is no dedicated key reserved
332+
// for this purpose, and even a nil key (key=nil) is considered a valid database entry.
333+
//
334+
// In LevelDB, writes are blocked until the data is written to the WAL, meaning
335+
// recent writes won't be lost unless a power failure or system crash occurs.
336+
// Additionally, LevelDB is no longer the default database engine and is likely
337+
// only used by hash-mode archive nodes. Given this, the durability guarantees
338+
// without explicit sync are acceptable in the context of LevelDB.
330339
return nil
331340
}
332341

ethdb/pebble/pebble.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,16 @@ func New(file string, cache int, handles int, namespace string, readonly bool) (
227227
WriteStallBegin: db.onWriteStallBegin,
228228
WriteStallEnd: db.onWriteStallEnd,
229229
},
230+
Logger: panicLogger{}, // TODO(karalabe): Delete when this is upstreamed in Pebble
231+
232+
// Pebble is configured to use asynchronous write mode, meaning write operations
233+
// return as soon as the data is cached in memory, without waiting for the WAL
234+
// to be written. This mode offers better write performance but risks losing
235+
// recent writes if the application crashes or a power failure/system crash occurs.
236+
//
237+
// By setting the WALBytesPerSync, the cached WAL writes will be periodically
238+
// flushed at the background if the accumulated size exceeds this threshold.
230239
WALBytesPerSync: 5 * ethdb.IdealBatchSize,
231-
Logger: panicLogger{}, // TODO(karalabe): Delete when this is upstreamed in Pebble
232240
}
233241
// Disable seek compaction explicitly. Check https://github.com/ethereum/go-ethereum/pull/20130
234242
// for more details.
@@ -419,6 +427,11 @@ func (d *Database) Path() string {
419427
// data durability up to that point.
420428
func (d *Database) Sync() error {
421429
b := d.db.NewBatch()
430+
431+
// The entry (value=nil) is not written to the database; it is only
432+
// added to the WAL. Writing this special log entry in sync mode
433+
// automatically flushes all previous writes, ensuring database
434+
// durability up to this point.
422435
b.LogData(nil, nil)
423436
return d.db.Apply(b, pebble.Sync)
424437
}

ethdb/pebble/pebble_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package pebble
1818

1919
import (
20+
"errors"
2021
"testing"
2122

2223
"github.com/cockroachdb/pebble"
@@ -54,3 +55,26 @@ func BenchmarkPebbleDB(b *testing.B) {
5455
}
5556
})
5657
}
58+
59+
func TestPebbleLogData(t *testing.T) {
60+
db, err := pebble.Open("", &pebble.Options{
61+
FS: vfs.NewMem(),
62+
})
63+
if err != nil {
64+
t.Fatal(err)
65+
}
66+
67+
_, _, err = db.Get(nil)
68+
if !errors.Is(err, pebble.ErrNotFound) {
69+
t.Fatal("Unknown database entry")
70+
}
71+
72+
b := db.NewBatch()
73+
b.LogData(nil, nil)
74+
db.Apply(b, pebble.Sync)
75+
76+
_, _, err = db.Get(nil)
77+
if !errors.Is(err, pebble.ErrNotFound) {
78+
t.Fatal("Unknown database entry")
79+
}
80+
}

triedb/pathdb/database.go

+9
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,15 @@ func (db *Database) Recover(root common.Hash) error {
454454
db.tree.reset(dl)
455455
}
456456
rawdb.DeleteTrieJournal(db.diskdb)
457+
458+
// Explicitly sync the key-value store to ensure all recent writes are
459+
// flushed to disk. This step is crucial to prevent a scenario where
460+
// recent key-value writes are lost due to an application panic, while
461+
// the associated state histories have already been removed, resulting
462+
// in the inability to perform a state rollback.
463+
if err := db.diskdb.Sync(); err != nil {
464+
return err
465+
}
457466
_, err := truncateFromHead(db.diskdb, db.freezer, dl.stateID())
458467
if err != nil {
459468
return err

0 commit comments

Comments
 (0)