Skip to content

Commit f7661a6

Browse files
core/rawdb: find smallest block stored in key-value store when chain gapped (ethereum#26719)
This change prints out more information about the problem, in the case where geth detects a gap between leveldb and ancients, so we can determine more exactly where the gap is (what the first missing is). Also prints out more metadata. --------- Co-authored-by: Martin Holst Swende <[email protected]>
1 parent bb4ac2d commit f7661a6

File tree

2 files changed

+59
-27
lines changed

2 files changed

+59
-27
lines changed

cmd/geth/dbcmd.go

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -694,41 +694,19 @@ func showMetaData(ctx *cli.Context) error {
694694
if err != nil {
695695
fmt.Fprintf(os.Stderr, "Error accessing ancients: %v", err)
696696
}
697-
pp := func(val *uint64) string {
698-
if val == nil {
699-
return "<nil>"
700-
}
701-
return fmt.Sprintf("%d (%#x)", *val, *val)
702-
}
703-
data := [][]string{
704-
{"databaseVersion", pp(rawdb.ReadDatabaseVersion(db))},
705-
{"headBlockHash", fmt.Sprintf("%v", rawdb.ReadHeadBlockHash(db))},
706-
{"headFastBlockHash", fmt.Sprintf("%v", rawdb.ReadHeadFastBlockHash(db))},
707-
{"headHeaderHash", fmt.Sprintf("%v", rawdb.ReadHeadHeaderHash(db))}}
697+
data := rawdb.ReadChainMetadata(db)
698+
data = append(data, []string{"frozen", fmt.Sprintf("%d items", ancients)})
699+
data = append(data, []string{"snapshotGenerator", snapshot.ParseGeneratorStatus(rawdb.ReadSnapshotGenerator(db))})
708700
if b := rawdb.ReadHeadBlock(db); b != nil {
709701
data = append(data, []string{"headBlock.Hash", fmt.Sprintf("%v", b.Hash())})
710702
data = append(data, []string{"headBlock.Root", fmt.Sprintf("%v", b.Root())})
711703
data = append(data, []string{"headBlock.Number", fmt.Sprintf("%d (%#x)", b.Number(), b.Number())})
712704
}
713-
if b := rawdb.ReadSkeletonSyncStatus(db); b != nil {
714-
data = append(data, []string{"SkeletonSyncStatus", string(b)})
715-
}
716705
if h := rawdb.ReadHeadHeader(db); h != nil {
717706
data = append(data, []string{"headHeader.Hash", fmt.Sprintf("%v", h.Hash())})
718707
data = append(data, []string{"headHeader.Root", fmt.Sprintf("%v", h.Root)})
719708
data = append(data, []string{"headHeader.Number", fmt.Sprintf("%d (%#x)", h.Number, h.Number)})
720709
}
721-
data = append(data, [][]string{{"frozen", fmt.Sprintf("%d items", ancients)},
722-
{"lastPivotNumber", pp(rawdb.ReadLastPivotNumber(db))},
723-
{"len(snapshotSyncStatus)", fmt.Sprintf("%d bytes", len(rawdb.ReadSnapshotSyncStatus(db)))},
724-
{"snapshotGenerator", snapshot.ParseGeneratorStatus(rawdb.ReadSnapshotGenerator(db))},
725-
{"snapshotDisabled", fmt.Sprintf("%v", rawdb.ReadSnapshotDisabled(db))},
726-
{"snapshotJournal", fmt.Sprintf("%d bytes", len(rawdb.ReadSnapshotJournal(db)))},
727-
{"snapshotRecoveryNumber", pp(rawdb.ReadSnapshotRecoveryNumber(db))},
728-
{"snapshotRoot", fmt.Sprintf("%v", rawdb.ReadSnapshotRoot(db))},
729-
{"txIndexTail", pp(rawdb.ReadTxIndexTail(db))},
730-
{"fastTxLookupLimit", pp(rawdb.ReadFastTxLookupLimit(db))},
731-
}...)
732710
table := tablewriter.NewWriter(os.Stdout)
733711
table.SetHeader([]string{"Field", "Value"})
734712
table.AppendBulk(data)

core/rawdb/database.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
202202
// Create the idle freezer instance
203203
frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly)
204204
if err != nil {
205+
printChainMetadata(db)
205206
return nil, err
206207
}
207208
// Since the freezer can be stored separately from the user's key-value database,
@@ -233,17 +234,30 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
233234
// the freezer and the key-value store.
234235
frgenesis, err := frdb.Ancient(ChainFreezerHashTable, 0)
235236
if err != nil {
237+
printChainMetadata(db)
236238
return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
237239
} else if !bytes.Equal(kvgenesis, frgenesis) {
240+
printChainMetadata(db)
238241
return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis)
239242
}
240243
// Key-value store and freezer belong to the same network. Ensure that they
241244
// are contiguous, otherwise we might end up with a non-functional freezer.
242245
if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 {
243246
// Subsequent header after the freezer limit is missing from the database.
244247
// Reject startup if the database has a more recent head.
245-
if ldbNum := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); ldbNum > frozen-1 {
246-
return nil, fmt.Errorf("gap in the chain between ancients (#%d) and leveldb (#%d) ", frozen, ldbNum)
248+
if head := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); head > frozen-1 {
249+
// Find the smallest block stored in the key-value store
250+
// in range of [frozen, head]
251+
var number uint64
252+
for number = frozen; number <= head; number++ {
253+
if present, _ := db.Has(headerHashKey(number)); present {
254+
break
255+
}
256+
}
257+
// We are about to exit on error. Print database metdata beore exiting
258+
printChainMetadata(db)
259+
return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ",
260+
frozen-1, number, head)
247261
}
248262
// Database contains only older data than the freezer, this happens if the
249263
// state was wiped and reinited from an existing freezer.
@@ -260,6 +274,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
260274
// Key-value store contains more data than the genesis block, make sure we
261275
// didn't freeze anything yet.
262276
if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 {
277+
printChainMetadata(db)
263278
return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
264279
}
265280
// Block #1 is still in the database, we're allowed to init a new freezer
@@ -581,3 +596,42 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
581596
}
582597
return nil
583598
}
599+
600+
// printChainMetadata prints out chain metadata to stderr.
601+
func printChainMetadata(db ethdb.KeyValueStore) {
602+
fmt.Fprintf(os.Stderr, "Chain metadata\n")
603+
for _, v := range ReadChainMetadata(db) {
604+
fmt.Fprintf(os.Stderr, " %s\n", strings.Join(v, ": "))
605+
}
606+
fmt.Fprintf(os.Stderr, "\n\n")
607+
}
608+
609+
// ReadChainMetadata returns a set of key/value pairs that contains informatin
610+
// about the database chain status. This can be used for diagnostic purposes
611+
// when investigating the state of the node.
612+
func ReadChainMetadata(db ethdb.KeyValueStore) [][]string {
613+
pp := func(val *uint64) string {
614+
if val == nil {
615+
return "<nil>"
616+
}
617+
return fmt.Sprintf("%d (%#x)", *val, *val)
618+
}
619+
data := [][]string{
620+
{"databaseVersion", pp(ReadDatabaseVersion(db))},
621+
{"headBlockHash", fmt.Sprintf("%v", ReadHeadBlockHash(db))},
622+
{"headFastBlockHash", fmt.Sprintf("%v", ReadHeadFastBlockHash(db))},
623+
{"headHeaderHash", fmt.Sprintf("%v", ReadHeadHeaderHash(db))},
624+
{"lastPivotNumber", pp(ReadLastPivotNumber(db))},
625+
{"len(snapshotSyncStatus)", fmt.Sprintf("%d bytes", len(ReadSnapshotSyncStatus(db)))},
626+
{"snapshotDisabled", fmt.Sprintf("%v", ReadSnapshotDisabled(db))},
627+
{"snapshotJournal", fmt.Sprintf("%d bytes", len(ReadSnapshotJournal(db)))},
628+
{"snapshotRecoveryNumber", pp(ReadSnapshotRecoveryNumber(db))},
629+
{"snapshotRoot", fmt.Sprintf("%v", ReadSnapshotRoot(db))},
630+
{"txIndexTail", pp(ReadTxIndexTail(db))},
631+
{"fastTxLookupLimit", pp(ReadFastTxLookupLimit(db))},
632+
}
633+
if b := ReadSkeletonSyncStatus(db); b != nil {
634+
data = append(data, []string{"SkeletonSyncStatus", string(b)})
635+
}
636+
return data
637+
}

0 commit comments

Comments
 (0)