Skip to content

Commit 1051976

Browse files
authored
core, ethdb: introduce database sync function (#31703)
This pull request introduces a SyncKeyValue function to the ethdb.KeyValueStore interface, providing the ability to forcibly flush all previous writes to disk. This functionality is critical for go-ethereum, which internally uses two independent database engines: a key-value store (such as Pebble, LevelDB, or memoryDB for testing) and a flat-file–based freezer. To ensure write-order consistency between these engines, the key-value store must be explicitly synced before writing to the freezer and vice versa. Fixes - #31405 - #29819
1 parent 07d073b commit 1051976

24 files changed

+194
-60
lines changed

core/bench_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
183183
if !disk {
184184
db = rawdb.NewMemoryDatabase()
185185
} else {
186-
pdb, err := pebble.New(b.TempDir(), 128, 128, "", false, true)
186+
pdb, err := pebble.New(b.TempDir(), 128, 128, "", false)
187187
if err != nil {
188188
b.Fatalf("cannot create temporary database: %v", err)
189189
}
@@ -303,7 +303,7 @@ func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uin
303303
func benchWriteChain(b *testing.B, full bool, count uint64) {
304304
genesis := &Genesis{Config: params.AllEthashProtocolChanges}
305305
for i := 0; i < b.N; i++ {
306-
pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false, true)
306+
pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false)
307307
if err != nil {
308308
b.Fatalf("error opening database: %v", err)
309309
}
@@ -316,7 +316,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
316316
func benchReadChain(b *testing.B, full bool, count uint64) {
317317
dir := b.TempDir()
318318

319-
pdb, err := pebble.New(dir, 1024, 128, "", false, true)
319+
pdb, err := pebble.New(dir, 1024, 128, "", false)
320320
if err != nil {
321321
b.Fatalf("error opening database: %v", err)
322322
}
@@ -332,7 +332,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
332332
b.ResetTimer()
333333

334334
for i := 0; i < b.N; i++ {
335-
pdb, err = pebble.New(dir, 1024, 128, "", false, true)
335+
pdb, err = pebble.New(dir, 1024, 128, "", false)
336336
if err != nil {
337337
b.Fatalf("error opening database: %v", err)
338338
}

core/blockchain.go

Lines changed: 12 additions & 12 deletions
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
}
@@ -1361,7 +1360,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
13611360
size += writeSize
13621361

13631362
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
1364-
if err := bc.db.Sync(); err != nil {
1363+
if err := bc.db.SyncAncient(); err != nil {
13651364
return 0, err
13661365
}
13671366
// Write hash to number mappings
@@ -2627,7 +2626,8 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
26272626
if err != nil {
26282627
return 0, err
26292628
}
2630-
if err := bc.db.Sync(); err != nil {
2629+
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
2630+
if err := bc.db.SyncAncient(); err != nil {
26312631
return 0, err
26322632
}
26332633
// Write hash to number mappings

core/blockchain_repair_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,7 +1765,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
17651765
datadir := t.TempDir()
17661766
ancient := filepath.Join(datadir, "ancient")
17671767

1768-
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
1768+
pdb, err := pebble.New(datadir, 0, 0, "", false)
17691769
if err != nil {
17701770
t.Fatalf("Failed to create persistent key-value database: %v", err)
17711771
}
@@ -1850,7 +1850,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
18501850
chain.stopWithoutSaving()
18511851

18521852
// Start a new blockchain back up and see where the repair leads us
1853-
pdb, err = pebble.New(datadir, 0, 0, "", false, true)
1853+
pdb, err = pebble.New(datadir, 0, 0, "", false)
18541854
if err != nil {
18551855
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
18561856
}
@@ -1915,7 +1915,7 @@ func testIssue23496(t *testing.T, scheme string) {
19151915
datadir := t.TempDir()
19161916
ancient := filepath.Join(datadir, "ancient")
19171917

1918-
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
1918+
pdb, err := pebble.New(datadir, 0, 0, "", false)
19191919
if err != nil {
19201920
t.Fatalf("Failed to create persistent key-value database: %v", err)
19211921
}
@@ -1973,7 +1973,7 @@ func testIssue23496(t *testing.T, scheme string) {
19731973
chain.stopWithoutSaving()
19741974

19751975
// Start a new blockchain back up and see where the repair leads us
1976-
pdb, err = pebble.New(datadir, 0, 0, "", false, true)
1976+
pdb, err = pebble.New(datadir, 0, 0, "", false)
19771977
if err != nil {
19781978
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
19791979
}

core/blockchain_sethead_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1969,7 +1969,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
19691969
datadir := t.TempDir()
19701970
ancient := filepath.Join(datadir, "ancient")
19711971

1972-
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
1972+
pdb, err := pebble.New(datadir, 0, 0, "", false)
19731973
if err != nil {
19741974
t.Fatalf("Failed to create persistent key-value database: %v", err)
19751975
}

core/blockchain_snapshot_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
6666
datadir := t.TempDir()
6767
ancient := filepath.Join(datadir, "ancient")
6868

69-
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
69+
pdb, err := pebble.New(datadir, 0, 0, "", false)
7070
if err != nil {
7171
t.Fatalf("Failed to create persistent key-value database: %v", err)
7272
}
@@ -257,7 +257,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
257257
chain.triedb.Close()
258258

259259
// Start a new blockchain back up and see where the repair leads us
260-
pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false, true)
260+
pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false)
261261
if err != nil {
262262
t.Fatalf("Failed to create persistent key-value database: %v", err)
263263
}

core/blockchain_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2492,7 +2492,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
24922492
datadir := t.TempDir()
24932493
ancient := path.Join(datadir, "ancient")
24942494

2495-
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
2495+
pdb, err := pebble.New(datadir, 0, 0, "", false)
24962496
if err != nil {
24972497
t.Fatalf("Failed to create persistent key-value database: %v", err)
24982498
}

core/headerchain.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,17 +591,50 @@ 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.SyncKeyValue(); 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, e.g., the gap between the key
624+
// value store and ancient can be created due to unclean shutdown.
625+
if delFn != nil {
626+
// Ignore the error here since light client won't hit this path
627+
frozen, _ := hc.chainDb.Ancients()
628+
header := hc.CurrentHeader()
629+
630+
// Truncate the excessive chain segment above the current chain head
631+
// in the ancient store.
632+
if header.Number.Uint64()+1 < frozen {
633+
_, err := hc.chainDb.TruncateHead(header.Number.Uint64() + 1)
634+
if err != nil {
635+
log.Crit("Failed to truncate head block", "err", err)
636+
}
637+
}
605638
}
606639
// Clear out any stale content from the caches
607640
hc.headerCache.Purge()

core/rawdb/chain_freezer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
205205
continue
206206
}
207207
// Batch of blocks have been frozen, flush them before wiping from key-value store
208-
if err := f.Sync(); err != nil {
208+
if err := f.SyncAncient(); err != nil {
209209
log.Crit("Failed to flush frozen tables", "err", err)
210210
}
211211
// Wipe out all data from the active database

core/rawdb/database.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) {
131131
return 0, errNotSupported
132132
}
133133

134-
// Sync returns an error as we don't have a backing chain freezer.
135-
func (db *nofreezedb) Sync() error {
134+
// SyncAncient returns an error as we don't have a backing chain freezer.
135+
func (db *nofreezedb) SyncAncient() error {
136136
return errNotSupported
137137
}
138138

core/rawdb/freezer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,8 @@ func (f *Freezer) TruncateTail(tail uint64) (uint64, error) {
325325
return old, nil
326326
}
327327

328-
// Sync flushes all data tables to disk.
329-
func (f *Freezer) Sync() error {
328+
// SyncAncient flushes all data tables to disk.
329+
func (f *Freezer) SyncAncient() error {
330330
var errs []error
331331
for _, table := range f.tables {
332332
if err := table.Sync(); err != nil {

0 commit comments

Comments
 (0)