Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix hot state disk leak #5768

Merged
merged 2 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions beacon_node/beacon_chain/src/block_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1382,18 +1382,20 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
let catchup_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_CATCHUP_STATE);

// Stage a batch of operations to be completed atomically if this block is imported
// successfully. We include the state root of the pre-state, which may be an advanced state
// that was stored in the DB with a `temporary` flag.
// successfully. If there is a skipped slot, we include the state root of the pre-state,
// which may be an advanced state that was stored in the DB with a `temporary` flag.
let mut state = parent.pre_state;

let mut confirmed_state_roots = if state.slot() > parent.beacon_block.slot() {
// Advanced pre-state. Delete its temporary flag.
let pre_state_root = state.update_tree_hash_cache()?;
vec![pre_state_root]
} else {
// Pre state is parent state. It is already stored in the DB without temporary status.
vec![]
};
let mut confirmed_state_roots =
if block.slot() > state.slot() && state.slot() > parent.beacon_block.slot() {
// Advanced pre-state. Delete its temporary flag.
let pre_state_root = state.update_tree_hash_cache()?;
vec![pre_state_root]
} else {
// Pre state is either unadvanced, or should not be stored long-term because there
// is no skipped slot between `parent` and `block`.
vec![]
};

// The block must have a higher slot than its parent.
if block.slot() <= parent.beacon_block.slot() {
Expand Down
5 changes: 5 additions & 0 deletions beacon_node/beacon_chain/src/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,11 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
));

store.do_atomically_with_block_and_blobs_cache(batch)?;

// Do a quick separate pass to delete obsoleted hot states, usually pre-states from the state
// advance which are not canonical due to blocks being applied on top.
store.prune_old_hot_states()?;

debug!(log, "Database pruning complete");

Ok(PruningOutcome::Successful {
Expand Down
51 changes: 51 additions & 0 deletions beacon_node/store/src/hot_cold_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2485,6 +2485,57 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>

Ok(())
}

/// Prune states from the hot database which are prior to the split.
///
/// This routine is important for cleaning up advanced states which are stored in the database
/// with a temporary flag.
pub fn prune_old_hot_states(&self) -> Result<(), Error> {
let split = self.get_split_info();
debug!(
self.log,
"Database state pruning started";
"split_slot" => split.slot,
);
let mut state_delete_batch = vec![];
for res in self
.hot_db
.iter_column::<Hash256>(DBColumn::BeaconStateSummary)
{
let (state_root, summary_bytes) = res?;
let summary = HotStateSummary::from_ssz_bytes(&summary_bytes)?;

if summary.slot <= split.slot {
let old = summary.slot < split.slot;
let non_canonical = summary.slot == split.slot
&& state_root != split.state_root
&& !split.state_root.is_zero();
if old || non_canonical {
let reason = if old {
"old dangling state"
} else {
"non-canonical"
};
debug!(
self.log,
"Deleting state";
"state_root" => ?state_root,
"slot" => summary.slot,
"reason" => reason,
);
state_delete_batch.push(StoreOp::DeleteState(state_root, Some(summary.slot)));
}
}
}
let num_deleted_states = state_delete_batch.len();
self.do_atomically_with_block_and_blobs_cache(state_delete_batch)?;
debug!(
self.log,
"Database state pruning complete";
"num_deleted_states" => num_deleted_states,
);
Ok(())
}
}

/// Advance the split point of the store, moving new finalized states to the freezer.
Expand Down
Loading