Hierarchical state diffs in hot DB (#6750)

This PR implements https://github.com/sigp/lighthouse/pull/5978 (tree-states) but on the hot DB. It allows Lighthouse to massively reduce its disk footprint during non-finality and overall I/O in all cases.

Closes https://github.com/sigp/lighthouse/issues/6580

Conga into https://github.com/sigp/lighthouse/pull/6744

### TODOs

- [x] Fix OOM in CI https://github.com/sigp/lighthouse/pull/7176
- [x] optimise store_hot_state to avoid storing a duplicate state if the summary already exists (should be safe from races now that pruning is cleaner)
- [x] mispelled: get_ancenstor_state_root
- [x] get_ancestor_state_root should use state summaries
- [x] Prevent split from changing during ancestor calc
- [x] Use same hierarchy for hot and cold

### TODO Good optimization for future PRs

- [ ] On the migration, if the latest hot snapshot is aligned with the cold snapshot migrate the diffs instead of the full states.
```
align slot  time
10485760    Nov-26-2024
12582912    Sep-14-2025
14680064    Jul-02-2026
```

### TODO Maybe things good to have

- [ ] Rename anchor_slot https://github.com/sigp/lighthouse/compare/tree-states-hot-rebase-oom...dapplion:lighthouse:tree-states-hot-anchor-slot-rename?expand=1
- [ ] Make anchor fields not public such that they must be mutated through a method. To prevent un-wanted changes of the anchor_slot

### NOTTODO

- [ ] Use fork-choice and a new method [`descendants_of_checkpoint`](ca2388e196 (diff-046fbdb517ca16b80e4464c2c824cf001a74a0a94ac0065e635768ac391062a8)) to filter only the state summaries that descend of finalized checkpoint]
This commit is contained in:
Lion - dapplion
2025-06-19 04:43:25 +02:00
committed by GitHub
parent 6786b9d12a
commit dd98534158
33 changed files with 2695 additions and 812 deletions

View File

@@ -105,15 +105,6 @@ impl<E: EthSpec> KeyValueStore<E> for BeaconNodeBackend<E> {
}
}
fn begin_rw_transaction(&self) -> parking_lot::MutexGuard<()> {
match self {
#[cfg(feature = "leveldb")]
BeaconNodeBackend::LevelDb(txn) => leveldb_impl::LevelDB::begin_rw_transaction(txn),
#[cfg(feature = "redb")]
BeaconNodeBackend::Redb(txn) => redb_impl::Redb::begin_rw_transaction(txn),
}
}
fn compact(&self) -> Result<(), Error> {
match self {
#[cfg(feature = "leveldb")]

View File

@@ -13,7 +13,6 @@ use leveldb::{
iterator::{Iterable, LevelDBIterator},
options::{Options, ReadOptions},
};
use parking_lot::{Mutex, MutexGuard};
use std::collections::HashSet;
use std::marker::PhantomData;
use std::path::Path;
@@ -23,8 +22,6 @@ use super::interface::WriteOptions;
pub struct LevelDB<E: EthSpec> {
db: Database<BytesKey>,
/// A mutex to synchronise sensitive read-write transactions.
transaction_mutex: Mutex<()>,
_phantom: PhantomData<E>,
}
@@ -43,11 +40,9 @@ impl<E: EthSpec> LevelDB<E> {
options.create_if_missing = true;
let db = Database::open(path, options)?;
let transaction_mutex = Mutex::new(());
Ok(Self {
db,
transaction_mutex,
_phantom: PhantomData,
})
}
@@ -177,10 +172,6 @@ impl<E: EthSpec> LevelDB<E> {
Ok(())
}
pub fn begin_rw_transaction(&self) -> MutexGuard<()> {
self.transaction_mutex.lock()
}
/// Compact all values in the states and states flag columns.
pub fn compact(&self) -> Result<(), Error> {
let _timer = metrics::start_timer(&metrics::DISK_DB_COMPACT_TIMES);

View File

@@ -1,6 +1,6 @@
use crate::{metrics, ColumnIter, ColumnKeyIter, Key};
use crate::{DBColumn, Error, KeyValueStoreOp};
use parking_lot::{Mutex, MutexGuard, RwLock};
use parking_lot::RwLock;
use redb::TableDefinition;
use std::collections::HashSet;
use std::{borrow::BorrowMut, marker::PhantomData, path::Path};
@@ -13,7 +13,6 @@ pub const DB_FILE_NAME: &str = "database.redb";
pub struct Redb<E: EthSpec> {
db: RwLock<redb::Database>,
transaction_mutex: Mutex<()>,
_phantom: PhantomData<E>,
}
@@ -31,7 +30,6 @@ impl<E: EthSpec> Redb<E> {
pub fn open(path: &Path) -> Result<Self, Error> {
let db_file = path.join(DB_FILE_NAME);
let db = redb::Database::create(db_file)?;
let transaction_mutex = Mutex::new(());
for column in DBColumn::iter() {
Redb::<E>::create_table(&db, column.into())?;
@@ -39,7 +37,6 @@ impl<E: EthSpec> Redb<E> {
Ok(Self {
db: db.into(),
transaction_mutex,
_phantom: PhantomData,
})
}
@@ -61,10 +58,6 @@ impl<E: EthSpec> Redb<E> {
opts
}
pub fn begin_rw_transaction(&self) -> MutexGuard<()> {
self.transaction_mutex.lock()
}
pub fn put_bytes_with_options(
&self,
col: DBColumn,