Skip to content

Commit

Permalink
Redb slasher backend impl (#4529)
Browse files Browse the repository at this point in the history
* initial redb impl

* redb impl

* remove phantom data

* fixed table definition

* fighting the borrow checker

* a rough draft that doesnt cause lifetime issues

* refactoring

* refactor

* refactor

* passing unit tests

* refactor

* refactor

* refactor

* commit

* move everything to one database

* remove panics, ready for a review

* merge

* a working redb impl

* passing a ref of txn to cursor

* this tries to create a second write transaction when initializing cursor. breaks everything

* Use 2 lifetimes and subtyping

Also fixes a bug in last_key caused by rev and next_back cancelling out

* Move table into cursor

* Merge remote-tracking branch 'origin/unstable' into redb-slasher-backend-impl

* changes based on feedback

* update lmdb

* fix lifetime issues

* moving everything from Cursor to Transaction

* update

* upgrade to redb 2.0

* Merge branch 'unstable' of https://github.com/sigp/lighthouse into redb-slasher-backend-impl

* bring back cursor

* Merge branch 'unstable' of https://github.com/sigp/lighthouse into redb-slasher-backend-impl

* fix delete while

* linting

* linting

* switch to lmdb

* update redb to v2.1

* build fixes, remove unwrap or default

* another build error

* hopefully this is the last build error

* fmt

* cargo.toml

* fix mdbx

* Merge branch 'unstable' of https://github.com/sigp/lighthouse into redb-slasher-backend-impl

* Remove a collect

* Merge remote-tracking branch 'origin/unstable' into redb-slasher-backend-impl

* Merge branch 'redb-slasher-backend-impl' of https://github.com/eserilev/lighthouse into redb-slasher-backend-impl

* re-enable test

* fix failing slasher test

* Merge remote-tracking branch 'origin/unstable' into redb-slasher-backend-impl

* Rename DB file to `slasher.redb`
  • Loading branch information
eserilev authored Jul 1, 2024
1 parent 16b8113 commit 70bcba1
Show file tree
Hide file tree
Showing 18 changed files with 495 additions and 68 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ PINNED_NIGHTLY ?= nightly
CLIPPY_PINNED_NIGHTLY=nightly-2022-05-19

# List of features to use when cross-compiling. Can be overridden via the environment.
CROSS_FEATURES ?= gnosis,slasher-lmdb,slasher-mdbx,jemalloc
CROSS_FEATURES ?= gnosis,slasher-lmdb,slasher-mdbx,slasher-redb,jemalloc

# Cargo profile for Cross builds. Default is for local builds, CI uses an override.
CROSS_PROFILE ?= release
Expand Down
2 changes: 2 additions & 0 deletions lighthouse/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ gnosis = []
slasher-mdbx = ["slasher/mdbx"]
# Support slasher LMDB backend.
slasher-lmdb = ["slasher/lmdb"]
# Support slasher redb backend.
slasher-redb = ["slasher/redb"]
# Deprecated. This is now enabled by default on non windows targets.
jemalloc = []

Expand Down
2 changes: 2 additions & 0 deletions lighthouse/tests/beacon_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2242,6 +2242,8 @@ fn slasher_broadcast_flag_false() {
assert!(!slasher_config.broadcast);
});
}

#[cfg(all(feature = "lmdb"))]
#[test]
fn slasher_backend_override_to_default() {
// Hard to test this flag because all but one backend is disabled by default and the backend
Expand Down
4 changes: 4 additions & 0 deletions slasher/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ edition = { workspace = true }
default = ["lmdb"]
mdbx = ["dep:mdbx"]
lmdb = ["lmdb-rkv", "lmdb-rkv-sys"]
redb = ["dep:redb"]
portable = ["types/portable"]

[dependencies]
bincode = { workspace = true }
byteorder = { workspace = true }
derivative = { workspace = true }
ethereum_ssz = { workspace = true }
ethereum_ssz_derive = { workspace = true }
flate2 = { version = "1.0.14", features = ["zlib"], default-features = false }
Expand All @@ -36,6 +38,8 @@ mdbx = { package = "libmdbx", git = "https://github.com/sigp/libmdbx-rs", tag =
lmdb-rkv = { git = "https://github.com/sigp/lmdb-rs", rev = "f33845c6469b94265319aac0ed5085597862c27e", optional = true }
lmdb-rkv-sys = { git = "https://github.com/sigp/lmdb-rs", rev = "f33845c6469b94265319aac0ed5085597862c27e", optional = true }

redb = { version = "2.1", optional = true }

[dev-dependencies]
maplit = { workspace = true }
rayon = { workspace = true }
Expand Down
9 changes: 7 additions & 2 deletions slasher/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@ pub const DEFAULT_MAX_DB_SIZE: usize = 512 * 1024; // 512 GiB
pub const DEFAULT_ATTESTATION_ROOT_CACHE_SIZE: NonZeroUsize = new_non_zero_usize(100_000);
pub const DEFAULT_BROADCAST: bool = false;

#[cfg(all(feature = "mdbx", not(feature = "lmdb")))]
#[cfg(all(feature = "mdbx", not(any(feature = "lmdb", feature = "redb"))))]
pub const DEFAULT_BACKEND: DatabaseBackend = DatabaseBackend::Mdbx;
#[cfg(feature = "lmdb")]
pub const DEFAULT_BACKEND: DatabaseBackend = DatabaseBackend::Lmdb;
#[cfg(not(any(feature = "mdbx", feature = "lmdb")))]
#[cfg(all(feature = "redb", not(any(feature = "mdbx", feature = "lmdb"))))]
pub const DEFAULT_BACKEND: DatabaseBackend = DatabaseBackend::Redb;
#[cfg(not(any(feature = "mdbx", feature = "lmdb", feature = "redb")))]
pub const DEFAULT_BACKEND: DatabaseBackend = DatabaseBackend::Disabled;

pub const MAX_HISTORY_LENGTH: usize = 1 << 16;
pub const MEGABYTE: usize = 1 << 20;
pub const MDBX_DATA_FILENAME: &str = "mdbx.dat";
pub const REDB_DATA_FILENAME: &str = "slasher.redb";

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
Expand Down Expand Up @@ -64,6 +67,8 @@ pub enum DatabaseBackend {
Mdbx,
#[cfg(feature = "lmdb")]
Lmdb,
#[cfg(feature = "redb")]
Redb,
Disabled,
}

Expand Down
59 changes: 22 additions & 37 deletions slasher/src/database.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod interface;
mod lmdb_impl;
mod mdbx_impl;
mod redb_impl;

use crate::{
metrics, AttesterRecord, AttesterSlashingStatus, CompactAttesterRecord, Config, Error,
Expand Down Expand Up @@ -489,8 +490,7 @@ impl<E: EthSpec> SlasherDB<E> {
}

// Store the new indexed attestation at the end of the current table.
let db = &self.databases.indexed_attestation_db;
let mut cursor = txn.cursor(db)?;
let mut cursor = txn.cursor(&self.databases.indexed_attestation_db)?;

let indexed_att_id = match cursor.last_key()? {
// First ID is 1 so that 0 can be used to represent `null` in `CompactAttesterRecord`.
Expand All @@ -504,7 +504,6 @@ impl<E: EthSpec> SlasherDB<E> {

cursor.put(attestation_key.as_ref(), &data)?;
drop(cursor);

// Update the (epoch, hash) to ID mapping.
self.put_indexed_attestation_id(txn, &id_key, attestation_key)?;

Expand Down Expand Up @@ -743,21 +742,17 @@ impl<E: EthSpec> SlasherDB<E> {
return Ok(());
}

loop {
let (key_bytes, _) = cursor.get_current()?.ok_or(Error::MissingProposerKey)?;

let (slot, _) = ProposerKey::parse(key_bytes)?;
let should_delete = |key: &[u8]| -> Result<bool, Error> {
let mut should_delete = false;
let (slot, _) = ProposerKey::parse(Cow::from(key))?;
if slot < min_slot {
cursor.delete_current()?;

// End the loop if there is no next entry.
if cursor.next_key()?.is_none() {
break;
}
} else {
break;
should_delete = true;
}
}

Ok(should_delete)
};

cursor.delete_while(should_delete)?;

Ok(())
}
Expand All @@ -771,37 +766,27 @@ impl<E: EthSpec> SlasherDB<E> {
.saturating_add(1u64)
.saturating_sub(self.config.history_length as u64);

// Collect indexed attestation IDs to delete.
let mut indexed_attestation_ids = vec![];

let mut cursor = txn.cursor(&self.databases.indexed_attestation_id_db)?;

// Position cursor at first key, bailing out if the database is empty.
if cursor.first_key()?.is_none() {
return Ok(());
}

loop {
let (key_bytes, value) = cursor
.get_current()?
.ok_or(Error::MissingIndexedAttestationIdKey)?;

let (target_epoch, _) = IndexedAttestationIdKey::parse(key_bytes)?;

let should_delete = |key: &[u8]| -> Result<bool, Error> {
let (target_epoch, _) = IndexedAttestationIdKey::parse(Cow::from(key))?;
if target_epoch < min_epoch {
indexed_attestation_ids.push(IndexedAttestationId::new(
IndexedAttestationId::parse(value)?,
));
return Ok(true);
}

cursor.delete_current()?;
Ok(false)
};

if cursor.next_key()?.is_none() {
break;
}
} else {
break;
}
}
let indexed_attestation_ids = cursor
.delete_while(should_delete)?
.into_iter()
.map(|id| IndexedAttestationId::parse(id).map(IndexedAttestationId::new))
.collect::<Result<Vec<IndexedAttestationId>, Error>>()?;
drop(cursor);

// Delete the indexed attestations.
Expand Down
Loading

0 comments on commit 70bcba1

Please sign in to comment.