Fallback on last hour performance for gateways
This commit is contained in:
@@ -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()))
|
||||
|
||||
Reference in New Issue
Block a user