Skip to content

Commit 28b526a

Browse files
committed
Speed up remove_stale_channels_and_tracking nontrivially
During startup, the lightning protocol forces us to fetch a ton of gossip for channels where there is a `channel_update` in only one direction. We then have to wait around a while until we can prune the crap cause we don't know when the gossip sync has completed. Sadly, doing a large prune via `remove_stale_channels_and_tracking` is somewhat slow. Removing a large portion of our graph currently takes a bit more than 7.5 seconds on an i9-14900K, which can ultimately ~hang a node with a few less GHz ~forever. The bulk of this time is in our `IndexedMap` removals, where we walk the entire `keys` `Vec` to remove the entry, then shift it down after removing. In the previous commit we shifted to a bulk removal model for channels, here we do the same for nodes. This reduces the same test to around 340 milliseconds on the same hardware.
1 parent cc028f4 commit 28b526a

File tree

2 files changed

+33
-5
lines changed

2 files changed

+33
-5
lines changed

lightning/src/routing/gossip.rs

Lines changed: 20 additions & 5 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};
@@ -2389,11 +2391,15 @@ where
23892391
let mut removed_channels_lck = self.removed_channels.lock().unwrap();
23902392

23912393
let channels_removed_bulk = channels.remove_fetch_bulk(&scids_to_remove);
2392-
removed_channels_lck.reserve(channels_removed_bulk.len());
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());
23932396
for (scid, info) in channels_removed_bulk {
2394-
self.remove_channel_in_nodes(&mut nodes, &info, scid);
2397+
self.remove_channel_in_nodes_callback(&mut nodes, &info, scid, |e| {
2398+
nodes_to_remove.insert(*e.key());
2399+
});
23952400
removed_channels_lck.insert(scid, Some(current_time_unix));
23962401
}
2402+
nodes.remove_bulk(&nodes_to_remove);
23972403
}
23982404

23992405
let should_keep_tracking = |time: &mut Option<u64>| {
@@ -2632,16 +2638,17 @@ where
26322638
Ok(())
26332639
}
26342640

2635-
fn remove_channel_in_nodes(
2641+
fn remove_channel_in_nodes_callback<RM: FnMut(IndexedMapOccupiedEntry<NodeId, NodeInfo>)>(
26362642
&self, nodes: &mut IndexedMap<NodeId, NodeInfo>, chan: &ChannelInfo, short_channel_id: u64,
2643+
mut remove_node: RM,
26372644
) {
26382645
macro_rules! remove_from_node {
26392646
($node_id: expr) => {
26402647
if let IndexedMapEntry::Occupied(mut entry) = nodes.entry($node_id) {
26412648
entry.get_mut().channels.retain(|chan_id| short_channel_id != *chan_id);
26422649
if entry.get().channels.is_empty() {
26432650
self.removed_node_counters.lock().unwrap().push(entry.get().node_counter);
2644-
entry.remove_entry();
2651+
remove_node(entry);
26452652
}
26462653
} else {
26472654
panic!(
@@ -2654,6 +2661,14 @@ where
26542661
remove_from_node!(chan.node_one);
26552662
remove_from_node!(chan.node_two);
26562663
}
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+
}
26572672
}
26582673

26592674
impl ReadOnlyNetworkGraph<'_> {

lightning/src/util/indexed_map.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
8484
res
8585
}
8686

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+
8795
/// Inserts the given `key`/`value` pair into the map, returning the element that was
8896
/// previously stored at the given `key`, if one exists.
8997
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
@@ -222,6 +230,11 @@ impl<'a, K: Hash + Ord, V> OccupiedEntry<'a, K, V> {
222230
res
223231
}
224232

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+
225238
/// Get a reference to the value at the position described by this entry.
226239
pub fn get(&self) -> &V {
227240
self.underlying_entry.get()

0 commit comments

Comments
 (0)