Skip to content

Commit 090c9fc

Browse files
committed
Offload blocking wallet calls to spawn_blocking
BDK wallet operations (apply_update, create_funding_transaction, get_spendable_amount_sats) internally acquire a mutex and walk the checkpoint chain, giving O(T*C) runtime where T is the number of transactions and C is the number of checkpoints. Running these on a tokio worker thread blocks the entire async executor during sync-heavy workloads. Move these calls to spawn_blocking so they run on the tokio blocking threadpool instead. This is a stopgap until the underlying issue is addressed in BDK. JoinError (panic or runtime shutdown) is mapped into the existing error paths at each call site: WalletOperationFailed for esplora sync and funding tx creation, unwrap_or(0) for spendable balance checks (conservative default that skips channel opens).
1 parent 735bb99 commit 090c9fc

4 files changed

Lines changed: 65 additions & 17 deletions

File tree

src/chain/bitcoind.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,17 @@ impl BitcoindChainSource {
436436
let cur_height = channel_manager.current_best_block().height;
437437

438438
let now = SystemTime::now();
439-
let bdk_unconfirmed_txids = onchain_wallet.get_unconfirmed_txids();
439+
440+
let wallet_ref = onchain_wallet.clone();
441+
let bdk_unconfirmed_txids = tokio::task::spawn_blocking(move || {
442+
wallet_ref.get_unconfirmed_txids()
443+
})
444+
.await
445+
.map_err(|e| {
446+
log_error!(self.logger, "Failed to retrieve unconfirmed txids: {}", e);
447+
Error::WalletOperationFailed
448+
})?;
449+
440450
match self
441451
.api_client
442452
.get_updated_mempool_transactions(cur_height, bdk_unconfirmed_txids)
@@ -450,11 +460,18 @@ impl BitcoindChainSource {
450460
evicted_txids.len(),
451461
now.elapsed().unwrap().as_millis()
452462
);
453-
onchain_wallet.apply_mempool_txs(unconfirmed_txs, evicted_txids).unwrap_or_else(
454-
|e| {
455-
log_error!(self.logger, "Failed to apply mempool transactions: {:?}", e);
456-
},
457-
);
463+
464+
let apply_res = tokio::task::spawn_blocking(move || {
465+
onchain_wallet.apply_mempool_txs(unconfirmed_txs, evicted_txids)
466+
})
467+
.await
468+
.map_err(|e| {
469+
log_error!(self.logger, "Applying mempool transactions panicked: {}", e);
470+
Error::WalletOperationFailed
471+
})?;
472+
apply_res.unwrap_or_else(|e| {
473+
log_error!(self.logger, "Failed to apply mempool transactions: {:?}", e);
474+
});
458475
},
459476
Err(e) => {
460477
log_error!(self.logger, "Failed to poll for mempool transactions: {:?}", e);

src/chain/esplora.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,15 @@ impl EsploraChainSource {
111111
let now = Instant::now();
112112
match $sync_future.await {
113113
Ok(res) => match res {
114-
Ok(update) => match onchain_wallet.apply_update(update) {
114+
Ok(update) => {
115+
let w = Arc::clone(&onchain_wallet);
116+
let apply_res = tokio::task::spawn_blocking(move || w.apply_update(update))
117+
.await
118+
.map_err(|e| {
119+
log_error!(self.logger, "Failed to apply wallet update: {}", e);
120+
Error::WalletOperationFailed
121+
})?;
122+
match apply_res {
115123
Ok(()) => {
116124
log_info!(
117125
self.logger,
@@ -135,6 +143,7 @@ impl EsploraChainSource {
135143
Ok(())
136144
},
137145
Err(e) => Err(e),
146+
}
138147
},
139148
Err(e) => match *e {
140149
esplora_client::Error::Reqwest(he) => {

src/event.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -564,12 +564,18 @@ where
564564

565565
// Sign the final funding transaction and broadcast it.
566566
let channel_amount = Amount::from_sat(channel_value_satoshis);
567-
match self.wallet.create_funding_transaction(
568-
output_script,
569-
channel_amount,
570-
confirmation_target,
571-
locktime,
572-
) {
567+
let w = Arc::clone(&self.wallet);
568+
let funding_res = tokio::task::spawn_blocking(move || {
569+
w.create_funding_transaction(
570+
output_script, channel_amount, confirmation_target, locktime,
571+
)
572+
})
573+
.await
574+
.unwrap_or_else(|e| {
575+
log_error!(self.logger, "Failed to create funding transaction: {}", e);
576+
Err(Error::WalletOperationFailed)
577+
});
578+
match funding_res {
573579
Ok(final_tx) => {
574580
let needs_manual_broadcast =
575581
self.liquidity_source.as_ref().map_or(false, |ls| {

src/liquidity.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -879,8 +879,16 @@ where
879879
let channel_amount_sats = (amt_to_forward_msat + over_provisioning_msat) / 1000;
880880
let cur_anchor_reserve_sats =
881881
total_anchor_channels_reserve_sats(&self.channel_manager, &self.config);
882-
let spendable_amount_sats =
883-
self.wallet.get_spendable_amount_sats(cur_anchor_reserve_sats).unwrap_or(0);
882+
let w = Arc::clone(&self.wallet);
883+
let spendable_amount_sats = tokio::task::spawn_blocking(move || {
884+
w.get_spendable_amount_sats(cur_anchor_reserve_sats)
885+
})
886+
.await
887+
.unwrap_or_else(|e| {
888+
log_error!(self.logger, "Failed to get spendable amount: {}", e);
889+
Err(Error::WalletOperationFailed)
890+
})
891+
.unwrap_or(0);
884892
let required_funds_sats = channel_amount_sats
885893
+ self.config.anchor_channels_config.as_ref().map_or(0, |c| {
886894
if init_features.requires_anchors_zero_fee_htlc_tx()
@@ -1242,8 +1250,16 @@ where
12421250
}
12431251
let cur_anchor_reserve_sats =
12441252
total_anchor_channels_reserve_sats(&self.channel_manager, &self.config);
1245-
let spendable_amount_sats =
1246-
self.wallet.get_spendable_amount_sats(cur_anchor_reserve_sats).unwrap_or(0);
1253+
let w = Arc::clone(&self.wallet);
1254+
let spendable_amount_sats = tokio::task::spawn_blocking(move || {
1255+
w.get_spendable_amount_sats(cur_anchor_reserve_sats)
1256+
})
1257+
.await
1258+
.unwrap_or_else(|e| {
1259+
log_error!(self.logger, "Failed to get spendable amount: {}", e);
1260+
Err(Error::WalletOperationFailed)
1261+
})
1262+
.unwrap_or(0);
12471263
let required_funds_sats = channel_amount_sats
12481264
+ self.config.anchor_channels_config.as_ref().map_or(0, |c| {
12491265
if init_features.requires_anchors_zero_fee_htlc_tx()

0 commit comments

Comments
 (0)