Skip to content

Commit 2c1682e

Browse files
committed
f Keep bitcoind background sync alive
Wait for an active manual sync and retry initial ownership so startup races do not permanently stop background polling. Co-Authored-By: HAL 9000
1 parent 7d60fd5 commit 2c1682e

1 file changed

Lines changed: 63 additions & 9 deletions

File tree

src/chain/bitcoind.rs

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,31 @@ const CHAIN_POLLING_TIMEOUT_SECS: u64 = 10;
5252
type BitcoindSpvClient =
5353
SpvClient<ChainPoller<Arc<BitcoindClient>, BitcoindClient>, Arc<ChainListener>>;
5454

55+
async fn acquire_initial_wallet_sync_guard<'a>(
56+
wallet_polling_status: &'a Mutex<WalletSyncStatus>,
57+
stop_sync_receiver: &mut tokio::sync::watch::Receiver<()>,
58+
) -> Option<WalletSyncGuard<'a>> {
59+
loop {
60+
let mut pending_sync = {
61+
let mut status_lock = wallet_polling_status.lock().expect("lock");
62+
match status_lock.register_or_subscribe_pending_sync() {
63+
Some(pending_sync) => pending_sync,
64+
None => {
65+
return Some(WalletSyncGuard::new(
66+
wallet_polling_status,
67+
Error::WalletOperationFailed,
68+
));
69+
},
70+
}
71+
};
72+
tokio::select! {
73+
biased;
74+
_ = stop_sync_receiver.changed() => return None,
75+
_ = pending_sync.recv() => {},
76+
}
77+
}
78+
}
79+
5580
pub(super) struct BitcoindChainSource {
5681
api_client: Arc<BitcoindClient>,
5782
spv_client: tokio::sync::Mutex<Option<BitcoindSpvClient>>,
@@ -160,13 +185,12 @@ impl BitcoindChainSource {
160185
) {
161186
// First register for the wallet polling status to make sure `Node::sync_wallets` calls
162187
// wait on the result before proceeding.
163-
let initial_sync_guard = {
164-
let mut status_lock = self.wallet_polling_status.lock().expect("lock");
165-
if status_lock.register_or_subscribe_pending_sync().is_some() {
166-
debug_assert!(false, "Sync already in progress. This should never happen.");
167-
return;
168-
}
169-
WalletSyncGuard::new(&self.wallet_polling_status, Error::WalletOperationFailed)
188+
let Some(initial_sync_guard) =
189+
acquire_initial_wallet_sync_guard(&self.wallet_polling_status, &mut stop_sync_receiver)
190+
.await
191+
else {
192+
log_trace!(self.logger, "Stopping initial chain sync.");
193+
return;
170194
};
171195

172196
log_info!(
@@ -1592,6 +1616,9 @@ impl std::error::Error for BitcoindClientError {}
15921616

15931617
#[cfg(test)]
15941618
mod tests {
1619+
use std::sync::Mutex;
1620+
use std::time::Duration;
1621+
15951622
use bitcoin::hashes::Hash;
15961623
use bitcoin::{FeeRate, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Txid, Witness};
15971624
use lightning_block_sync::http::JsonResponse;
@@ -1601,9 +1628,36 @@ mod tests {
16011628
use serde_json::json;
16021629

16031630
use crate::chain::bitcoind::{
1604-
FeeResponse, GetMempoolEntryResponse, GetRawMempoolResponse, GetRawTransactionResponse,
1605-
MempoolMinFeeResponse,
1631+
acquire_initial_wallet_sync_guard, FeeResponse, GetMempoolEntryResponse,
1632+
GetRawMempoolResponse, GetRawTransactionResponse, MempoolMinFeeResponse,
16061633
};
1634+
use crate::chain::{WalletSyncGuard, WalletSyncStatus};
1635+
use crate::Error;
1636+
1637+
#[tokio::test]
1638+
async fn initial_sync_waits_for_in_progress_sync() {
1639+
let status = Mutex::new(WalletSyncStatus::Completed);
1640+
assert!(status.lock().expect("lock").register_or_subscribe_pending_sync().is_none());
1641+
let in_progress_guard = WalletSyncGuard::new(&status, Error::WalletOperationFailed);
1642+
let (_stop_sender, mut stop_receiver) = tokio::sync::watch::channel(());
1643+
let mut acquire_guard =
1644+
Box::pin(acquire_initial_wallet_sync_guard(&status, &mut stop_receiver));
1645+
1646+
let early_result =
1647+
tokio::time::timeout(Duration::from_millis(10), acquire_guard.as_mut()).await;
1648+
assert!(early_result.is_err(), "background sync should wait for the active sync");
1649+
1650+
in_progress_guard.complete(Ok(()));
1651+
let acquired_guard = tokio::time::timeout(Duration::from_secs(1), acquire_guard)
1652+
.await
1653+
.expect("background sync should resume")
1654+
.expect("background sync should acquire the sync guard");
1655+
assert!(
1656+
matches!(*status.lock().expect("lock"), WalletSyncStatus::InProgress { .. }),
1657+
"background sync should own the next sync"
1658+
);
1659+
acquired_guard.complete(Ok(()));
1660+
}
16071661

16081662
prop_compose! {
16091663
fn arbitrary_witness()(

0 commit comments

Comments
 (0)