core: improve snapshot journal recovery (#21594)
* core/state/snapshot: introduce snapshot journal version * core: update the disk layer in an atomic way * core: persist the disk layer generator periodically * core/state/snapshot: improve logging * core/state/snapshot: forcibly ensure the legacy snapshot is matched * core/state/snapshot: add debug logs * core, tests: fix tests and special recovery case * core: polish * core: add more blockchain tests for snapshot recovery * core/state: fix comment * core: add recovery flag for snapshot * core: add restart after start-after-crash tests * core/rawdb: fix imports * core: fix tests * core: remove log * core/state/snapshot: fix snapshot * core: avoid callbacks in SetHead * core: fix setHead cornercase where the threshold root has state * core: small docs for the test cases Co-authored-by: Péter Szilágyi <peterke@gmail.com>
This commit is contained in:
@ -28,6 +28,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/leveldb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// reverse reverses the contents of a byte slice. It's used to update random accs
|
||||
@ -429,6 +430,81 @@ func TestDiskPartialMerge(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that when the bottom-most diff layer is merged into the disk
|
||||
// layer whether the corresponding generator is persisted correctly.
|
||||
func TestDiskGeneratorPersistence(t *testing.T) {
|
||||
var (
|
||||
accOne = randomHash()
|
||||
accTwo = randomHash()
|
||||
accOneSlotOne = randomHash()
|
||||
accOneSlotTwo = randomHash()
|
||||
|
||||
accThree = randomHash()
|
||||
accThreeSlot = randomHash()
|
||||
baseRoot = randomHash()
|
||||
diffRoot = randomHash()
|
||||
diffTwoRoot = randomHash()
|
||||
genMarker = append(randomHash().Bytes(), randomHash().Bytes()...)
|
||||
)
|
||||
// Testing scenario 1, the disk layer is still under the construction.
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
|
||||
rawdb.WriteAccountSnapshot(db, accOne, accOne[:])
|
||||
rawdb.WriteStorageSnapshot(db, accOne, accOneSlotOne, accOneSlotOne[:])
|
||||
rawdb.WriteStorageSnapshot(db, accOne, accOneSlotTwo, accOneSlotTwo[:])
|
||||
rawdb.WriteSnapshotRoot(db, baseRoot)
|
||||
|
||||
// Create a disk layer based on all above updates
|
||||
snaps := &Tree{
|
||||
layers: map[common.Hash]snapshot{
|
||||
baseRoot: &diskLayer{
|
||||
diskdb: db,
|
||||
cache: fastcache.New(500 * 1024),
|
||||
root: baseRoot,
|
||||
genMarker: genMarker,
|
||||
},
|
||||
},
|
||||
}
|
||||
// Modify or delete some accounts, flatten everything onto disk
|
||||
if err := snaps.Update(diffRoot, baseRoot, nil, map[common.Hash][]byte{
|
||||
accTwo: accTwo[:],
|
||||
}, nil); err != nil {
|
||||
t.Fatalf("failed to update snapshot tree: %v", err)
|
||||
}
|
||||
if err := snaps.Cap(diffRoot, 0); err != nil {
|
||||
t.Fatalf("failed to flatten snapshot tree: %v", err)
|
||||
}
|
||||
blob := rawdb.ReadSnapshotGenerator(db)
|
||||
var generator journalGenerator
|
||||
if err := rlp.DecodeBytes(blob, &generator); err != nil {
|
||||
t.Fatalf("Failed to decode snapshot generator %v", err)
|
||||
}
|
||||
if !bytes.Equal(generator.Marker, genMarker) {
|
||||
t.Fatalf("Generator marker is not matched")
|
||||
}
|
||||
// Test senario 2, the disk layer is fully generated
|
||||
// Modify or delete some accounts, flatten everything onto disk
|
||||
if err := snaps.Update(diffTwoRoot, diffRoot, nil, map[common.Hash][]byte{
|
||||
accThree: accThree.Bytes(),
|
||||
}, map[common.Hash]map[common.Hash][]byte{
|
||||
accThree: {accThreeSlot: accThreeSlot.Bytes()},
|
||||
}); err != nil {
|
||||
t.Fatalf("failed to update snapshot tree: %v", err)
|
||||
}
|
||||
diskLayer := snaps.layers[snaps.diskRoot()].(*diskLayer)
|
||||
diskLayer.genMarker = nil // Construction finished
|
||||
if err := snaps.Cap(diffTwoRoot, 0); err != nil {
|
||||
t.Fatalf("failed to flatten snapshot tree: %v", err)
|
||||
}
|
||||
blob = rawdb.ReadSnapshotGenerator(db)
|
||||
if err := rlp.DecodeBytes(blob, &generator); err != nil {
|
||||
t.Fatalf("Failed to decode snapshot generator %v", err)
|
||||
}
|
||||
if len(generator.Marker) != 0 {
|
||||
t.Fatalf("Failed to update snapshot generator")
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that merging something into a disk layer persists it into the database
|
||||
// and invalidates any previously written and cached values, discarding anything
|
||||
// after the in-progress generation marker.
|
||||
|
Reference in New Issue
Block a user