Fallback on last hour performance for gateways

This commit is contained in:
Jon Häggblad
2023-09-01 12:58:04 +02:00
parent 58f79a972c
commit defbcea227
2 changed files with 75 additions and 29 deletions
@@ -1,4 +1,4 @@
use core::fmt;
use std::fmt;
use serde::{Deserialize, Serialize};
@@ -115,3 +115,9 @@ pub struct PagedResult<T> {
pub struct Gateway {
pub identity: String,
}
impl fmt::Display for Gateway {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Gateway({})", self.identity)
}
}
@@ -15,6 +15,7 @@ use nym_topology::gateway;
use nym_validator_client::nym_api::Client as ApiClient;
use std::str::FromStr;
use std::sync::Arc;
use tap::TapFallible;
use tokio::sync::RwLock;
use url::Url;
@@ -43,7 +44,6 @@ pub async fn get_services(
log::trace!("Fetching services");
let all_services_with_category = fetch_services(&privacy_level).await?;
log::trace!("Received: {:#?}", all_services_with_category);
// Flatten all services into a single vector (get rid of categories)
// We currently don't care about categories, but we might in the future...
@@ -51,34 +51,47 @@ pub async fn get_services(
.into_iter()
.flat_map(|sp| sp.items)
.collect_vec();
log::debug!("Received {} services", all_services.len());
log::trace!("Received: {:#?}", all_services);
// Early return if we're running with medium toggle enabled
if let PrivacyLevel::Medium = privacy_level {
return Ok(all_services);
}
// If there is a failure getting the active services, just return all of them
// TODO: get paged
log::trace!("Fetching active services");
let active_services = fetch_active_services().await?;
let Ok(active_services) = query_active_services().await else {
log::warn!("Using all services instead as fallback");
return Ok(all_services);
};
log::debug!(
"Received {} active services from harbourmaster",
active_services.items.len()
);
log::trace!("Active: {:#?}", active_services);
if active_services.items.is_empty() {
log::warn!("No active services found! Using all services instead as fallback");
return Ok(all_services);
}
log::trace!("Filter out inactive");
// From the list of all services, filter out the ones that are inactive
log::trace!("Filter out inactive and low performance");
let filtered_services = filter_out_inactive_services(&all_services, active_services);
log::trace!("After filtering: {:#?}", filtered_services);
if let Ok(services) = filtered_services {
Ok(services)
} else {
log::warn!(
"After filtering, no active services found! Using all services instead as fallback"
);
Ok(all_services)
}
// If there is a failure filtering out inactive services, just return all of them
filtered_services
.tap_ok(|services| {
log::debug!(
"After filtering out inactive and low performance: {}",
services.len()
);
log::trace!("After filtering: {:#?}", services);
})
.or_else(|_| {
// If for some reason harbourmaster is done, we want things to still sort of work.
log::warn!(
"After filtering, no active services found! Using all services instead as fallback"
);
Ok(all_services)
})
}
async fn fetch_services(privacy_level: &PrivacyLevel) -> Result<Vec<DirectoryService>> {
@@ -101,7 +114,7 @@ async fn fetch_services(privacy_level: &PrivacyLevel) -> Result<Vec<DirectorySer
}
}
async fn fetch_active_services() -> Result<PagedResult<HarbourMasterService>> {
async fn query_active_services() -> Result<PagedResult<HarbourMasterService>> {
let active_services = reqwest::get(HARBOUR_MASTER_URL)
.await?
.json::<PagedResult<HarbourMasterService>>()
@@ -145,7 +158,7 @@ async fn fetch_all_gateways() -> Result<Vec<GatewayBondAnnotated>> {
}
}
async fn fetch_compatible_gateways() -> Result<Vec<GatewayBondAnnotated>> {
async fn fetch_only_compatible_gateways() -> Result<Vec<GatewayBondAnnotated>> {
let gateways = fetch_all_gateways().await?;
let our_version = env!("CARGO_PKG_VERSION");
log::debug!(
@@ -168,18 +181,35 @@ async fn fetch_compatible_gateways() -> Result<Vec<GatewayBondAnnotated>> {
fn filter_out_low_performance_gateways(
gateways: Vec<GatewayBondAnnotated>,
) -> Result<Vec<GatewayBondAnnotated>> {
let gateways: Vec<_> = gateways
.into_iter()
let mut filtered_gateways: Vec<_> = gateways
.iter()
.filter(|g| {
g.node_performance.most_recent
> Percent::from_percentage_value(GATEWAY_PERFORMANCE_SCORE_THRESHOLD).unwrap()
})
.cloned()
.collect();
if gateways.is_empty() {
// Sometimes the most_recent is zero for all gateways (bug in nym-api?)
if filtered_gateways.is_empty() {
log::warn!(
"No gateways with recent performance score above threshold found! Using \
last hour performance scores instead as fallback"
);
filtered_gateways = gateways
.into_iter()
.filter(|g| {
g.node_performance.last_hour
> Percent::from_percentage_value(GATEWAY_PERFORMANCE_SCORE_THRESHOLD).unwrap()
})
.collect();
}
if filtered_gateways.is_empty() {
log::error!("No gateways found! (with high enough performance score)");
Err(BackendError::NoGatewayWithAcceptablePerformanceFound)
} else {
Ok(gateways)
Ok(filtered_gateways)
}
}
@@ -200,7 +230,8 @@ async fn select_gateway_by_latency(gateways: Vec<GatewayBondAnnotated>) -> Resul
#[tauri::command]
pub async fn get_gateways() -> Result<Vec<Gateway>> {
log::trace!("Fetching gateways");
let all_gateways = fetch_compatible_gateways().await?;
let all_gateways = fetch_only_compatible_gateways().await?;
log::debug!("Received {} gateways", all_gateways.len());
log::trace!("Received: {:#?}", all_gateways);
let gateways_filtered = filter_out_low_performance_gateways(all_gateways.clone())?
@@ -209,15 +240,24 @@ pub async fn get_gateways() -> Result<Vec<Gateway>> {
identity: g.identity().clone(),
})
.collect_vec();
log::trace!("Filtered: {:#?}", gateways_filtered);
log::debug!(
"After filtering out low-performance gateways: {}",
gateways_filtered.len()
);
log::trace!(
"Filtered: [\n\t{}\n]",
gateways_filtered.iter().join(",\n\t")
);
Ok(gateways_filtered)
}
// Lookup and select a single gateway with low latency.
#[tauri::command]
pub async fn get_gateway_with_low_latency() -> Result<Gateway> {
log::trace!("Fetching gateways");
let all_gateways = fetch_compatible_gateways().await?;
log::trace!("Fetching gateways with low latency");
let all_gateways = fetch_only_compatible_gateways().await?;
log::debug!("Received {} gateways", all_gateways.len());
log::trace!("Received: {:#?}", all_gateways);
let gateways_filtered = filter_out_low_performance_gateways(all_gateways)?;
@@ -233,7 +273,7 @@ pub async fn get_gateway_with_low_latency() -> Result<Gateway> {
pub async fn select_gateway_with_low_latency_from_list(gateways: Vec<Gateway>) -> Result<Gateway> {
log::debug!("Selecting a gateway with low latency");
let gateways = gateways.into_iter().map(|g| g.identity).collect_vec();
let all_gateways = fetch_compatible_gateways().await?;
let all_gateways = fetch_only_compatible_gateways().await?;
let gateways_union_set: Vec<GatewayBondAnnotated> = all_gateways
.into_iter()
.filter(|g| gateways.contains(g.identity()))