Skip route selection after unfeasible route update

If an unfeasible update is applied to an existing, unselected route, we
now skip route selection altogether. Since the route is not selected,
it can obviously not be unselected, and selecting it won't be possible
since its now not feasible.

By immediately returning we also can't notify downstream peers which
have sent a seqno request for this combination. This is fine, since if
there is an outstanding request for this seqno, considering it is
unfeasible, it won't be of interest to the downstream peer. Note that if
the seqno request triggered a bump of the route seqno, the _first_ update
is always feasible.

Also expose a metric value to track how often we just skip route
selection.

Closes #329

Signed-off-by: Lee Smet <lee.smet@hotmail.com>
This commit is contained in:
Lee Smet
2024-07-10 11:17:32 +02:00
parent 33730dbda9
commit 7682bc60c2
4 changed files with 30 additions and 2 deletions

View File

@@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
memory which is not currently used by the cache but still allocated.
- Demote seqno cache warnings about duplicate seqno requests go debug lvl, as it
is valid to send duplicate requests if sufficient time passed.
- Skip route selection after an unfeasible update to a fallback route, as the (now
unfeasible) route won't be selected anyway.
### Fixed
@@ -33,7 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Seqno request cache, to avoid spamming peers with duplicate seqno requests and
to make sure seqno's are forwarded to different peers.
- Added myceliumd-private binary, which contains private network functionality.
- Added API endpoint to retrieve the public key associated with an IP.
- Added API endpoint to retrieve the public key associated with an IP.
- The CLI can now be used to list, remove or add peers (see `mycelium peers --help`)
- The CLI can now be used to list selected and fallback routes (see `mycelium routes --help`)

View File

@@ -27,6 +27,7 @@ pub struct PrometheusExporter {
router_received_tlvs: IntCounter,
router_tlv_source_died: IntCounter,
router_propage_selected_peers_time_spent: IntCounter,
router_update_skipped_route_selection: IntCounter,
peer_manager_peer_added: IntCounterVec,
peer_manager_known_peers: IntGauge,
peer_manager_connection_attemps: IntCounterVec,
@@ -125,6 +126,11 @@ impl PrometheusExporter {
"Time spent in the propagate_selected_route task, which periodically announces selected routes to peers. Measurement is in nanoseconds",
)
.expect("Can register an int counter in default registry"),
router_update_skipped_route_selection: register_int_counter!(
"mycelium_router_update_skipped_route_selection",
"Updates which were processed but did not run the route selection step, because the updated route could not be selected anyway",
)
.expect("Can register an int counter in default registry"),
peer_manager_peer_added: register_int_counter_vec!(
opts!(
"mycelium_peer_manager_peers_added",
@@ -363,6 +369,11 @@ impl Metrics for PrometheusExporter {
.inc_by(duration.as_nanos() as u64)
}
#[inline]
fn router_update_skipped_route_selection(&self) {
self.router_update_skipped_route_selection.inc()
}
#[inline]
fn peer_manager_peer_added(&self, pt: mycelium::peer_manager::PeerType) {
let label = match pt {

View File

@@ -155,6 +155,10 @@ pub trait Metrics {
) {
}
/// An update was processed and accepted by the router, but did not run route selection.
#[inline]
fn router_update_skipped_route_selection(&self) {}
/// A new [`Peer`](crate::peer::Peer) was added to the
/// [`PeerManager`](crate::peer_manager::PeerManager) while it is running.
#[inline]

View File

@@ -972,9 +972,9 @@ where
.read()
.unwrap()
.is_update_feasible(&update);
// We load all routes here for the subnet. Because we hold the mutex for the
// writer, this view is accurate and we can't diverge until the mutex is released.
let mut routing_table_entries = {
if let Some(rte) = self.routing_table.routes_mut(subnet) {
rte
@@ -1032,6 +1032,16 @@ where
existing_entry.set_metric(metric);
existing_entry.set_router_id(router_id);
existing_entry.set_expires(tokio::time::Instant::now() + route_hold_time(&update));
// If the route is not selected, and the update is unfeasible, the route will not be
// selected by a subsequent route selection, so we can skip it and avoid wasting time
// here.
if !existing_entry.selected() && !update_feasible {
trace!("Ignoring route selection for unfeasible update to unselected route");
self.metrics.router_update_skipped_route_selection();
return;
}
// If the update is unfeasible the route must be unselected.
if existing_entry.selected() && !update_feasible {
existing_route_unselected = true;
@@ -1041,6 +1051,7 @@ where
// If there is no entry yet ignore unfeasible updates and retractions.
if metric.is_infinite() || !update_feasible {
debug!("Received unfeasible update | retraction for unknown route - neighbour");
self.metrics.router_update_skipped_route_selection();
return;
}