Skip to content

Commit 50391d3

Browse files
authored
Merge pull request #4080 from TheBlueMatt/2025-09-faster-prune
Speed up remove_stale_channels_and_tracking nontrivially
2 parents cd85a80 + 28b526a commit 50391d3

File tree

2 files changed

+52
-13
lines changed

2 files changed

+52
-13
lines changed

lightning/src/routing/gossip.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ use crate::ln::types::ChannelId;
3737
use crate::routing::utxo::{self, UtxoLookup, UtxoResolver};
3838
use crate::types::features::{ChannelFeatures, InitFeatures, NodeFeatures};
3939
use crate::types::string::PrintableString;
40-
use crate::util::indexed_map::{Entry as IndexedMapEntry, IndexedMap};
40+
use crate::util::indexed_map::{
41+
Entry as IndexedMapEntry, IndexedMap, OccupiedEntry as IndexedMapOccupiedEntry,
42+
};
4143
use crate::util::logger::{Level, Logger};
4244
use crate::util::scid_utils::{block_from_scid, scid_from_parts, MAX_SCID_BLOCK};
4345
use crate::util::ser::{MaybeReadable, Readable, ReadableArgs, RequiredWrapper, Writeable, Writer};
@@ -2356,9 +2358,7 @@ where
23562358
return;
23572359
}
23582360
let min_time_unix: u32 = (current_time_unix - STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS) as u32;
2359-
// Sadly BTreeMap::retain was only stabilized in 1.53 so we can't switch to it for some
2360-
// time.
2361-
let mut scids_to_remove = Vec::new();
2361+
let mut scids_to_remove = new_hash_set();
23622362
for (scid, info) in channels.unordered_iter_mut() {
23632363
if info.one_to_two.is_some()
23642364
&& info.one_to_two.as_ref().unwrap().last_update < min_time_unix
@@ -2382,19 +2382,24 @@ where
23822382
if announcement_received_timestamp < min_time_unix as u64 {
23832383
log_gossip!(self.logger, "Removing channel {} because both directional updates are missing and its announcement timestamp {} being below {}",
23842384
scid, announcement_received_timestamp, min_time_unix);
2385-
scids_to_remove.push(*scid);
2385+
scids_to_remove.insert(*scid);
23862386
}
23872387
}
23882388
}
23892389
if !scids_to_remove.is_empty() {
23902390
let mut nodes = self.nodes.write().unwrap();
2391-
for scid in scids_to_remove {
2392-
let info = channels
2393-
.remove(&scid)
2394-
.expect("We just accessed this scid, it should be present");
2395-
self.remove_channel_in_nodes(&mut nodes, &info, scid);
2396-
self.removed_channels.lock().unwrap().insert(scid, Some(current_time_unix));
2391+
let mut removed_channels_lck = self.removed_channels.lock().unwrap();
2392+
2393+
let channels_removed_bulk = channels.remove_fetch_bulk(&scids_to_remove);
2394+
self.removed_node_counters.lock().unwrap().reserve(channels_removed_bulk.len());
2395+
let mut nodes_to_remove = hash_set_with_capacity(channels_removed_bulk.len());
2396+
for (scid, info) in channels_removed_bulk {
2397+
self.remove_channel_in_nodes_callback(&mut nodes, &info, scid, |e| {
2398+
nodes_to_remove.insert(*e.key());
2399+
});
2400+
removed_channels_lck.insert(scid, Some(current_time_unix));
23972401
}
2402+
nodes.remove_bulk(&nodes_to_remove);
23982403
}
23992404

24002405
let should_keep_tracking = |time: &mut Option<u64>| {
@@ -2633,16 +2638,17 @@ where
26332638
Ok(())
26342639
}
26352640

2636-
fn remove_channel_in_nodes(
2641+
fn remove_channel_in_nodes_callback<RM: FnMut(IndexedMapOccupiedEntry<NodeId, NodeInfo>)>(
26372642
&self, nodes: &mut IndexedMap<NodeId, NodeInfo>, chan: &ChannelInfo, short_channel_id: u64,
2643+
mut remove_node: RM,
26382644
) {
26392645
macro_rules! remove_from_node {
26402646
($node_id: expr) => {
26412647
if let IndexedMapEntry::Occupied(mut entry) = nodes.entry($node_id) {
26422648
entry.get_mut().channels.retain(|chan_id| short_channel_id != *chan_id);
26432649
if entry.get().channels.is_empty() {
26442650
self.removed_node_counters.lock().unwrap().push(entry.get().node_counter);
2645-
entry.remove_entry();
2651+
remove_node(entry);
26462652
}
26472653
} else {
26482654
panic!(
@@ -2655,6 +2661,14 @@ where
26552661
remove_from_node!(chan.node_one);
26562662
remove_from_node!(chan.node_two);
26572663
}
2664+
2665+
fn remove_channel_in_nodes(
2666+
&self, nodes: &mut IndexedMap<NodeId, NodeInfo>, chan: &ChannelInfo, short_channel_id: u64,
2667+
) {
2668+
self.remove_channel_in_nodes_callback(nodes, chan, short_channel_id, |e| {
2669+
e.remove_entry();
2670+
});
2671+
}
26582672
}
26592673

26602674
impl ReadOnlyNetworkGraph<'_> {

lightning/src/util/indexed_map.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,26 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
7272
ret
7373
}
7474

75+
/// Removes elements with the given `keys` in bulk, returning the set of removed elements.
76+
pub fn remove_fetch_bulk(&mut self, keys: &HashSet<K>) -> Vec<(K, V)> {
77+
let mut res = Vec::with_capacity(keys.len());
78+
for key in keys.iter() {
79+
if let Some((k, v)) = self.map.remove_entry(key) {
80+
res.push((k, v));
81+
}
82+
}
83+
self.keys.retain(|k| !keys.contains(k));
84+
res
85+
}
86+
87+
/// Removes elements with the given `keys` in bulk.
88+
pub fn remove_bulk(&mut self, keys: &HashSet<K>) {
89+
for key in keys.iter() {
90+
self.map.remove(key);
91+
}
92+
self.keys.retain(|k| !keys.contains(k));
93+
}
94+
7595
/// Inserts the given `key`/`value` pair into the map, returning the element that was
7696
/// previously stored at the given `key`, if one exists.
7797
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
@@ -210,6 +230,11 @@ impl<'a, K: Hash + Ord, V> OccupiedEntry<'a, K, V> {
210230
res
211231
}
212232

233+
/// Get a reference to the key at the position described by this entry.
234+
pub fn key(&self) -> &K {
235+
self.underlying_entry.key()
236+
}
237+
213238
/// Get a reference to the value at the position described by this entry.
214239
pub fn get(&self) -> &V {
215240
self.underlying_entry.get()

0 commit comments

Comments
 (0)