Skip to content

Commit a62207c

Browse files
rjl493456442buddh0
authored andcommitted
core, ethdb: introduce database sync function (ethereum#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 - ethereum#31405 - ethereum#29819
1 parent 52780d4 commit a62207c

26 files changed

+201
-66
lines changed

cmd/geth/dbcmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@ func hbss2pbss(ctx *cli.Context) error {
12821282
defer stack.Close()
12831283

12841284
db := utils.MakeChainDatabase(ctx, stack, false, false)
1285-
db.BlockStore().Sync()
1285+
db.BlockStore().SyncAncient()
12861286
stateDiskDb := db.StateStore()
12871287
defer db.Close()
12881288

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
}
@@ -304,7 +304,7 @@ func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uin
304304
func benchWriteChain(b *testing.B, full bool, count uint64) {
305305
genesis := &Genesis{Config: params.AllEthashProtocolChanges}
306306
for i := 0; i < b.N; i++ {
307-
pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false, true)
307+
pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false)
308308
if err != nil {
309309
b.Fatalf("error opening database: %v", err)
310310
}
@@ -317,7 +317,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
317317
func benchReadChain(b *testing.B, full bool, count uint64) {
318318
dir := b.TempDir()
319319

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

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

core/blockchain.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,17 +1119,16 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
11191119
// Ignore the error here since light client won't hit this path
11201120
frozen, _ := bc.db.BlockStore().Ancients()
11211121
if num+1 <= frozen {
1122-
// Truncate all relative data(header, total difficulty, body, receipt
1123-
// and canonical hash) from ancient store.
1124-
if _, err := bc.db.BlockStore().TruncateHead(num); err != nil {
1125-
log.Crit("Failed to truncate ancient data", "number", num, "err", err)
1126-
}
1127-
// Remove the hash <-> number mapping from the active store.
1128-
rawdb.DeleteHeaderNumber(db, hash)
1122+
// The chain segment, such as the block header, canonical hash,
1123+
// body, and receipt, will be removed from the ancient store
1124+
// in one go.
1125+
//
1126+
// The hash-to-number mapping in the key-value store will be
1127+
// removed by the hc.SetHead function.
11291128
} else {
1130-
// Remove relative body and receipts from the active store.
1131-
// The header, total difficulty and canonical hash will be
1132-
// removed in the hc.SetHead function.
1129+
// Remove the associated body and receipts from the key-value store.
1130+
// The header, hash-to-number mapping, and canonical hash will be
1131+
// removed by the hc.SetHead function.
11331132
rawdb.DeleteBody(db, hash, num)
11341133
rawdb.DeleteBlobSidecars(db, hash, num)
11351134
rawdb.DeleteReceipts(db, hash, num)
@@ -1599,7 +1598,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
15991598
size += writeSize
16001599

16011600
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
1602-
if err := bc.db.BlockStore().Sync(); err != nil {
1601+
if err := bc.db.BlockStore().SyncAncient(); err != nil {
16031602
return 0, err
16041603
}
16051604
// Update the current snap block because all block data is now present in DB.

core/blockchain_repair_test.go

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

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

18631863
// Start a new blockchain back up and see where the repair leads us
1864-
pdb, err = pebble.New(datadir, 0, 0, "", false, true)
1864+
pdb, err = pebble.New(datadir, 0, 0, "", false)
18651865
if err != nil {
18661866
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
18671867
}
@@ -1926,7 +1926,7 @@ func testIssue23496(t *testing.T, scheme string) {
19261926
datadir := t.TempDir()
19271927
ancient := filepath.Join(datadir, "ancient")
19281928

1929-
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
1929+
pdb, err := pebble.New(datadir, 0, 0, "", false)
19301930
if err != nil {
19311931
t.Fatalf("Failed to create persistent key-value database: %v", err)
19321932
}
@@ -1984,7 +1984,7 @@ func testIssue23496(t *testing.T, scheme string) {
19841984
chain.stopWithoutSaving()
19851985

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

core/blockchain_sethead_test.go

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

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

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
@@ -2670,7 +2670,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
26702670
datadir := t.TempDir()
26712671
ancient := path.Join(datadir, "ancient")
26722672

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

core/headerchain.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,18 +695,51 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
695695
hashes = append(hashes, hdr.Hash())
696696
}
697697
for _, hash := range hashes {
698+
// Remove the associated block body and receipts if required.
699+
//
700+
// If the block is in the chain freezer, then this delete operation
701+
// is actually ineffective.
698702
if delFn != nil {
699703
delFn(blockBatch, hash, num)
700704
}
705+
// Remove the hash->number mapping along with the header itself
701706
rawdb.DeleteHeader(blockBatch, hash, num)
702707
rawdb.DeleteTd(blockBatch, hash, num)
703708
}
709+
// Remove the number->hash mapping
704710
rawdb.DeleteCanonicalHash(blockBatch, num)
705711
}
706712
}
707713
// Flush all accumulated deletions.
708714
if err := blockBatch.Write(); err != nil {
709-
log.Crit("Failed to rewind block", "error", err)
715+
log.Crit("Failed to commit batch in setHead", "err", err)
716+
}
717+
// Explicitly flush the pending writes in the key-value store to disk, ensuring
718+
// data durability of the previous deletions.
719+
if err := hc.chainDb.SyncKeyValue(); err != nil {
720+
log.Crit("Failed to sync the key-value store in setHead", "err", err)
721+
}
722+
// Truncate the excessive chain segments in the ancient store.
723+
// These are actually deferred deletions from the loop above.
724+
//
725+
// This step must be performed after synchronizing the key-value store;
726+
// otherwise, in the event of a panic, it's theoretically possible to
727+
// lose recent key-value store writes while the ancient store deletions
728+
// remain, leading to data inconsistency, e.g., the gap between the key
729+
// value store and ancient can be created due to unclean shutdown.
730+
if delFn != nil {
731+
// Ignore the error here since light client won't hit this path
732+
frozen, _ := hc.chainDb.Ancients()
733+
header := hc.CurrentHeader()
734+
735+
// Truncate the excessive chain segment above the current chain head
736+
// in the ancient store.
737+
if header.Number.Uint64()+1 < frozen {
738+
_, err := hc.chainDb.BlockStore().TruncateHead(header.Number.Uint64() + 1)
739+
if err != nil {
740+
log.Crit("Failed to truncate head block", "err", err)
741+
}
742+
}
710743
}
711744
// Clear out any stale content from the caches
712745
hc.headerCache.Purge()

core/rawdb/chain_freezer.go

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

core/rawdb/database.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ func (db *nofreezedb) ResetTable(kind string, startAt uint64, onlyEmpty bool) er
235235
return errNotSupported
236236
}
237237

238-
// Sync returns an error as we don't have a backing chain freezer.
239-
func (db *nofreezedb) Sync() error {
238+
// SyncAncient returns an error as we don't have a backing chain freezer.
239+
func (db *nofreezedb) SyncAncient() error {
240240
return errNotSupported
241241
}
242242

@@ -391,8 +391,8 @@ func (db *emptyfreezedb) ResetTable(kind string, startAt uint64, onlyEmpty bool)
391391
return nil
392392
}
393393

394-
// Sync returns nil for pruned db that we don't have a backing chain freezer.
395-
func (db *emptyfreezedb) Sync() error {
394+
// SyncAncient returns nil for pruned db that we don't have a backing chain freezer.
395+
func (db *emptyfreezedb) SyncAncient() error {
396396
return nil
397397
}
398398

0 commit comments

Comments
 (0)