core/rawdb, ethdb: introduce batched/atomic reads from ancients (#23566)
This PR adds a new accessor method to the freezer database. This new view offers a consistent interface, guaranteeing that all individual tables (headers, bodies etc) are all on the same number, and that this number is not changes (added/truncated) while the operation is performing.
This commit is contained in:
committed by
GitHub
parent
2954f40eac
commit
0e7efd696b
@@ -35,20 +35,15 @@ import (
|
||||
|
||||
// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
|
||||
func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash {
|
||||
data, _ := db.Ancient(freezerHashTable, number)
|
||||
if len(data) == 0 {
|
||||
data, _ = db.Get(headerHashKey(number))
|
||||
// In the background freezer is moving data from leveldb to flatten files.
|
||||
// So during the first check for ancient db, the data is not yet in there,
|
||||
// but when we reach into leveldb, the data was already moved. That would
|
||||
// result in a not found error.
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReader) error {
|
||||
data, _ = reader.Ancient(freezerHashTable, number)
|
||||
if len(data) == 0 {
|
||||
data, _ = db.Ancient(freezerHashTable, number)
|
||||
// Get it by hash from leveldb
|
||||
data, _ = db.Get(headerHashKey(number))
|
||||
}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
@@ -304,32 +299,25 @@ func WriteFastTxLookupLimit(db ethdb.KeyValueWriter, number uint64) {
|
||||
|
||||
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
||||
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
// First try to look up the data in ancient database. Extra hash
|
||||
// comparison is necessary since ancient database only maintains
|
||||
// the canonical data.
|
||||
data, _ := db.Ancient(freezerHeaderTable, number)
|
||||
if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
|
||||
return data
|
||||
}
|
||||
// Then try to look up the data in leveldb.
|
||||
data, _ = db.Get(headerKey(number, hash))
|
||||
if len(data) > 0 {
|
||||
return data
|
||||
}
|
||||
// In the background freezer is moving data from leveldb to flatten files.
|
||||
// So during the first check for ancient db, the data is not yet in there,
|
||||
// but when we reach into leveldb, the data was already moved. That would
|
||||
// result in a not found error.
|
||||
data, _ = db.Ancient(freezerHeaderTable, number)
|
||||
if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
|
||||
return data
|
||||
}
|
||||
return nil // Can't find the data anywhere.
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReader) error {
|
||||
// First try to look up the data in ancient database. Extra hash
|
||||
// comparison is necessary since ancient database only maintains
|
||||
// the canonical data.
|
||||
data, _ = reader.Ancient(freezerHeaderTable, number)
|
||||
if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(headerKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
// HasHeader verifies the existence of a block header corresponding to the hash.
|
||||
func HasHeader(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash {
|
||||
if isCanon(db, number, hash) {
|
||||
return true
|
||||
}
|
||||
if has, err := db.Has(headerKey(number, hash)); !has || err != nil {
|
||||
@@ -389,53 +377,48 @@ func deleteHeaderWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
|
||||
}
|
||||
}
|
||||
|
||||
// isCanon is an internal utility method, to check whether the given number/hash
|
||||
// is part of the ancient (canon) set.
|
||||
func isCanon(reader ethdb.AncientReader, number uint64, hash common.Hash) bool {
|
||||
h, err := reader.Ancient(freezerHashTable, number)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(h, hash[:])
|
||||
}
|
||||
|
||||
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
|
||||
func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
// First try to look up the data in ancient database. Extra hash
|
||||
// comparison is necessary since ancient database only maintains
|
||||
// the canonical data.
|
||||
data, _ := db.Ancient(freezerBodiesTable, number)
|
||||
if len(data) > 0 {
|
||||
h, _ := db.Ancient(freezerHashTable, number)
|
||||
if common.BytesToHash(h) == hash {
|
||||
return data
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReader) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(freezerBodiesTable, number)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Then try to look up the data in leveldb.
|
||||
data, _ = db.Get(blockBodyKey(number, hash))
|
||||
if len(data) > 0 {
|
||||
return data
|
||||
}
|
||||
// In the background freezer is moving data from leveldb to flatten files.
|
||||
// So during the first check for ancient db, the data is not yet in there,
|
||||
// but when we reach into leveldb, the data was already moved. That would
|
||||
// result in a not found error.
|
||||
data, _ = db.Ancient(freezerBodiesTable, number)
|
||||
if len(data) > 0 {
|
||||
h, _ := db.Ancient(freezerHashTable, number)
|
||||
if common.BytesToHash(h) == hash {
|
||||
return data
|
||||
}
|
||||
}
|
||||
return nil // Can't find the data anywhere.
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(blockBodyKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadCanonicalBodyRLP retrieves the block body (transactions and uncles) for the canonical
|
||||
// block at number, in RLP encoding.
|
||||
func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
|
||||
// If it's an ancient one, we don't need the canonical hash
|
||||
data, _ := db.Ancient(freezerBodiesTable, number)
|
||||
if len(data) == 0 {
|
||||
// Need to get the hash
|
||||
data, _ = db.Get(blockBodyKey(number, ReadCanonicalHash(db, number)))
|
||||
// In the background freezer is moving data from leveldb to flatten files.
|
||||
// So during the first check for ancient db, the data is not yet in there,
|
||||
// but when we reach into leveldb, the data was already moved. That would
|
||||
// result in a not found error.
|
||||
if len(data) == 0 {
|
||||
data, _ = db.Ancient(freezerBodiesTable, number)
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReader) error {
|
||||
data, _ = reader.Ancient(freezerBodiesTable, number)
|
||||
if len(data) > 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Get it by hash from leveldb
|
||||
data, _ = db.Get(blockBodyKey(number, ReadCanonicalHash(db, number)))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -448,7 +431,7 @@ func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp
|
||||
|
||||
// HasBody verifies the existence of a block body corresponding to the hash.
|
||||
func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash {
|
||||
if isCanon(db, number, hash) {
|
||||
return true
|
||||
}
|
||||
if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
|
||||
@@ -489,33 +472,18 @@ func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
|
||||
// ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding.
|
||||
func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
// First try to look up the data in ancient database. Extra hash
|
||||
// comparison is necessary since ancient database only maintains
|
||||
// the canonical data.
|
||||
data, _ := db.Ancient(freezerDifficultyTable, number)
|
||||
if len(data) > 0 {
|
||||
h, _ := db.Ancient(freezerHashTable, number)
|
||||
if common.BytesToHash(h) == hash {
|
||||
return data
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReader) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(freezerDifficultyTable, number)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Then try to look up the data in leveldb.
|
||||
data, _ = db.Get(headerTDKey(number, hash))
|
||||
if len(data) > 0 {
|
||||
return data
|
||||
}
|
||||
// In the background freezer is moving data from leveldb to flatten files.
|
||||
// So during the first check for ancient db, the data is not yet in there,
|
||||
// but when we reach into leveldb, the data was already moved. That would
|
||||
// result in a not found error.
|
||||
data, _ = db.Ancient(freezerDifficultyTable, number)
|
||||
if len(data) > 0 {
|
||||
h, _ := db.Ancient(freezerHashTable, number)
|
||||
if common.BytesToHash(h) == hash {
|
||||
return data
|
||||
}
|
||||
}
|
||||
return nil // Can't find the data anywhere.
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(headerTDKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadTd retrieves a block's total difficulty corresponding to the hash.
|
||||
@@ -553,7 +521,7 @@ func DeleteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
// HasReceipts verifies the existence of all the transaction receipts belonging
|
||||
// to a block.
|
||||
func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash {
|
||||
if isCanon(db, number, hash) {
|
||||
return true
|
||||
}
|
||||
if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil {
|
||||
@@ -564,33 +532,18 @@ func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
|
||||
// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
|
||||
func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
// First try to look up the data in ancient database. Extra hash
|
||||
// comparison is necessary since ancient database only maintains
|
||||
// the canonical data.
|
||||
data, _ := db.Ancient(freezerReceiptTable, number)
|
||||
if len(data) > 0 {
|
||||
h, _ := db.Ancient(freezerHashTable, number)
|
||||
if common.BytesToHash(h) == hash {
|
||||
return data
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReader) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(freezerReceiptTable, number)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Then try to look up the data in leveldb.
|
||||
data, _ = db.Get(blockReceiptsKey(number, hash))
|
||||
if len(data) > 0 {
|
||||
return data
|
||||
}
|
||||
// In the background freezer is moving data from leveldb to flatten files.
|
||||
// So during the first check for ancient db, the data is not yet in there,
|
||||
// but when we reach into leveldb, the data was already moved. That would
|
||||
// result in a not found error.
|
||||
data, _ = db.Ancient(freezerReceiptTable, number)
|
||||
if len(data) > 0 {
|
||||
h, _ := db.Ancient(freezerHashTable, number)
|
||||
if common.BytesToHash(h) == hash {
|
||||
return data
|
||||
}
|
||||
}
|
||||
return nil // Can't find the data anywhere.
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(blockReceiptsKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
|
||||
|
Reference in New Issue
Block a user