Skip to content

Commit 7720935

Browse files
committed
Integrate TierStore into NodeBuilder
Add native builder support for tiered storage by introducing `TierStoreConfig`, backup and ephemeral store configuration methods, and a `build_with_dynstore` path that wraps the provided store as the primary tier and applies any configured secondary tiers. This makes tiered storage a builder concern while keeping the Rust `build_with_store` API ergonomic for native callers. Note: The temporary `dead_code` allowance will be removed in a follow-up commit once the new tier-store builder APIs are exercised.
1 parent 0cda655 commit 7720935

File tree

2 files changed

+73
-2
lines changed

2 files changed

+73
-2
lines changed

src/builder.rs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ use crate::event::EventQueue;
5454
use crate::fee_estimator::OnchainFeeEstimator;
5555
use crate::gossip::GossipSource;
5656
use crate::io::sqlite_store::SqliteStore;
57+
use crate::io::tier_store::TierStore;
5758
use crate::io::utils::{
5859
read_event_queue, read_external_pathfinding_scores_from_cache, read_network_graph,
5960
read_node_metrics, read_output_sweeper, read_payments, read_peer_info, read_pending_payments,
@@ -151,6 +152,21 @@ impl std::fmt::Debug for LogWriterConfig {
151152
}
152153
}
153154

155+
#[derive(Default)]
156+
struct TierStoreConfig {
157+
ephemeral: Option<Arc<DynStore>>,
158+
backup: Option<Arc<DynStore>>,
159+
}
160+
161+
impl std::fmt::Debug for TierStoreConfig {
162+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163+
f.debug_struct("TierStoreConfig")
164+
.field("ephemeral", &self.ephemeral.as_ref().map(|_| "Arc<DynStore>"))
165+
.field("backup", &self.backup.as_ref().map(|_| "Arc<DynStore>"))
166+
.finish()
167+
}
168+
}
169+
154170
/// An error encountered during building a [`Node`].
155171
///
156172
/// [`Node`]: crate::Node
@@ -278,6 +294,7 @@ pub struct NodeBuilder {
278294
liquidity_source_config: Option<LiquiditySourceConfig>,
279295
log_writer_config: Option<LogWriterConfig>,
280296
async_payments_role: Option<AsyncPaymentsRole>,
297+
tier_store_config: Option<TierStoreConfig>,
281298
runtime_handle: Option<tokio::runtime::Handle>,
282299
pathfinding_scores_sync_config: Option<PathfindingScoresSyncConfig>,
283300
recovery_mode: bool,
@@ -296,6 +313,7 @@ impl NodeBuilder {
296313
let gossip_source_config = None;
297314
let liquidity_source_config = None;
298315
let log_writer_config = None;
316+
let tier_store_config = None;
299317
let runtime_handle = None;
300318
let pathfinding_scores_sync_config = None;
301319
let recovery_mode = false;
@@ -305,6 +323,7 @@ impl NodeBuilder {
305323
gossip_source_config,
306324
liquidity_source_config,
307325
log_writer_config,
326+
tier_store_config,
308327
runtime_handle,
309328
async_payments_role: None,
310329
pathfinding_scores_sync_config,
@@ -614,6 +633,36 @@ impl NodeBuilder {
614633
self
615634
}
616635

636+
/// Configures the backup store for local disaster recovery.
637+
///
638+
/// When building with tiered storage, this store receives a second durable
639+
/// copy of data written to the primary store.
640+
///
641+
/// Writes and removals for primary-backed data only succeed once both the
642+
/// primary and backup stores complete successfully.
643+
///
644+
/// If not set, durable data will be stored only in the primary store.
645+
#[allow(dead_code)] // Used by subsequent FFI/test integration commits.
646+
pub fn set_backup_store(&mut self, backup_store: Arc<DynStore>) -> &mut Self {
647+
let tier_store_config = self.tier_store_config.get_or_insert(TierStoreConfig::default());
648+
tier_store_config.backup = Some(backup_store);
649+
self
650+
}
651+
652+
/// Configures the ephemeral store for non-critical, frequently-accessed data.
653+
///
654+
/// When building with tiered storage, this store is used for ephemeral data like
655+
/// the network graph and scorer data to reduce latency for reads. Data stored here
656+
/// can be rebuilt if lost.
657+
///
658+
/// If not set, non-critical data will be stored in the primary store.
659+
#[allow(dead_code)] // Used by subsequent FFI/test integration commits.
660+
pub fn set_ephemeral_store(&mut self, ephemeral_store: Arc<DynStore>) -> &mut Self {
661+
let tier_store_config = self.tier_store_config.get_or_insert(TierStoreConfig::default());
662+
tier_store_config.ephemeral = Some(ephemeral_store);
663+
self
664+
}
665+
617666
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
618667
/// previously configured.
619668
pub fn build(&self, node_entropy: NodeEntropy) -> Result<Node, BuildError> {
@@ -762,8 +811,23 @@ impl NodeBuilder {
762811
}
763812

764813
/// Builds a [`Node`] instance according to the options previously configured.
814+
///
815+
/// The provided `kv_store` will be used as the primary storage backend. Optionally,
816+
/// an ephemeral store for frequently-accessed non-critical data (e.g., network graph, scorer)
817+
/// and a backup store for local disaster recovery can be configured via
818+
/// [`set_ephemeral_store`] and [`set_backup_store`].
819+
///
820+
/// [`set_ephemeral_store`]: Self::set_ephemeral_store
821+
/// [`set_backup_store`]: Self::set_backup_store
765822
pub fn build_with_store<S: SyncAndAsyncKVStore + Send + Sync + 'static>(
766823
&self, node_entropy: NodeEntropy, kv_store: S,
824+
) -> Result<Node, BuildError> {
825+
let primary_store: Arc<DynStore> = Arc::new(DynStoreWrapper(kv_store));
826+
self.build_with_dynstore(node_entropy, primary_store)
827+
}
828+
829+
fn build_with_dynstore(
830+
&self, node_entropy: NodeEntropy, primary_store: Arc<DynStore>,
767831
) -> Result<Node, BuildError> {
768832
let logger = setup_logger(&self.log_writer_config, &self.config)?;
769833

@@ -776,6 +840,13 @@ impl NodeBuilder {
776840
})?)
777841
};
778842

843+
let ts_config = self.tier_store_config.as_ref();
844+
let mut tier_store = TierStore::new(primary_store, Arc::clone(&logger));
845+
if let Some(config) = ts_config {
846+
config.ephemeral.as_ref().map(|s| tier_store.set_ephemeral_store(Arc::clone(s)));
847+
config.backup.as_ref().map(|s| tier_store.set_backup_store(Arc::clone(s)));
848+
}
849+
779850
let seed_bytes = node_entropy.to_seed_bytes();
780851
let config = Arc::new(self.config.clone());
781852

@@ -790,7 +861,7 @@ impl NodeBuilder {
790861
seed_bytes,
791862
runtime,
792863
logger,
793-
Arc::new(DynStoreWrapper(kv_store)),
864+
Arc::new(DynStoreWrapper(tier_store)),
794865
)
795866
}
796867
}

src/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ where
5151
{
5252
}
5353

54-
pub(crate) trait DynStoreTrait: Send + Sync {
54+
pub trait DynStoreTrait: Send + Sync {
5555
fn read_async(
5656
&self, primary_namespace: &str, secondary_namespace: &str, key: &str,
5757
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, bitcoin::io::Error>> + Send + 'static>>;

0 commit comments

Comments
 (0)