Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 72 additions & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use crate::event::EventQueue;
use crate::fee_estimator::OnchainFeeEstimator;
use crate::gossip::GossipSource;
use crate::io::sqlite_store::SqliteStore;
use crate::io::tier_store::TierStore;
use crate::io::utils::{
read_event_queue, read_external_pathfinding_scores_from_cache, read_network_graph,
read_node_metrics, read_output_sweeper, read_payments, read_peer_info, read_pending_payments,
Expand Down Expand Up @@ -151,6 +152,21 @@ impl std::fmt::Debug for LogWriterConfig {
}
}

#[derive(Default)]
struct TierStoreConfig {
ephemeral: Option<Arc<DynStore>>,
backup: Option<Arc<DynStore>>,
}

impl std::fmt::Debug for TierStoreConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TierStoreConfig")
.field("ephemeral", &self.ephemeral.as_ref().map(|_| "Arc<DynStore>"))
.field("backup", &self.backup.as_ref().map(|_| "Arc<DynStore>"))
.finish()
}
}

/// An error encountered during building a [`Node`].
///
/// [`Node`]: crate::Node
Expand Down Expand Up @@ -278,6 +294,7 @@ pub struct NodeBuilder {
liquidity_source_config: Option<LiquiditySourceConfig>,
log_writer_config: Option<LogWriterConfig>,
async_payments_role: Option<AsyncPaymentsRole>,
tier_store_config: Option<TierStoreConfig>,
runtime_handle: Option<tokio::runtime::Handle>,
pathfinding_scores_sync_config: Option<PathfindingScoresSyncConfig>,
recovery_mode: bool,
Expand All @@ -296,6 +313,7 @@ impl NodeBuilder {
let gossip_source_config = None;
let liquidity_source_config = None;
let log_writer_config = None;
let tier_store_config = None;
let runtime_handle = None;
let pathfinding_scores_sync_config = None;
let recovery_mode = false;
Expand All @@ -305,6 +323,7 @@ impl NodeBuilder {
gossip_source_config,
liquidity_source_config,
log_writer_config,
tier_store_config,
runtime_handle,
async_payments_role: None,
pathfinding_scores_sync_config,
Expand Down Expand Up @@ -614,6 +633,36 @@ impl NodeBuilder {
self
}

/// Configures the backup store for local disaster recovery.
///
/// When building with tiered storage, this store receives a second durable
/// copy of data written to the primary store.
///
/// Writes and removals for primary-backed data only succeed once both the
/// primary and backup stores complete successfully.
///
/// If not set, durable data will be stored only in the primary store.
#[allow(dead_code)] // Used by subsequent FFI/test integration commits.
pub fn set_backup_store(&mut self, backup_store: Arc<DynStore>) -> &mut Self {
let tier_store_config = self.tier_store_config.get_or_insert(TierStoreConfig::default());
tier_store_config.backup = Some(backup_store);
self
}

/// Configures the ephemeral store for non-critical, frequently-accessed data.
///
/// When building with tiered storage, this store is used for ephemeral data like
/// the network graph and scorer data to reduce latency for reads. Data stored here
/// can be rebuilt if lost.
///
/// If not set, non-critical data will be stored in the primary store.
#[allow(dead_code)] // Used by subsequent FFI/test integration commits.
pub fn set_ephemeral_store(&mut self, ephemeral_store: Arc<DynStore>) -> &mut Self {
let tier_store_config = self.tier_store_config.get_or_insert(TierStoreConfig::default());
tier_store_config.ephemeral = Some(ephemeral_store);
self
}

/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
/// previously configured.
pub fn build(&self, node_entropy: NodeEntropy) -> Result<Node, BuildError> {
Expand Down Expand Up @@ -762,8 +811,23 @@ impl NodeBuilder {
}

/// Builds a [`Node`] instance according to the options previously configured.
///
/// The provided `kv_store` will be used as the primary storage backend. Optionally,
/// an ephemeral store for frequently-accessed non-critical data (e.g., network graph, scorer)
/// and a backup store for local disaster recovery can be configured via
/// [`set_ephemeral_store`] and [`set_backup_store`].
///
/// [`set_ephemeral_store`]: Self::set_ephemeral_store
/// [`set_backup_store`]: Self::set_backup_store
pub fn build_with_store<S: SyncAndAsyncKVStore + Send + Sync + 'static>(
&self, node_entropy: NodeEntropy, kv_store: S,
) -> Result<Node, BuildError> {
let primary_store: Arc<DynStore> = Arc::new(DynStoreWrapper(kv_store));
self.build_with_dynstore(node_entropy, primary_store)
}

fn build_with_dynstore(
&self, node_entropy: NodeEntropy, primary_store: Arc<DynStore>,
) -> Result<Node, BuildError> {
let logger = setup_logger(&self.log_writer_config, &self.config)?;

Expand All @@ -776,6 +840,13 @@ impl NodeBuilder {
})?)
};

let ts_config = self.tier_store_config.as_ref();
let mut tier_store = TierStore::new(primary_store, Arc::clone(&logger));
if let Some(config) = ts_config {
config.ephemeral.as_ref().map(|s| tier_store.set_ephemeral_store(Arc::clone(s)));
config.backup.as_ref().map(|s| tier_store.set_backup_store(Arc::clone(s)));
}

let seed_bytes = node_entropy.to_seed_bytes();
let config = Arc::new(self.config.clone());

Expand All @@ -790,7 +861,7 @@ impl NodeBuilder {
seed_bytes,
runtime,
logger,
Arc::new(DynStoreWrapper(kv_store)),
Arc::new(DynStoreWrapper(tier_store)),
)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
pub mod sqlite_store;
#[cfg(test)]
pub(crate) mod test_utils;
pub(crate) mod tier_store;
pub(crate) mod utils;
pub mod vss_store;

Expand Down
Loading