Skip to content

Commit

Permalink
A0-3554: remove pruning option for rocksdb (#1516)
Browse files Browse the repository at this point in the history
# Description

Removes `rocksdb` as the db back-end when one wants to use database
pruning. Pruning when `rocksdb` is used works differently than when we
use `paritydb`, i.e. some pruning state is stored in-memory instead of
being db-backed. In our tests, rocksdb-pruning was pretty unstable -
sometimes it works, sometimes it allocates whole available memory and
crashes. In case of `paritydb` we haven't observed such problems. It
also renames `--experimental-pruning` cli option to `enable-pruning`.

## Type of change

Please delete options that are not relevant.

- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing
functionality to not work as expected)

# Checklist:

- I have made corresponding changes to the existing documentation
  • Loading branch information
fixxxedpoint authored Dec 4, 2023
1 parent 685e33f commit 35ab54d
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 67 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions bin/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ futures = { workspace = true }
hex = { workspace = true }
hex-literal = { workspace = true }
libp2p = { workspace = true }
static_assertions = { workspace = true }
thiserror = { workspace = true }

sc-basic-authorship = { workspace = true }
Expand Down
12 changes: 6 additions & 6 deletions bin/node/src/aleph_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ pub struct AlephCli {
#[clap(long, default_value_t = 20)]
max_nonfinalized_blocks: u32,

/// Experimental flag, allows pruning
///
/// TURNING THIS FLAG ON, CAN LEAD TO MALICIOUS BEHAVIOUR AND CAN BE PUNISHED ACCORDINGLY!
/// Enable database pruning. It removes older entries in the state-database. Pruning of blocks is not supported.
/// Note that we only support pruning with ParityDB database backend.
/// See also `--state-pruning` option for more details.
#[clap(long, default_value_t = false)]
experimental_pruning: bool,
enable_pruning: bool,

/// Maximum bit-rate per node in bytes per second of the alephbft validator network.
#[clap(long, default_value_t = 64 * 1024)]
Expand Down Expand Up @@ -90,8 +90,8 @@ impl AlephCli {
self.max_nonfinalized_blocks
}

pub fn experimental_pruning(&self) -> bool {
self.experimental_pruning
pub fn enable_pruning(&self) -> bool {
self.enable_pruning
}

pub fn alephbft_bit_rate_per_connection(&self) -> u64 {
Expand Down
70 changes: 9 additions & 61 deletions bin/node/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,25 @@
mod pruning_config;

#[cfg(any(feature = "try-runtime", feature = "runtime-benchmarks"))]
use aleph_node::ExecutorDispatch;
use aleph_node::{new_authority, new_partial, Cli, Subcommand};
#[cfg(any(feature = "try-runtime", feature = "runtime-benchmarks"))]
use aleph_runtime::Block;
use log::{info, warn};
use log::info;
use primitives::HEAP_PAGES;
use sc_cli::{clap::Parser, CliConfiguration, DatabasePruningMode, PruningParams, SubstrateCli};
use pruning_config::PruningConfigValidator;
use sc_cli::{clap::Parser, SubstrateCli};
use sc_network::config::Role;
use sc_service::{Configuration, PartialComponents};

fn default_state_pruning() -> Option<DatabasePruningMode> {
Some(DatabasePruningMode::Archive)
}

fn default_blocks_pruning() -> DatabasePruningMode {
DatabasePruningMode::ArchiveCanonical
}

fn pruning_changed(params: &PruningParams) -> bool {
let state_pruning_changed =
params.state_pruning.is_some() && (params.state_pruning != default_state_pruning());

let blocks_pruning_changed = params.blocks_pruning != default_blocks_pruning();

state_pruning_changed || blocks_pruning_changed
}

fn enforce_heap_pages(config: &mut Configuration) {
config.default_heap_pages = Some(HEAP_PAGES);
}

fn main() -> sc_cli::Result<()> {
let mut cli = Cli::parse();
let overwritten_pruning = pruning_changed(&cli.run.import_params.pruning_params);
if !cli.aleph.experimental_pruning() {
cli.run.import_params.pruning_params.state_pruning = default_state_pruning();
cli.run.import_params.pruning_params.blocks_pruning = default_blocks_pruning();
// We need to override state pruning to our default (archive), as substrate has 256 by default.
// 256 does not work with our code.
} else if cli.run.import_params.pruning_params.state_pruning.is_none() {
cli.run.import_params.pruning_params.state_pruning = default_state_pruning();
}

let pruning_config_validation_result = PruningConfigValidator::process(&mut cli);

match &cli.subcommand {
Some(Subcommand::BootstrapChain(cmd)) => cmd.run(),
Expand Down Expand Up @@ -155,14 +134,8 @@ fn main() -> sc_cli::Result<()> {
),
None => {
let runner = cli.create_runner(&cli.run)?;
if cli.aleph.experimental_pruning() {
warn!("Experimental_pruning was turned on. Usage of this flag can lead to misbehaviour, which can be punished. State pruning: {:?}; Blocks pruning: {:?};",
cli.run.state_pruning()?.unwrap_or_default(),
cli.run.blocks_pruning()?,
);
} else if overwritten_pruning {
warn!("Pruning not supported. Switching to keeping all block bodies and states.");
}

pruning_config_validation_result.report();

let mut aleph_cli_config = cli.aleph;
runner.run_node_until_exit(|mut config| async move {
Expand All @@ -187,28 +160,3 @@ fn main() -> sc_cli::Result<()> {
}
}
}

#[cfg(test)]
mod tests {
use sc_service::{BlocksPruning, PruningMode};

use super::{default_blocks_pruning, default_state_pruning, PruningParams};

#[test]
fn pruning_sanity_check() {
let pruning_params = PruningParams {
state_pruning: default_state_pruning(),
blocks_pruning: default_blocks_pruning(),
};

assert_eq!(
pruning_params.blocks_pruning().unwrap(),
BlocksPruning::KeepFinalized
);

assert_eq!(
pruning_params.state_pruning().unwrap().unwrap(),
PruningMode::ArchiveAll
);
}
}
162 changes: 162 additions & 0 deletions bin/node/src/pruning_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use log::warn;
use primitives::DEFAULT_SESSION_PERIOD;
use sc_cli::{Database, DatabasePruningMode, PruningParams};
use static_assertions::const_assert;

use crate::Cli;

/// Anything greater than 1800, which is two normal length sessions, should be enough.
/// We need to be able to read back so many states to retrieve the list of authorities for a session.
const MINIMAL_STATE_PRUNING: u32 = 2048;
const_assert!(MINIMAL_STATE_PRUNING >= 2 * DEFAULT_SESSION_PERIOD);

const DEFAULT_STATE_PRUNING: DatabasePruningMode = DatabasePruningMode::Archive;

const DEFAULT_BLOCKS_PRUNING: DatabasePruningMode = DatabasePruningMode::ArchiveCanonical;

const DEFAULT_DATABASE_FOR_PRUNING: sc_cli::Database = Database::ParityDb;

pub struct PruningConfigValidator {
pruning_enabled: bool,
overwritten_pruning: bool,
invalid_state_pruning_setting: Result<(), u32>,
invalid_blocks_pruning_setting: Result<(), u32>,
invalid_database_backend: Result<(), ()>,
}

impl PruningConfigValidator {
fn pruning_changed(params: &PruningParams) -> bool {
let state_pruning_changed =
params.state_pruning.unwrap_or(DEFAULT_STATE_PRUNING) != DEFAULT_STATE_PRUNING;
let blocks_pruning_changed = params.blocks_pruning != DEFAULT_BLOCKS_PRUNING;

state_pruning_changed || blocks_pruning_changed
}

pub fn process(cli: &mut Cli) -> Self {
let overwritten_pruning = Self::pruning_changed(&cli.run.import_params.pruning_params);
let pruning_enabled = cli.aleph.enable_pruning();

let mut result = PruningConfigValidator {
pruning_enabled,
overwritten_pruning,
invalid_state_pruning_setting: Ok(()),
invalid_blocks_pruning_setting: Ok(()),
invalid_database_backend: Ok(()),
};

if !pruning_enabled {
// We need to override state pruning to our default (archive), as substrate has 256 by default.
// 256 does not work with our code.
cli.run.import_params.pruning_params.state_pruning = Some(DEFAULT_STATE_PRUNING);
cli.run.import_params.pruning_params.blocks_pruning = DEFAULT_BLOCKS_PRUNING;
return result;
}

result.process_state_pruning(cli);
result.process_blocks_pruning(cli);
result.process_database(cli);

result
}

pub fn report(self) {
if !self.pruning_enabled {
if self.overwritten_pruning {
warn!("Pruning not enabled. Switching to keeping all block bodies and states. Please use `--enable-pruning` flag.");
}
return;
}
if let Err(max_blocks) = self.invalid_state_pruning_setting {
warn!(
"State pruning was enabled but the `state-pruning` \
parameter is smaller than minimal supported value \
(provided: {}, min: {}). Switching to {}.",
max_blocks, MINIMAL_STATE_PRUNING, MINIMAL_STATE_PRUNING,
);
}
if let Err(blocks_pruning) = self.invalid_blocks_pruning_setting {
warn!(
"Blocks pruning was enabled but the provided value for the `blocks-pruning` parameter is not valid ({blocks_pruning}). \
Supported values are `archive` and `archive-canonical`. \
Switching to `archive-canonical`",
);
}
if self.invalid_database_backend.is_err() {
warn!(
"Pruning was enabled but the selected database backend \
is not supported with pruning. Switching to `paritydb`.",
);
}
}

fn process_database(&mut self, cli: &mut Cli) {
match cli
.run
.import_params
.database_params
.database
.get_or_insert(DEFAULT_DATABASE_FOR_PRUNING)
{
Database::ParityDb => {}
db @ (Database::RocksDb | Database::Auto | Database::ParityDbDeprecated) => {
self.invalid_database_backend = Err(());
*db = DEFAULT_DATABASE_FOR_PRUNING;
}
}
}

fn process_blocks_pruning(&mut self, cli: &mut Cli) {
match cli.run.import_params.pruning_params.blocks_pruning {
DatabasePruningMode::Archive | DatabasePruningMode::ArchiveCanonical => {}
DatabasePruningMode::Custom(blocks_pruning) => {
self.invalid_blocks_pruning_setting = Err(blocks_pruning);
cli.run.import_params.pruning_params.blocks_pruning = DEFAULT_BLOCKS_PRUNING;
}
}
}

fn process_state_pruning(&mut self, cli: &mut Cli) {
match cli
.run
.import_params
.pruning_params
.state_pruning
.get_or_insert(DatabasePruningMode::Custom(MINIMAL_STATE_PRUNING))
{
DatabasePruningMode::Archive | DatabasePruningMode::ArchiveCanonical => {}
DatabasePruningMode::Custom(max_blocks) => {
if *max_blocks < MINIMAL_STATE_PRUNING {
self.invalid_state_pruning_setting = Err(*max_blocks);
*max_blocks = MINIMAL_STATE_PRUNING;
}
}
}
}
}

#[cfg(test)]
mod tests {
use sc_service::{BlocksPruning, PruningMode};

use super::PruningParams;
use crate::pruning_config::{DEFAULT_BLOCKS_PRUNING, DEFAULT_STATE_PRUNING};

#[test]
fn pruning_sanity_check() {
let pruning_params = PruningParams {
state_pruning: Some(DEFAULT_STATE_PRUNING),
blocks_pruning: DEFAULT_BLOCKS_PRUNING,
};

assert_eq!(
pruning_params.blocks_pruning().unwrap(),
BlocksPruning::KeepFinalized
);

assert_eq!(
pruning_params.state_pruning().unwrap().unwrap(),
PruningMode::ArchiveAll
);
}
}

0 comments on commit 35ab54d

Please sign in to comment.