Parallelize `ChannelMonitorUpdate` loading
What changed, and why it matters
This commit is a performance optimization, not a security fix. It changes how Lightning node data is loaded from storage when the program starts up. Previously, certain update records were read one at a time; now they are read in parallel. The commit message and code changes only describe speed improvements and reducing network round trips. There is no indication of a vulnerability being fixed.
No security action required. Treat as a normal performance improvement during review or deployment.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch modifies lightning/src/util/persist.rs in the MonitorUpdatingPersister::read_monitor_and_updates path. It replaces a sequential loop that reads each ChannelMonitorUpdate with a parallel fetch using ResultFuture and MultiResultFuturePoller. The re-application of updates to the monitor remains unchanged. The commit message explicitly frames this as a latency/RTT optimization for async KVStore backends.
Changed components
lightning/src/util/persist.rsMonitorUpdatingPersister::read_monitor_and_updatesInspect captured patch +21 / −17
diff --git a/lightning/src/util/persist.rs b/lightning/src/util/persist.rs
index 3eedfc4..2e1e880 100644
--- a/lightning/src/util/persist.rs
+++ b/lightning/src/util/persist.rs
@@ -1106,23 +1106,27 @@ where
list_res?.into_iter().map(|name| UpdateName::new(name)).collect();
let mut updates = updates?;
updates.sort_unstable();
- // TODO: Parallelize this loop
- for update_name in updates {
- if update_name.0 > current_update_id {
- let update = self.read_monitor_update(monitor_key, &update_name).await?;
- monitor
- .update_monitor(&update, &self.broadcaster, &self.fee_estimator, &self.logger)
- .map_err(|e| {
- log_error!(
- self.logger,
- "Monitor update failed. monitor: {} update: {} reason: {:?}",
- monitor_key,
- update_name.as_str(),
- e
- );
- io::Error::new(io::ErrorKind::Other, "Monitor update failed")
- })?;
- }
+ let updates_to_load = updates.iter().filter(|update| update.0 > current_update_id);
+ let mut update_futures = Vec::with_capacity(updates_to_load.clone().count());
+ for update_name in updates_to_load {
+ update_futures.push(ResultFuture::Pending(Box::pin(async move {
+ (update_name, self.read_monitor_update(monitor_key, update_name).await)
+ })));
+ }
+ for (update_name, update_res) in MultiResultFuturePoller::new(update_futures).await {
+ let update = update_res?;
+ monitor
+ .update_monitor(&update, &self.broadcaster, &self.fee_estimator, &self.logger)
+ .map_err(|e| {
+ log_error!(
+ self.logger,
+ "Monitor update failed. monitor: {} update: {} reason: {:?}",
+ monitor_key,
+ update_name.as_str(),
+ e
+ );
+ io::Error::new(io::ErrorKind::Other, "Monitor update failed")
+ })?;
}
Ok(Some((block_hash, monitor)))
}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.