@@ -58,6 +58,7 @@ use crate::event::EventQueue;
5858use crate :: fee_estimator:: OnchainFeeEstimator ;
5959use crate :: gossip:: GossipSource ;
6060use crate :: io:: sqlite_store:: SqliteStore ;
61+ use crate :: io:: tier_store:: TierStore ;
6162use 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}
0 commit comments