Skip to content

Commit 064115c

Browse files
committed
Fix DB manager downgrade
1 parent 03fece6 commit 064115c

File tree

2 files changed

+72
-40
lines changed

2 files changed

+72
-40
lines changed

beacon_node/beacon_chain/src/schema_change/migration_schema_v24.rs

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ use crate::{
33
summaries_dag::{DAGStateSummary, DAGStateSummaryV22, StateSummariesDAG},
44
};
55
use ssz::{Decode, DecodeError, Encode};
6-
use ssz_derive::{Decode, Encode};
6+
use ssz_derive::Encode;
77
use std::{
88
sync::Arc,
99
time::{Duration, Instant},
1010
};
1111
use store::{
12-
hdiff::StorageStrategy, hot_cold_store::OptionalDiffBaseState, DBColumn, Error, HotColdDB,
13-
HotStateSummary, KeyValueStore, KeyValueStoreOp, StoreItem,
12+
hdiff::StorageStrategy,
13+
hot_cold_store::{HotStateSummaryV22, OptionalDiffBaseState},
14+
DBColumn, Error, HotColdDB, HotStateSummary, KeyValueStore, KeyValueStoreOp, StoreItem,
1415
};
1516
use tracing::{debug, info, warn};
1617
use types::{
@@ -158,27 +159,6 @@ impl StoreItem for PruningCheckpoint {
158159
}
159160
}
160161

161-
#[derive(Debug, Clone, Copy, Encode, Decode)]
162-
pub struct HotStateSummaryV22 {
163-
slot: Slot,
164-
latest_block_root: Hash256,
165-
epoch_boundary_state_root: Hash256,
166-
}
167-
168-
impl StoreItem for HotStateSummaryV22 {
169-
fn db_column() -> DBColumn {
170-
DBColumn::BeaconStateSummary
171-
}
172-
173-
fn as_store_bytes(&self) -> Vec<u8> {
174-
self.as_ssz_bytes()
175-
}
176-
177-
fn from_store_bytes(bytes: &[u8]) -> Result<Self, Error> {
178-
Ok(Self::from_ssz_bytes(bytes)?)
179-
}
180-
}
181-
182162
pub fn upgrade_to_v24<T: BeaconChainTypes>(
183163
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
184164
) -> Result<Vec<KeyValueStoreOp>, Error> {

beacon_node/store/src/hot_cold_store.rs

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -289,15 +289,26 @@ impl<E: EthSpec> HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>> {
289289
// stop and restart correctly. This needs to occur *before* running any migrations
290290
// because some migrations load states and depend on the split.
291291
//
292-
// V24: `load_split` needs to load a hot state summary, which need to be migrated from V22
293-
// to V24. Attempting to `load_split` here before the migration will trigger an SSZ decode
294-
// error. Instead we load the partial split, and load the full split after the migration.
295-
if let Some(split) = db.load_split_partial()? {
292+
// We use a method that is ambivalent to the state summaries being V22 or V24, because
293+
// we need to support several scenarios:
294+
//
295+
// - Migrating from V22 to V24: initially summaries are V22 , and we need
296+
// to be able to load a block root from them. Loading the split partially at first
297+
// (without reading a V24 summary) and then completing the full load after the migration
298+
// runs is possible in this case, but not in the next case.
299+
// - Migrating from V24 to V22: initially summaries are V24, but after the migration runs
300+
// they will be V22. If we used the "load full split after migration" approach with strict
301+
// V24 summaries, it would break when trying to read V22 summaries after the migration.
302+
//
303+
// Therefore we take the most flexible approach of reading _either_ a V22 or V24 summary and
304+
// using this to load the split correctly the first time.
305+
if let Some(split) = db.load_split()? {
296306
*db.split.write() = split;
297307

298308
info!(
299309
%split.slot,
300-
split_state = ?split.state_root,
310+
?split.state_root,
311+
?split.block_root,
301312
"Hot-Cold DB initialized"
302313
);
303314
}
@@ -395,14 +406,6 @@ impl<E: EthSpec> HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>> {
395406
db.store_schema_version(CURRENT_SCHEMA_VERSION)?;
396407
}
397408

398-
// Load the full split after the migration to set the `split.block_root` to the correct
399-
// value.
400-
if let Some(split) = db.load_split()? {
401-
*db.split.write() = split;
402-
403-
debug!(?split, "Hot-Cold DB split initialized");
404-
}
405-
406409
// TODO(tree-states): Here we can choose to prune advanced states to reclaim disk space. As
407410
// it's a foreground task there's no risk of race condition that can corrupt the DB.
408411
// Advanced states for invalid blocks that were never written to the DB, or descendants of
@@ -2764,14 +2767,14 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
27642767
Some(mut split) => {
27652768
debug!(?split, "Loaded split partial");
27662769
// Load the hot state summary to get the block root.
2767-
let summary = self
2768-
.load_hot_state_summary(&split.state_root)
2770+
let latest_block_root = self
2771+
.load_block_root_from_summary_any_version(&split.state_root)
27692772
.map_err(|e| Error::LoadHotStateSummaryForSplit(e.into()))?
27702773
.ok_or(HotColdDBError::MissingSplitState(
27712774
split.state_root,
27722775
split.slot,
27732776
))?;
2774-
split.block_root = summary.latest_block_root;
2777+
split.block_root = latest_block_root;
27752778
Ok(Some(split))
27762779
}
27772780
None => Ok(None),
@@ -2801,6 +2804,31 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
28012804
.map_err(|e| Error::LoadHotStateSummary(*state_root, e.into()))
28022805
}
28032806

2807+
/// Load a hot state's summary in V22 format, given its root.
2808+
pub fn load_hot_state_summary_v22(
2809+
&self,
2810+
state_root: &Hash256,
2811+
) -> Result<Option<HotStateSummaryV22>, Error> {
2812+
self.hot_db
2813+
.get(state_root)
2814+
.map_err(|e| Error::LoadHotStateSummary(*state_root, e.into()))
2815+
}
2816+
2817+
/// Load the latest block root for a hot state summary either in modern form, or V22 form.
2818+
///
2819+
/// This function is required to open a V22 database for migration to V24, or vice versa.
2820+
pub fn load_block_root_from_summary_any_version(
2821+
&self,
2822+
state_root: &Hash256,
2823+
) -> Result<Option<Hash256>, Error> {
2824+
self.load_hot_state_summary(state_root)
2825+
.map(|opt_summary| opt_summary.map(|summary| summary.latest_block_root))
2826+
.or_else(|_| {
2827+
self.load_hot_state_summary_v22(state_root)
2828+
.map(|opt_summary| opt_summary.map(|summary| summary.latest_block_root))
2829+
})
2830+
}
2831+
28042832
/// Load all hot state summaries present in the hot DB
28052833
pub fn load_hot_state_summaries(&self) -> Result<Vec<(Hash256, HotStateSummary)>, Error> {
28062834
self.hot_db
@@ -3689,6 +3717,30 @@ impl HotStateSummary {
36893717
}
36903718
}
36913719

3720+
/// Legacy hot state summary used in schema V22 and before.
3721+
///
3722+
/// This can be deleted when we remove V22 support.
3723+
#[derive(Debug, Clone, Copy, Encode, Decode)]
3724+
pub struct HotStateSummaryV22 {
3725+
pub slot: Slot,
3726+
pub latest_block_root: Hash256,
3727+
pub epoch_boundary_state_root: Hash256,
3728+
}
3729+
3730+
impl StoreItem for HotStateSummaryV22 {
3731+
fn db_column() -> DBColumn {
3732+
DBColumn::BeaconStateSummary
3733+
}
3734+
3735+
fn as_store_bytes(&self) -> Vec<u8> {
3736+
self.as_ssz_bytes()
3737+
}
3738+
3739+
fn from_store_bytes(bytes: &[u8]) -> Result<Self, Error> {
3740+
Ok(Self::from_ssz_bytes(bytes)?)
3741+
}
3742+
}
3743+
36923744
/// Struct for summarising a state in the freezer database.
36933745
#[derive(Debug, Clone, Copy, Default, Encode, Decode)]
36943746
pub(crate) struct ColdStateSummary {

0 commit comments

Comments
 (0)