@@ -190,6 +190,8 @@ pub enum BuildError {
190190 ///
191191 /// [`KVStore`]: lightning::util::persist::KVStore
192192 KVStoreSetupFailed ,
193+ /// Another node currently owns the PostgreSQL node lease.
194+ NodeLeaseUnavailable ,
193195 /// We failed to setup the onchain wallet.
194196 WalletSetupFailed ,
195197 /// We failed to setup the logger.
@@ -232,6 +234,7 @@ impl fmt::Display for BuildError {
232234 Self :: WriteFailed => write ! ( f, "Failed to write to store." ) ,
233235 Self :: StoragePathAccessFailed => write ! ( f, "Failed to access the given storage path." ) ,
234236 Self :: KVStoreSetupFailed => write ! ( f, "Failed to setup KVStore." ) ,
237+ Self :: NodeLeaseUnavailable => write ! ( f, "The PostgreSQL node lease is unavailable." ) ,
235238 Self :: WalletSetupFailed => write ! ( f, "Failed to setup onchain wallet." ) ,
236239 Self :: LoggerSetupFailed => write ! ( f, "Failed to setup the logger." ) ,
237240 Self :: ChainSourceSetupFailed => write ! ( f, "Failed to setup the chain source." ) ,
@@ -683,6 +686,9 @@ impl NodeBuilder {
683686 /// Builds a [`Node`] instance with a [PostgreSQL] backend and according to the options
684687 /// previously configured.
685688 ///
689+ /// This acquires an exclusive lease for the selected KV table before reading persisted node
690+ /// state. Nodes may share a database when each node identity uses a distinct `kv_table_name`.
691+ ///
686692 /// Connects to the PostgreSQL database at the given `connection_string`, e.g.,
687693 /// `"postgres://user:password@localhost/ldk_db"`.
688694 ///
@@ -701,6 +707,9 @@ impl NodeBuilder {
701707 /// certificates (it does not replace them). If `certificate_pem` is `None`, connections
702708 /// will be unencrypted.
703709 ///
710+ /// Returns [`BuildError::NodeLeaseUnavailable`] while another process owns the selected KV
711+ /// table's lease.
712+ ///
704713 /// [PostgreSQL]: https://www.postgresql.org
705714 #[ cfg( feature = "postgres" ) ]
706715 pub fn build_with_postgres_store (
@@ -709,19 +718,29 @@ impl NodeBuilder {
709718 ) -> Result < Node , BuildError > {
710719 let logger = setup_logger ( & self . log_writer_config , & self . config ) ?;
711720 let runtime = self . setup_runtime ( & logger) ?;
712- let kv_store = runtime
713- . block_on ( io:: postgres_store:: PostgresStore :: new_with_logger (
714- connection_string,
715- db_name,
716- kv_table_name,
717- certificate_pem,
718- Some ( Arc :: clone ( & logger) ) ,
719- ) )
720- . map_err ( |e| {
721+ let kv_store = match runtime. block_on ( io:: postgres_store:: PostgresStore :: new_with_logger (
722+ connection_string,
723+ db_name,
724+ kv_table_name,
725+ certificate_pem,
726+ Some ( Arc :: clone ( & logger) ) ,
727+ ) ) {
728+ Ok ( kv_store) => kv_store,
729+ Err ( e) if e. kind ( ) == lightning:: io:: ErrorKind :: WouldBlock => {
730+ return Err ( BuildError :: NodeLeaseUnavailable ) ;
731+ } ,
732+ Err ( e) => {
721733 log_error ! ( logger, "Failed to set up Postgres store: {e}" ) ;
722- BuildError :: KVStoreSetupFailed
723- } ) ?;
724- self . build_with_store_runtime_and_logger ( node_entropy, kv_store, runtime, logger)
734+ return Err ( BuildError :: KVStoreSetupFailed ) ;
735+ } ,
736+ } ;
737+ let node_lease = kv_store. node_lease ( ) ;
738+ let mut node =
739+ self . build_with_store_runtime_and_logger ( node_entropy, kv_store, runtime, logger) ?;
740+ if !node. install_node_lease ( node_lease) {
741+ return Err ( BuildError :: NodeLeaseUnavailable ) ;
742+ }
743+ Ok ( node)
725744 }
726745
727746 /// Builds a [`Node`] instance with a [`FilesystemStoreV2`] backend and according to the options
@@ -1217,6 +1236,9 @@ impl ArcedNodeBuilder {
12171236 /// Builds a [`Node`] instance with a [PostgreSQL] backend and according to the options
12181237 /// previously configured.
12191238 ///
1239+ /// This acquires an exclusive lease for the selected KV table before reading persisted node
1240+ /// state. Nodes may share a database when each node identity uses a distinct `kv_table_name`.
1241+ ///
12201242 /// Connects to the PostgreSQL database at the given `connection_string`, e.g.,
12211243 /// `"postgres://user:password@localhost/ldk_db"`.
12221244 ///
@@ -1235,6 +1257,9 @@ impl ArcedNodeBuilder {
12351257 /// certificates (it does not replace them). If `certificate_pem` is `None`, connections
12361258 /// will be unencrypted.
12371259 ///
1260+ /// Returns [`BuildError::NodeLeaseUnavailable`] while another process owns the selected KV
1261+ /// table's lease.
1262+ ///
12381263 /// [PostgreSQL]: https://www.postgresql.org
12391264 #[ cfg( feature = "postgres" ) ]
12401265 pub fn build_with_postgres_store (
@@ -2335,6 +2360,7 @@ fn build_with_store_internal(
23352360 payment_store,
23362361 lnurl_auth,
23372362 is_running,
2363+ node_lease : None ,
23382364 node_metrics,
23392365 om_mailbox,
23402366 async_payments_role,
0 commit comments