Skip to content

Commit d4a1fe2

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 ae6728e commit d4a1fe2

3 files changed

Lines changed: 71 additions & 3 deletions

File tree

src/builder.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ use crate::event::EventQueue;
5858
use crate::fee_estimator::OnchainFeeEstimator;
5959
use crate::gossip::GossipSource;
6060
use crate::io::sqlite_store::SqliteStore;
61+
use crate::io::tier_store::TierStore;
6162
use crate::io::utils::{
6263
read_all_objects, read_event_queue, read_external_pathfinding_scores_from_cache,
6364
read_network_graph, read_node_metrics, read_output_sweeper, read_peer_info, read_scorer,
@@ -154,6 +155,21 @@ impl std::fmt::Debug for LogWriterConfig {
154155
}
155156
}
156157

158+
#[derive(Default)]
159+
struct TierStoreConfig {
160+
ephemeral: Option<Arc<DynStore>>,
161+
backup: Option<Arc<DynStore>>,
162+
}
163+
164+
impl std::fmt::Debug for TierStoreConfig {
165+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166+
f.debug_struct("TierStoreConfig")
167+
.field("ephemeral", &self.ephemeral.as_ref().map(|_| "Arc<DynStore>"))
168+
.field("backup", &self.backup.as_ref().map(|_| "Arc<DynStore>"))
169+
.finish()
170+
}
171+
}
172+
157173
/// An error encountered during building a [`Node`].
158174
///
159175
/// [`Node`]: crate::Node
@@ -289,6 +305,7 @@ pub struct NodeBuilder {
289305
liquidity_source_config: Option<LiquiditySourceConfig>,
290306
log_writer_config: Option<LogWriterConfig>,
291307
async_payments_role: Option<AsyncPaymentsRole>,
308+
tier_store_config: Option<TierStoreConfig>,
292309
runtime_handle: Option<tokio::runtime::Handle>,
293310
pathfinding_scores_sync_config: Option<PathfindingScoresSyncConfig>,
294311
recovery_mode: bool,
@@ -307,6 +324,7 @@ impl NodeBuilder {
307324
let gossip_source_config = None;
308325
let liquidity_source_config = None;
309326
let log_writer_config = None;
327+
let tier_store_config = None;
310328
let runtime_handle = None;
311329
let pathfinding_scores_sync_config = None;
312330
let recovery_mode = false;
@@ -316,6 +334,7 @@ impl NodeBuilder {
316334
gossip_source_config,
317335
liquidity_source_config,
318336
log_writer_config,
337+
tier_store_config,
319338
runtime_handle,
320339
async_payments_role: None,
321340
pathfinding_scores_sync_config,
@@ -625,6 +644,34 @@ impl NodeBuilder {
625644
self
626645
}
627646

647+
/// Configures the backup store for local disaster recovery.
648+
///
649+
/// When building with tiered storage, this store receives a second durable
650+
/// copy of data written to the primary store.
651+
///
652+
/// Writes and removals for primary-backed data only succeed once both the
653+
/// primary and backup stores complete successfully.
654+
///
655+
/// If not set, durable data will be stored only in the primary store.
656+
pub fn set_backup_store(&mut self, backup_store: Arc<DynStore>) -> &mut Self {
657+
let tier_store_config = self.tier_store_config.get_or_insert(TierStoreConfig::default());
658+
tier_store_config.backup = Some(backup_store);
659+
self
660+
}
661+
662+
/// Configures the ephemeral store for non-critical, frequently-accessed data.
663+
///
664+
/// When building with tiered storage, this store is used for ephemeral data like
665+
/// the network graph and scorer data to reduce latency for reads. Data stored here
666+
/// can be rebuilt if lost.
667+
///
668+
/// If not set, non-critical data will be stored in the primary store.
669+
pub fn set_ephemeral_store(&mut self, ephemeral_store: Arc<DynStore>) -> &mut Self {
670+
let tier_store_config = self.tier_store_config.get_or_insert(TierStoreConfig::default());
671+
tier_store_config.ephemeral = Some(ephemeral_store);
672+
self
673+
}
674+
628675
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
629676
/// previously configured.
630677
pub fn build(&self, node_entropy: NodeEntropy) -> Result<Node, BuildError> {
@@ -780,8 +827,23 @@ impl NodeBuilder {
780827
}
781828

782829
/// Builds a [`Node`] instance according to the options previously configured.
830+
///
831+
/// The provided `kv_store` will be used as the primary storage backend. Optionally,
832+
/// an ephemeral store for frequently-accessed non-critical data (e.g., network graph, scorer)
833+
/// and a backup store for local disaster recovery can be configured via
834+
/// [`set_ephemeral_store`] and [`set_backup_store`].
835+
///
836+
/// [`set_ephemeral_store`]: Self::set_ephemeral_store
837+
/// [`set_backup_store`]: Self::set_backup_store
783838
pub fn build_with_store<S: SyncAndAsyncKVStore + Send + Sync + 'static>(
784839
&self, node_entropy: NodeEntropy, kv_store: S,
840+
) -> Result<Node, BuildError> {
841+
let primary_store: Arc<DynStore> = Arc::new(DynStoreWrapper(kv_store));
842+
self.build_with_dynstore(node_entropy, primary_store)
843+
}
844+
845+
fn build_with_dynstore(
846+
&self, node_entropy: NodeEntropy, primary_store: Arc<DynStore>,
785847
) -> Result<Node, BuildError> {
786848
let logger = setup_logger(&self.log_writer_config, &self.config)?;
787849

@@ -800,6 +862,13 @@ impl NodeBuilder {
800862
})?)
801863
};
802864

865+
let ts_config = self.tier_store_config.as_ref();
866+
let mut tier_store = TierStore::new(primary_store, Arc::clone(&logger));
867+
if let Some(config) = ts_config {
868+
config.ephemeral.as_ref().map(|s| tier_store.set_ephemeral_store(Arc::clone(s)));
869+
config.backup.as_ref().map(|s| tier_store.set_backup_store(Arc::clone(s)));
870+
}
871+
803872
let seed_bytes = node_entropy.to_seed_bytes();
804873
let config = Arc::new(self.config.clone());
805874

@@ -814,7 +883,7 @@ impl NodeBuilder {
814883
seed_bytes,
815884
runtime,
816885
logger,
817-
Arc::new(DynStoreWrapper(kv_store)),
886+
Arc::new(DynStoreWrapper(tier_store)),
818887
)
819888
}
820889
}

src/io/tier_store.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
55
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66
// accordance with one or both of these licenses.
7-
#![allow(dead_code)] // TODO: Temporal warning silencer. Will be removed in later commit.
87

98
use crate::io::utils::check_namespace_key_validity;
109
use crate::logger::{LdkLogger, Logger};

src/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ where
5757
{
5858
}
5959

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

0 commit comments

Comments
 (0)