Skip to content

Commit a1b7d61

Browse files
authored
Modularize beacon node backend (#4718)
#4669 Modularize the beacon node backend to make it easier to add new database implementations
1 parent 266b241 commit a1b7d61

38 files changed

+1478
-649
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ BUILD_PATH_AARCH64 = "target/$(AARCH64_TAG)/release"
1414
PINNED_NIGHTLY ?= nightly
1515

1616
# List of features to use when cross-compiling. Can be overridden via the environment.
17-
CROSS_FEATURES ?= gnosis,slasher-lmdb,slasher-mdbx,slasher-redb,jemalloc
17+
CROSS_FEATURES ?= gnosis,slasher-lmdb,slasher-mdbx,slasher-redb,jemalloc,beacon-node-leveldb,beacon-node-redb
1818

1919
# Cargo profile for Cross builds. Default is for local builds, CI uses an override.
2020
CROSS_PROFILE ?= release

beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,6 @@ impl<E: EthSpec> PendingComponents<E> {
317317
None,
318318
)
319319
};
320-
321320
let executed_block = recover(diet_executed_block)?;
322321

323322
let AvailabilityPendingExecutedBlock {
@@ -732,7 +731,7 @@ mod test {
732731
use slog::{info, Logger};
733732
use state_processing::ConsensusContext;
734733
use std::collections::VecDeque;
735-
use store::{HotColdDB, ItemStore, LevelDB, StoreConfig};
734+
use store::{database::interface::BeaconNodeBackend, HotColdDB, ItemStore, StoreConfig};
736735
use tempfile::{tempdir, TempDir};
737736
use types::non_zero_usize::new_non_zero_usize;
738737
use types::{ExecPayload, MinimalEthSpec};
@@ -744,7 +743,7 @@ mod test {
744743
db_path: &TempDir,
745744
spec: Arc<ChainSpec>,
746745
log: Logger,
747-
) -> Arc<HotColdDB<E, LevelDB<E>, LevelDB<E>>> {
746+
) -> Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>> {
748747
let hot_path = db_path.path().join("hot_db");
749748
let cold_path = db_path.path().join("cold_db");
750749
let blobs_path = db_path.path().join("blobs_db");
@@ -920,7 +919,11 @@ mod test {
920919
)
921920
where
922921
E: EthSpec,
923-
T: BeaconChainTypes<HotStore = LevelDB<E>, ColdStore = LevelDB<E>, EthSpec = E>,
922+
T: BeaconChainTypes<
923+
HotStore = BeaconNodeBackend<E>,
924+
ColdStore = BeaconNodeBackend<E>,
925+
EthSpec = E,
926+
>,
924927
{
925928
let log = test_logger();
926929
let chain_db_path = tempdir().expect("should get temp dir");

beacon_node/beacon_chain/src/historical_blocks.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ use std::borrow::Cow;
1010
use std::iter;
1111
use std::time::Duration;
1212
use store::metadata::DataColumnInfo;
13-
use store::{
14-
get_key_for_col, AnchorInfo, BlobInfo, DBColumn, Error as StoreError, KeyValueStore,
15-
KeyValueStoreOp,
16-
};
13+
use store::{AnchorInfo, BlobInfo, DBColumn, Error as StoreError, KeyValueStore, KeyValueStoreOp};
1714
use strum::IntoStaticStr;
1815
use types::{FixedBytesExtended, Hash256, Slot};
1916

@@ -153,7 +150,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
153150
// Store block roots, including at all skip slots in the freezer DB.
154151
for slot in (block.slot().as_u64()..prev_block_slot.as_u64()).rev() {
155152
cold_batch.push(KeyValueStoreOp::PutKeyValue(
156-
get_key_for_col(DBColumn::BeaconBlockRoots.into(), &slot.to_be_bytes()),
153+
DBColumn::BeaconBlockRoots,
154+
slot.to_be_bytes().to_vec(),
157155
block_root.as_slice().to_vec(),
158156
));
159157
}
@@ -169,7 +167,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
169167
let genesis_slot = self.spec.genesis_slot;
170168
for slot in genesis_slot.as_u64()..prev_block_slot.as_u64() {
171169
cold_batch.push(KeyValueStoreOp::PutKeyValue(
172-
get_key_for_col(DBColumn::BeaconBlockRoots.into(), &slot.to_be_bytes()),
170+
DBColumn::BeaconBlockRoots,
171+
slot.to_be_bytes().to_vec(),
173172
self.genesis_block_root.as_slice().to_vec(),
174173
));
175174
}

beacon_node/beacon_chain/src/schema_change/migration_schema_v21.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ use crate::validator_pubkey_cache::DatabasePubkey;
33
use slog::{info, Logger};
44
use ssz::{Decode, Encode};
55
use std::sync::Arc;
6-
use store::{
7-
get_key_for_col, DBColumn, Error, HotColdDB, KeyValueStore, KeyValueStoreOp, StoreItem,
8-
};
6+
use store::{DBColumn, Error, HotColdDB, KeyValueStore, KeyValueStoreOp, StoreItem};
97
use types::{Hash256, PublicKey};
108

119
const LOG_EVERY: usize = 200_000;
@@ -62,9 +60,9 @@ pub fn downgrade_from_v21<T: BeaconChainTypes>(
6260
message: format!("{e:?}"),
6361
})?;
6462

65-
let db_key = get_key_for_col(DBColumn::PubkeyCache.into(), key.as_slice());
6663
ops.push(KeyValueStoreOp::PutKeyValue(
67-
db_key,
64+
DBColumn::PubkeyCache,
65+
key.as_slice().to_vec(),
6866
pubkey_bytes.as_ssz_bytes(),
6967
));
7068

beacon_node/beacon_chain/src/schema_change/migration_schema_v22.rs

+6-11
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use std::sync::Arc;
44
use store::chunked_iter::ChunkedVectorIter;
55
use store::{
66
chunked_vector::BlockRootsChunked,
7-
get_key_for_col,
87
metadata::{
98
SchemaVersion, ANCHOR_FOR_ARCHIVE_NODE, ANCHOR_UNINITIALIZED, STATE_UPPER_LIMIT_NO_RETAIN,
109
},
@@ -21,7 +20,7 @@ fn load_old_schema_frozen_state<T: BeaconChainTypes>(
2120
) -> Result<Option<BeaconState<T::EthSpec>>, Error> {
2221
let Some(partial_state_bytes) = db
2322
.cold_db
24-
.get_bytes(DBColumn::BeaconState.into(), state_root.as_slice())?
23+
.get_bytes(DBColumn::BeaconState, state_root.as_slice())?
2524
else {
2625
return Ok(None);
2726
};
@@ -136,10 +135,7 @@ pub fn delete_old_schema_freezer_data<T: BeaconChainTypes>(
136135
for column in columns {
137136
for res in db.cold_db.iter_column_keys::<Vec<u8>>(column) {
138137
let key = res?;
139-
cold_ops.push(KeyValueStoreOp::DeleteKey(get_key_for_col(
140-
column.as_str(),
141-
&key,
142-
)));
138+
cold_ops.push(KeyValueStoreOp::DeleteKey(column, key));
143139
}
144140
}
145141
let delete_ops = cold_ops.len();
@@ -175,7 +171,8 @@ pub fn write_new_schema_block_roots<T: BeaconChainTypes>(
175171
// Store the genesis block root if it would otherwise not be stored.
176172
if oldest_block_slot != 0 {
177173
cold_ops.push(KeyValueStoreOp::PutKeyValue(
178-
get_key_for_col(DBColumn::BeaconBlockRoots.into(), &0u64.to_be_bytes()),
174+
DBColumn::BeaconBlockRoots,
175+
0u64.to_be_bytes().to_vec(),
179176
genesis_block_root.as_slice().to_vec(),
180177
));
181178
}
@@ -192,10 +189,8 @@ pub fn write_new_schema_block_roots<T: BeaconChainTypes>(
192189
// OK to hold these in memory (10M slots * 43 bytes per KV ~= 430 MB).
193190
for (i, (slot, block_root)) in block_root_iter.enumerate() {
194191
cold_ops.push(KeyValueStoreOp::PutKeyValue(
195-
get_key_for_col(
196-
DBColumn::BeaconBlockRoots.into(),
197-
&(slot as u64).to_be_bytes(),
198-
),
192+
DBColumn::BeaconBlockRoots,
193+
slot.to_be_bytes().to_vec(),
199194
block_root.as_slice().to_vec(),
200195
));
201196

beacon_node/beacon_chain/src/test_utils.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ use std::str::FromStr;
5656
use std::sync::atomic::{AtomicUsize, Ordering};
5757
use std::sync::{Arc, LazyLock};
5858
use std::time::Duration;
59-
use store::{config::StoreConfig, HotColdDB, ItemStore, LevelDB, MemoryStore};
59+
use store::database::interface::BeaconNodeBackend;
60+
use store::{config::StoreConfig, HotColdDB, ItemStore, MemoryStore};
6061
use task_executor::TaskExecutor;
6162
use task_executor::{test_utils::TestRuntime, ShutdownReason};
6263
use tree_hash::TreeHash;
@@ -116,7 +117,7 @@ pub fn get_kzg(spec: &ChainSpec) -> Arc<Kzg> {
116117
pub type BaseHarnessType<E, THotStore, TColdStore> =
117118
Witness<TestingSlotClock, CachingEth1Backend<E>, E, THotStore, TColdStore>;
118119

119-
pub type DiskHarnessType<E> = BaseHarnessType<E, LevelDB<E>, LevelDB<E>>;
120+
pub type DiskHarnessType<E> = BaseHarnessType<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>;
120121
pub type EphemeralHarnessType<E> = BaseHarnessType<E, MemoryStore<E>, MemoryStore<E>>;
121122

122123
pub type BoxedMutator<E, Hot, Cold> = Box<
@@ -299,7 +300,10 @@ impl<E: EthSpec> Builder<EphemeralHarnessType<E>> {
299300

300301
impl<E: EthSpec> Builder<DiskHarnessType<E>> {
301302
/// Disk store, start from genesis.
302-
pub fn fresh_disk_store(mut self, store: Arc<HotColdDB<E, LevelDB<E>, LevelDB<E>>>) -> Self {
303+
pub fn fresh_disk_store(
304+
mut self,
305+
store: Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>>,
306+
) -> Self {
303307
let validator_keypairs = self
304308
.validator_keypairs
305309
.clone()
@@ -324,7 +328,10 @@ impl<E: EthSpec> Builder<DiskHarnessType<E>> {
324328
}
325329

326330
/// Disk store, resume.
327-
pub fn resumed_disk_store(mut self, store: Arc<HotColdDB<E, LevelDB<E>, LevelDB<E>>>) -> Self {
331+
pub fn resumed_disk_store(
332+
mut self,
333+
store: Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>>,
334+
) -> Self {
328335
let mutator = move |builder: BeaconChainBuilder<_>| {
329336
builder
330337
.resume_from_db()

beacon_node/beacon_chain/tests/op_verification.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use state_processing::per_block_processing::errors::{
1414
AttesterSlashingInvalid, BlockOperationError, ExitInvalid, ProposerSlashingInvalid,
1515
};
1616
use std::sync::{Arc, LazyLock};
17-
use store::{LevelDB, StoreConfig};
17+
use store::database::interface::BeaconNodeBackend;
18+
use store::StoreConfig;
1819
use tempfile::{tempdir, TempDir};
1920
use types::*;
2021

@@ -26,7 +27,7 @@ static KEYPAIRS: LazyLock<Vec<Keypair>> =
2627

2728
type E = MinimalEthSpec;
2829
type TestHarness = BeaconChainHarness<DiskHarnessType<E>>;
29-
type HotColdDB = store::HotColdDB<E, LevelDB<E>, LevelDB<E>>;
30+
type HotColdDB = store::HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>;
3031

3132
fn get_store(db_path: &TempDir) -> Arc<HotColdDB> {
3233
let spec = Arc::new(test_spec::<E>());

beacon_node/beacon_chain/tests/store_tests.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ use std::collections::HashSet;
2525
use std::convert::TryInto;
2626
use std::sync::{Arc, LazyLock};
2727
use std::time::Duration;
28+
use store::database::interface::BeaconNodeBackend;
2829
use store::metadata::{SchemaVersion, CURRENT_SCHEMA_VERSION, STATE_UPPER_LIMIT_NO_RETAIN};
2930
use store::{
3031
iter::{BlockRootsIterator, StateRootsIterator},
31-
BlobInfo, DBColumn, HotColdDB, LevelDB, StoreConfig,
32+
BlobInfo, DBColumn, HotColdDB, StoreConfig,
3233
};
3334
use tempfile::{tempdir, TempDir};
3435
use tokio::time::sleep;
@@ -46,15 +47,15 @@ static KEYPAIRS: LazyLock<Vec<Keypair>> =
4647
type E = MinimalEthSpec;
4748
type TestHarness = BeaconChainHarness<DiskHarnessType<E>>;
4849

49-
fn get_store(db_path: &TempDir) -> Arc<HotColdDB<E, LevelDB<E>, LevelDB<E>>> {
50+
fn get_store(db_path: &TempDir) -> Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>> {
5051
get_store_generic(db_path, StoreConfig::default(), test_spec::<E>())
5152
}
5253

5354
fn get_store_generic(
5455
db_path: &TempDir,
5556
config: StoreConfig,
5657
spec: ChainSpec,
57-
) -> Arc<HotColdDB<E, LevelDB<E>, LevelDB<E>>> {
58+
) -> Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>> {
5859
let hot_path = db_path.path().join("chain_db");
5960
let cold_path = db_path.path().join("freezer_db");
6061
let blobs_path = db_path.path().join("blobs_db");
@@ -73,7 +74,7 @@ fn get_store_generic(
7374
}
7475

7576
fn get_harness(
76-
store: Arc<HotColdDB<E, LevelDB<E>, LevelDB<E>>>,
77+
store: Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>>,
7778
validator_count: usize,
7879
) -> TestHarness {
7980
// Most tests expect to retain historic states, so we use this as the default.
@@ -85,7 +86,7 @@ fn get_harness(
8586
}
8687

8788
fn get_harness_generic(
88-
store: Arc<HotColdDB<E, LevelDB<E>, LevelDB<E>>>,
89+
store: Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>>,
8990
validator_count: usize,
9091
chain_config: ChainConfig,
9192
) -> TestHarness {
@@ -244,7 +245,6 @@ async fn full_participation_no_skips() {
244245
AttestationStrategy::AllValidators,
245246
)
246247
.await;
247-
248248
check_finalization(&harness, num_blocks_produced);
249249
check_split_slot(&harness, store);
250250
check_chain_dump(&harness, num_blocks_produced + 1);
@@ -3508,7 +3508,10 @@ fn check_finalization(harness: &TestHarness, expected_slot: u64) {
35083508
}
35093509

35103510
/// Check that the HotColdDB's split_slot is equal to the start slot of the last finalized epoch.
3511-
fn check_split_slot(harness: &TestHarness, store: Arc<HotColdDB<E, LevelDB<E>, LevelDB<E>>>) {
3511+
fn check_split_slot(
3512+
harness: &TestHarness,
3513+
store: Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>>,
3514+
) {
35123515
let split_slot = store.get_split_slot();
35133516
assert_eq!(
35143517
harness

beacon_node/client/src/builder.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use beacon_chain::{
1414
eth1_chain::{CachingEth1Backend, Eth1Chain},
1515
slot_clock::{SlotClock, SystemTimeSlotClock},
1616
state_advance_timer::spawn_state_advance_timer,
17-
store::{HotColdDB, ItemStore, LevelDB, StoreConfig},
17+
store::{HotColdDB, ItemStore, StoreConfig},
1818
BeaconChain, BeaconChainTypes, Eth1ChainBackend, MigratorConfig, ServerSentEventHandler,
1919
};
2020
use beacon_chain::{Kzg, LightClientProducerEvent};
@@ -41,6 +41,7 @@ use std::path::{Path, PathBuf};
4141
use std::sync::Arc;
4242
use std::time::Duration;
4343
use std::time::{SystemTime, UNIX_EPOCH};
44+
use store::database::interface::BeaconNodeBackend;
4445
use timer::spawn_timer;
4546
use tokio::sync::oneshot;
4647
use types::{
@@ -1030,7 +1031,7 @@ where
10301031
}
10311032

10321033
impl<TSlotClock, TEth1Backend, E>
1033-
ClientBuilder<Witness<TSlotClock, TEth1Backend, E, LevelDB<E>, LevelDB<E>>>
1034+
ClientBuilder<Witness<TSlotClock, TEth1Backend, E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>>
10341035
where
10351036
TSlotClock: SlotClock + 'static,
10361037
TEth1Backend: Eth1ChainBackend<E> + 'static,

beacon_node/http_api/tests/tests.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1933,7 +1933,7 @@ impl ApiTester {
19331933
.sync_committee_period(&self.chain.spec)
19341934
.unwrap();
19351935

1936-
let result = match self
1936+
match self
19371937
.client
19381938
.get_beacon_light_client_updates::<E>(current_sync_committee_period, 1)
19391939
.await
@@ -1954,7 +1954,6 @@ impl ApiTester {
19541954
.unwrap();
19551955

19561956
assert_eq!(1, expected.len());
1957-
assert_eq!(result.clone().unwrap().len(), expected.len());
19581957
self
19591958
}
19601959

@@ -1979,7 +1978,6 @@ impl ApiTester {
19791978
.get_light_client_bootstrap(&self.chain.store, &block_root, 1u64, &self.chain.spec);
19801979

19811980
assert!(expected.is_ok());
1982-
19831981
assert_eq!(result.unwrap().data, expected.unwrap().unwrap().0);
19841982

19851983
self

beacon_node/src/cli.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1591,5 +1591,14 @@ pub fn cli_app() -> Command {
15911591
.action(ArgAction::Set)
15921592
.display_order(0)
15931593
)
1594+
.arg(
1595+
Arg::new("beacon-node-backend")
1596+
.long("beacon-node-backend")
1597+
.value_name("DATABASE")
1598+
.value_parser(store::config::DatabaseBackend::VARIANTS.to_vec())
1599+
.help("Set the database backend to be used by the beacon node.")
1600+
.action(ArgAction::Set)
1601+
.display_order(0)
1602+
)
15941603
.group(ArgGroup::new("enable_http").args(["http", "gui", "staking"]).multiple(true))
15951604
}

beacon_node/src/config.rs

+4
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,10 @@ pub fn get_config<E: EthSpec>(
432432
warn!(log, "The slots-per-restore-point flag is deprecated");
433433
}
434434

435+
if let Some(backend) = clap_utils::parse_optional(cli_args, "beacon-node-backend")? {
436+
client_config.store.backend = backend;
437+
}
438+
435439
if let Some(hierarchy_config) = clap_utils::parse_optional(cli_args, "hierarchy-exponents")? {
436440
client_config.store.hierarchy_config = hierarchy_config;
437441
}

0 commit comments

Comments
 (0)