diff --git a/beacon_node/client/src/config.rs b/beacon_node/client/src/config.rs index 6c3a98a46d..5f79d290ae 100644 --- a/beacon_node/client/src/config.rs +++ b/beacon_node/client/src/config.rs @@ -201,6 +201,10 @@ impl Config { pub fn create_data_dir(&self) -> Result { ensure_dir_exists(self.get_data_dir()) } + + pub fn get_blob_prune_margin_epochs(&self) -> Option { + self.store.blob_prune_margin_epochs + } } /// Ensure that the directory at `path` exists, by creating it and all parents if necessary. diff --git a/beacon_node/src/cli.rs b/beacon_node/src/cli.rs index 510882c366..1ce3b995cd 100644 --- a/beacon_node/src/cli.rs +++ b/beacon_node/src/cli.rs @@ -568,6 +568,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { .takes_value(true) .default_value("1") ) + .arg( + Arg::with_name("blob-prune-margin-epochs") + .long("blob-prune-margin-epochs") + .help("The margin for blob pruning in epochs. The oldest blobs are pruned \ + up until data_availability_boundary - blob_prune_margin_epochs.") + .takes_value(true) + .default_value("0") + ) /* * Misc. diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 14d40db03b..4974acc252 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -421,6 +421,14 @@ pub fn get_config( client_config.store.epochs_per_blob_prune = epochs_per_blob_prune; } + if let Some(blob_prune_margin_epochs) = + clap_utils::parse_optional(cli_args, "blob-prune-margin-epochs")? + { + if blob_prune_margin_epochs > 0 { + client_config.store.blob_prune_margin_epochs = Some(blob_prune_margin_epochs); + } + } + /* * Zero-ports * diff --git a/beacon_node/store/src/config.rs b/beacon_node/store/src/config.rs index b3b5285262..a7f3d177af 100644 --- a/beacon_node/store/src/config.rs +++ b/beacon_node/store/src/config.rs @@ -9,6 +9,7 @@ pub const DEFAULT_SLOTS_PER_RESTORE_POINT: u64 = 8192; pub const DEFAULT_BLOCK_CACHE_SIZE: usize = 5; pub const DEFAULT_BLOB_CACHE_SIZE: usize = 5; pub const DEFAULT_EPOCHS_PER_BLOB_PRUNE: u64 = 1; +pub const DEFAULT_BLOB_PUNE_MARGIN_EPOCHS: Option = None; /// Database configuration parameters. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -29,8 +30,11 @@ pub struct StoreConfig { pub prune_payloads: bool, /// Whether to prune blobs older than the blob data availability boundary. pub prune_blobs: bool, - /// Frequency of blob pruning. Default: every epoch. + /// Frequency of blob pruning in epochs. Default: every epoch. pub epochs_per_blob_prune: u64, + /// The margin for blob pruning in epochs. The oldest blobs are pruned up until + /// data_availability_boundary - blob_prune_margin_epochs. Default: 0. + pub blob_prune_margin_epochs: Option, } /// Variant of `StoreConfig` that gets written to disk. Contains immutable configuration params. @@ -57,6 +61,7 @@ impl Default for StoreConfig { prune_payloads: true, prune_blobs: true, epochs_per_blob_prune: DEFAULT_EPOCHS_PER_BLOB_PRUNE, + blob_prune_margin_epochs: DEFAULT_BLOB_PUNE_MARGIN_EPOCHS, } } } diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index 09a2de9a2f..4f37d30888 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -1798,8 +1798,20 @@ impl, Cold: ItemStore> HotColdDB let mut ops = vec![]; let mut last_pruned_block_root = None; - // Prune up until the data availability boundary. - let end_slot = (data_availability_boundary - 1).end_slot(E::slots_per_epoch()); + + // At most prune up until the data availability boundary epoch, leaving at least blobs in + // the data availability boundary epoch and younger. + let end_slot = { + let earliest_prunable_epoch = data_availability_boundary - 1; + // Stop pruning before reaching the data availability boundary if a margin is + // configured. + let end_epoch = if let Some(margin) = self.get_config().blob_prune_margin_epochs { + earliest_prunable_epoch - margin + } else { + earliest_prunable_epoch + }; + end_epoch.end_slot(E::slots_per_epoch()) + }; for res in self.forwards_block_roots_iterator_until( oldest_blob_slot, diff --git a/database_manager/src/lib.rs b/database_manager/src/lib.rs index 852da1af77..58d2103acc 100644 --- a/database_manager/src/lib.rs +++ b/database_manager/src/lib.rs @@ -94,6 +94,16 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { .help("Data directory for the freezer database.") .takes_value(true), ) + .arg( + Arg::with_name("blob-prune-margin-epochs") + .long("blob-prune-margin-epochs") + .help( + "The margin for blob pruning in epochs. The oldest blobs are pruned \ + up until data_availability_boundary - blob_prune_margin_epochs.", + ) + .takes_value(true) + .default_value("0"), + ) .subcommand(migrate_cli_app()) .subcommand(version_cli_app()) .subcommand(inspect_cli_app()) @@ -117,6 +127,14 @@ fn parse_client_config( client_config.store.slots_per_restore_point = sprp; client_config.store.slots_per_restore_point_set_explicitly = sprp_explicit; + if let Some(blob_prune_margin_epochs) = + clap_utils::parse_optional(cli_args, "blob-prune-margin-epochs")? + { + if blob_prune_margin_epochs > 0 { + client_config.store.blob_prune_margin_epochs = Some(blob_prune_margin_epochs); + } + } + Ok(client_config) }