convert deferred stream processor to generic streams instead of flume channel

more aggressive relay reconnection
more discovery context improvement
This commit is contained in:
Christien Rioux 2024-09-25 21:28:30 -04:00
parent 55f07d7bcc
commit 545b646d8f
17 changed files with 240 additions and 63 deletions

4
Cargo.lock generated
View File

@ -6299,9 +6299,9 @@ dependencies = [
[[package]] [[package]]
name = "veilid-hashlink" name = "veilid-hashlink"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a3dabbda02cfe176635dcaa18a021416ff2eb4d0b47a913e3fdc7f62049d7b1" checksum = "2070d1d09dad90091d23e49743408f82f8874994dec5ae0a8d3689b061bba426"
dependencies = [ dependencies = [
"hashbrown 0.14.5", "hashbrown 0.14.5",
"serde", "serde",

View File

@ -88,7 +88,7 @@ enumset = { version = "1.1.3", features = ["serde"] }
keyvaluedb = "0.1.2" keyvaluedb = "0.1.2"
range-set-blaze = "0.1.16" range-set-blaze = "0.1.16"
weak-table = "0.3.2" weak-table = "0.3.2"
hashlink = { package = "veilid-hashlink", version = "0.1.0", features = [ hashlink = { package = "veilid-hashlink", version = "0.1.1", features = [
"serde_impl", "serde_impl",
] } ] }

View File

@ -55,6 +55,7 @@ struct ConnectionManagerInner {
async_processor_jh: Option<MustJoinHandle<()>>, async_processor_jh: Option<MustJoinHandle<()>>,
stop_source: Option<StopSource>, stop_source: Option<StopSource>,
protected_addresses: HashMap<SocketAddress, ProtectedAddress>, protected_addresses: HashMap<SocketAddress, ProtectedAddress>,
reconnection_processor: DeferredStreamProcessor,
} }
struct ConnectionManagerArc { struct ConnectionManagerArc {
@ -84,6 +85,7 @@ impl ConnectionManager {
stop_source: StopSource, stop_source: StopSource,
sender: flume::Sender<ConnectionManagerEvent>, sender: flume::Sender<ConnectionManagerEvent>,
async_processor_jh: MustJoinHandle<()>, async_processor_jh: MustJoinHandle<()>,
reconnection_processor: DeferredStreamProcessor,
) -> ConnectionManagerInner { ) -> ConnectionManagerInner {
ConnectionManagerInner { ConnectionManagerInner {
next_id: 0.into(), next_id: 0.into(),
@ -91,6 +93,7 @@ impl ConnectionManager {
sender, sender,
async_processor_jh: Some(async_processor_jh), async_processor_jh: Some(async_processor_jh),
protected_addresses: HashMap::new(), protected_addresses: HashMap::new(),
reconnection_processor,
} }
} }
fn new_arc(network_manager: NetworkManager) -> ConnectionManagerArc { fn new_arc(network_manager: NetworkManager) -> ConnectionManagerArc {
@ -133,11 +136,6 @@ impl ConnectionManager {
log_net!(debug "startup connection manager"); log_net!(debug "startup connection manager");
let mut inner = self.arc.inner.lock();
if inner.is_some() {
panic!("shouldn't start connection manager twice without shutting it down first");
}
// Create channel for async_processor to receive notifications of networking events // Create channel for async_processor to receive notifications of networking events
let (sender, receiver) = flume::unbounded(); let (sender, receiver) = flume::unbounded();
@ -150,8 +148,21 @@ impl ConnectionManager {
self.clone().async_processor(stop_source.token(), receiver), self.clone().async_processor(stop_source.token(), receiver),
); );
// Spawn the reconnection processor
let mut reconnection_processor = DeferredStreamProcessor::new();
reconnection_processor.init().await;
// Store in the inner object // Store in the inner object
*inner = Some(Self::new_inner(stop_source, sender, async_processor)); let mut inner = self.arc.inner.lock();
if inner.is_some() {
panic!("shouldn't start connection manager twice without shutting it down first");
}
*inner = Some(Self::new_inner(
stop_source,
sender,
async_processor,
reconnection_processor,
));
guard.success(); guard.success();
@ -175,7 +186,9 @@ impl ConnectionManager {
} }
} }
}; };
// Stop the reconnection processor
log_net!(debug "stopping reconnection processor task");
inner.reconnection_processor.terminate().await;
// Stop all the connections and the async processor // Stop all the connections and the async processor
log_net!(debug "stopping async processor task"); log_net!(debug "stopping async processor task");
drop(inner.stop_source.take()); drop(inner.stop_source.take());
@ -278,11 +291,12 @@ impl ConnectionManager {
&self, &self,
inner: &mut ConnectionManagerInner, inner: &mut ConnectionManagerInner,
prot_conn: ProtocolNetworkConnection, prot_conn: ProtocolNetworkConnection,
opt_dial_info: Option<DialInfo>,
) -> EyreResult<NetworkResult<ConnectionHandle>> { ) -> EyreResult<NetworkResult<ConnectionHandle>> {
// Get next connection id to use // Get next connection id to use
let id = inner.next_id; let id = inner.next_id;
inner.next_id += 1u64; inner.next_id += 1u64;
log_net!(debug log_net!(
"on_new_protocol_network_connection: id={} prot_conn={:?}", "on_new_protocol_network_connection: id={} prot_conn={:?}",
id, id,
prot_conn prot_conn
@ -294,7 +308,13 @@ impl ConnectionManager {
None => bail!("not creating connection because we are stopping"), None => bail!("not creating connection because we are stopping"),
}; };
let mut conn = NetworkConnection::from_protocol(self.clone(), stop_token, prot_conn, id); let mut conn = NetworkConnection::from_protocol(
self.clone(),
stop_token,
prot_conn,
id,
opt_dial_info,
);
let handle = conn.get_handle(); let handle = conn.get_handle();
// See if this should be a protected connection // See if this should be a protected connection
@ -396,7 +416,7 @@ impl ConnectionManager {
// Async lock on the remote address for atomicity per remote // Async lock on the remote address for atomicity per remote
let _lock_guard = self.arc.address_lock_table.lock_tag(remote_addr).await; let _lock_guard = self.arc.address_lock_table.lock_tag(remote_addr).await;
log_net!(debug "== get_or_create_connection dial_info={:?}", dial_info); log_net!("== get_or_create_connection dial_info={:?}", dial_info);
// If any connection to this remote exists that has the same protocol, return it // If any connection to this remote exists that has the same protocol, return it
// Any connection will do, we don't have to match the local address but if we can // Any connection will do, we don't have to match the local address but if we can
@ -406,7 +426,7 @@ impl ConnectionManager {
.connection_table .connection_table
.get_best_connection_by_remote(best_port, peer_address) .get_best_connection_by_remote(best_port, peer_address)
{ {
log_net!(debug log_net!(
"== Returning best existing connection {:?}", "== Returning best existing connection {:?}",
best_existing_conn best_existing_conn
); );
@ -468,7 +488,7 @@ impl ConnectionManager {
} }
}; };
self.on_new_protocol_network_connection(inner, prot_conn) self.on_new_protocol_network_connection(inner, prot_conn, Some(dial_info))
} }
/////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////
@ -502,7 +522,7 @@ impl ConnectionManager {
// We don't care if this fails, since nobody here asked for the inbound connection. // We don't care if this fails, since nobody here asked for the inbound connection.
// If it does, we just drop the connection // If it does, we just drop the connection
let _ = self.on_new_protocol_network_connection(inner, prot_conn); let _ = self.on_new_protocol_network_connection(inner, prot_conn, None);
} }
None => { None => {
// If this somehow happens, we're shutting down // If this somehow happens, we're shutting down
@ -598,6 +618,9 @@ impl ConnectionManager {
// See if we've had more than the threshold number of drops in the last span // See if we've had more than the threshold number of drops in the last span
let cur_ts = Timestamp::now(); let cur_ts = Timestamp::now();
let duration = cur_ts.saturating_sub(pa.span_start_ts); let duration = cur_ts.saturating_sub(pa.span_start_ts);
let mut reconnect = true;
if duration < PROTECTED_CONNECTION_DROP_SPAN { if duration < PROTECTED_CONNECTION_DROP_SPAN {
pa.drops_in_span += 1; pa.drops_in_span += 1;
log_net!(debug "== Protected connection dropped (count={}): {} -> {} for node {}", pa.drops_in_span, conn.connection_id(), conn.debug_print(Timestamp::now()), protect_nr); log_net!(debug "== Protected connection dropped (count={}): {} -> {} for node {}", pa.drops_in_span, conn.connection_id(), conn.debug_print(Timestamp::now()), protect_nr);
@ -605,6 +628,7 @@ impl ConnectionManager {
if pa.drops_in_span >= PROTECTED_CONNECTION_DROP_COUNT { if pa.drops_in_span >= PROTECTED_CONNECTION_DROP_COUNT {
// Consider this as a failure to send if we've dropped the connection too many times in a single timespan // Consider this as a failure to send if we've dropped the connection too many times in a single timespan
protect_nr.report_protected_connection_dropped(); protect_nr.report_protected_connection_dropped();
reconnect = false;
// Reset the drop counter // Reset the drop counter
pa.drops_in_span = 0; pa.drops_in_span = 0;
@ -618,6 +642,15 @@ impl ConnectionManager {
log_net!(debug "== Protected connection dropped (count={}): {} -> {} for node {}", pa.drops_in_span, conn.connection_id(), conn.debug_print(Timestamp::now()), protect_nr); log_net!(debug "== Protected connection dropped (count={}): {} -> {} for node {}", pa.drops_in_span, conn.connection_id(), conn.debug_print(Timestamp::now()), protect_nr);
} }
// Reconnect the protected connection immediately
if reconnect {
if let Some(dial_info) = conn.dial_info() {
self.spawn_reconnector_inner(inner, dial_info);
} else {
log_net!(debug "Can't reconnect to accepted protected connection: {} -> {} for node {}", conn.connection_id(), conn.debug_print(Timestamp::now()), protect_nr);
}
}
break; break;
} }
} }
@ -627,6 +660,30 @@ impl ConnectionManager {
} }
} }
fn spawn_reconnector_inner(&self, inner: &mut ConnectionManagerInner, dial_info: DialInfo) {
let this = self.clone();
inner.reconnection_processor.add(
Box::pin(futures_util::stream::once(async { dial_info })),
move |dial_info| {
let this = this.clone();
Box::pin(async move {
match this.get_or_create_connection(dial_info.clone()).await {
Ok(NetworkResult::Value(conn)) => {
log_net!(debug "Reconnection successful to {}: {:?}", dial_info,conn);
}
Ok(res) => {
log_net!(debug "Reconnection unsuccessful to {}: {:?}", dial_info, res);
}
Err(e) => {
log_net!(debug "Reconnection error to {}: {}", dial_info, e);
}
}
false
})
},
);
}
pub async fn debug_print(&self) -> String { pub async fn debug_print(&self) -> String {
//let inner = self.arc.inner.lock(); //let inner = self.arc.inner.lock();
format!( format!(

View File

@ -1210,7 +1210,7 @@ impl NetworkManager {
} }
// Report peer info changes // Report peer info changes
pub fn report_peer_info_change(&self, peer_info: Arc<PeerInfo>) { pub fn report_peer_info_change(&mut self, peer_info: Arc<PeerInfo>) {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if let Some(address_check) = inner.address_check.as_mut() { if let Some(address_check) = inner.address_check.as_mut() {
address_check.report_peer_info_change(peer_info); address_check.report_peer_info_change(peer_info);

View File

@ -199,7 +199,6 @@ impl DiscoveryContext {
} }
// For each peer, ask them for our public address, filtering on desired dial info // For each peer, ask them for our public address, filtering on desired dial info
let mut unord = FuturesUnordered::new();
let get_public_address_func = |node: NodeRef| { let get_public_address_func = |node: NodeRef| {
let this = self.clone(); let this = self.clone();
@ -225,12 +224,12 @@ impl DiscoveryContext {
}; };
let mut external_address_infos = Vec::new(); let mut external_address_infos = Vec::new();
let mut unord = FuturesUnordered::new();
for node in nodes.iter().take(nodes.len() - 1).cloned() { for node in nodes.iter().cloned() {
let gpa_future = get_public_address_func(node); let gpa_future = get_public_address_func(node);
unord.push(gpa_future); unord.push(gpa_future);
// Always process two at a time so we get both addresses in parallel if possible // Always process N at a time so we get all addresses in parallel if possible
if unord.len() == EXTERNAL_INFO_VALIDATIONS { if unord.len() == EXTERNAL_INFO_VALIDATIONS {
// Process one // Process one
if let Some(Some(ei)) = unord.next().in_current_span().await { if let Some(Some(ei)) = unord.next().in_current_span().await {
@ -280,10 +279,10 @@ impl DiscoveryContext {
{ {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
inner.external_info = external_address_infos; inner.external_info = external_address_infos;
log_net!(debug "external_info ({:?}:{:?})[{}]", log_net!(debug "External Addresses: ({:?}:{:?})[{}]",
self.unlocked_inner.protocol_type, self.unlocked_inner.protocol_type,
self.unlocked_inner.address_type, self.unlocked_inner.address_type,
inner.external_info.iter().map(|x| format!("{}",x.address)).collect::<Vec<_>>().join(", ")); inner.external_info.iter().map(|x| format!("{} <- {}",x.address, x.node)).collect::<Vec<_>>().join(", "));
} }
true true

View File

@ -30,6 +30,10 @@ use std::path::{Path, PathBuf};
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
pub const UPDATE_NETWORK_CLASS_TASK_TICK_PERIOD_SECS: u32 = 1;
pub const NETWORK_INTERFACES_TASK_TICK_PERIOD_SECS: u32 = 1;
pub const UPNP_TASK_TICK_PERIOD_SECS: u32 = 1;
pub const PEEK_DETECT_LEN: usize = 64; pub const PEEK_DETECT_LEN: usize = 64;
cfg_if! { cfg_if! {
@ -168,9 +172,15 @@ impl Network {
routing_table, routing_table,
connection_manager, connection_manager,
interfaces: NetworkInterfaces::new(), interfaces: NetworkInterfaces::new(),
update_network_class_task: TickTask::new("update_network_class_task", 1), update_network_class_task: TickTask::new(
network_interfaces_task: TickTask::new("network_interfaces_task", 1), "update_network_class_task",
upnp_task: TickTask::new("upnp_task", 1), UPDATE_NETWORK_CLASS_TASK_TICK_PERIOD_SECS,
),
network_interfaces_task: TickTask::new(
"network_interfaces_task",
NETWORK_INTERFACES_TASK_TICK_PERIOD_SECS,
),
upnp_task: TickTask::new("upnp_task", UPNP_TASK_TICK_PERIOD_SECS),
network_task_lock: AsyncMutex::new(()), network_task_lock: AsyncMutex::new(()),
igd_manager: igd_manager::IGDManager::new(config.clone()), igd_manager: igd_manager::IGDManager::new(config.clone()),
} }

View File

@ -14,16 +14,15 @@ impl Network {
let _guard = self.unlocked_inner.network_task_lock.lock().await; let _guard = self.unlocked_inner.network_task_lock.lock().await;
// Do the public dial info check // Do the public dial info check
let out = self.do_public_dial_info_check(stop_token, l, t).await; let finished = self.do_public_dial_info_check(stop_token, l, t).await?;
// Done with public dial info check // Done with public dial info check
{ if finished {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
inner.needs_public_dial_info_check = false; inner.needs_public_dial_info_check = false;
inner.public_dial_info_check_punishment = None;
} }
out Ok(())
} }
#[instrument(level = "trace", skip(self, editor), err)] #[instrument(level = "trace", skip(self, editor), err)]
@ -54,7 +53,7 @@ impl Network {
stop_token: StopToken, stop_token: StopToken,
_l: Timestamp, _l: Timestamp,
_t: Timestamp, _t: Timestamp,
) -> EyreResult<()> { ) -> EyreResult<bool> {
// Figure out if we can optimize TCP/WS checking since they are often on the same port // Figure out if we can optimize TCP/WS checking since they are often on the same port
let (protocol_config, inbound_protocol_map) = { let (protocol_config, inbound_protocol_map) = {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
@ -105,7 +104,7 @@ impl Network {
.into_iter() .into_iter()
.collect(); .collect();
// Set most permissive network config // Set most permissive network config and start from scratch
let mut editor = self.routing_table().edit_public_internet_routing_domain(); let mut editor = self.routing_table().edit_public_internet_routing_domain();
editor.setup_network( editor.setup_network(
protocol_config.outbound, protocol_config.outbound,
@ -113,11 +112,9 @@ impl Network {
protocol_config.family_global, protocol_config.family_global,
protocol_config.public_internet_capabilities.clone(), protocol_config.public_internet_capabilities.clone(),
); );
editor.commit(true).await;
// Start from scratch
editor.clear_dial_info_details(None, None); editor.clear_dial_info_details(None, None);
editor.set_network_class(None); editor.set_network_class(None);
editor.commit(true).await;
// Process all protocol and address combinations // Process all protocol and address combinations
let mut unord = FuturesUnordered::new(); let mut unord = FuturesUnordered::new();
@ -125,20 +122,15 @@ impl Network {
for ((at, _llpt, port), protocols) in &inbound_protocol_map { for ((at, _llpt, port), protocols) in &inbound_protocol_map {
let first_pt = protocols.first().unwrap(); let first_pt = protocols.first().unwrap();
let discovery_context = DiscoveryContext::new( let discovery_context =
self.routing_table(), DiscoveryContext::new(self.routing_table(), self.clone(), *first_pt, *at, *port);
self.clone(),
*first_pt,
*at,
*port,
// clear_network_callback.clone(),
);
discovery_context.discover(&mut unord).await; discovery_context.discover(&mut unord).await;
} }
// Wait for all discovery futures to complete and apply discoverycontexts // Wait for all discovery futures to complete and apply discoverycontexts
let mut all_address_types = AddressTypeSet::new(); let mut all_address_types = AddressTypeSet::new();
let mut force_outbound_only = false; let mut force_outbound_only = false;
let mut success = true;
loop { loop {
match unord match unord
.next() .next()
@ -181,7 +173,8 @@ impl Network {
} }
} }
Ok(Some(None)) => { Ok(Some(None)) => {
// Found no new dial info for this protocol/address combination // Found no dial info for this protocol/address combination
success = false;
} }
Ok(None) => { Ok(None) => {
// All done, normally // All done, normally
@ -189,11 +182,16 @@ impl Network {
} }
Err(_) => { Err(_) => {
// Stop token, exit early without error propagation // Stop token, exit early without error propagation
return Ok(()); return Ok(true);
} }
} }
} }
if !success {
log_net!(debug "Network class discovery failed, trying again");
return Ok(false);
}
// All done // All done
log_net!(debug "Network class discovery finished with address_types {:?}", all_address_types); log_net!(debug "Network class discovery finished with address_types {:?}", all_address_types);
@ -230,7 +228,7 @@ impl Network {
} }
} }
Ok(()) Ok(true)
} }
/// Make a dialinfo from an address and protocol type /// Make a dialinfo from an address and protocol type

View File

@ -82,16 +82,29 @@ pub struct NetworkConnectionStats {
last_message_recv_time: Option<Timestamp>, last_message_recv_time: Option<Timestamp>,
} }
/// Represents a connection in the connection table for connection-oriented protocols
#[derive(Debug)] #[derive(Debug)]
pub(in crate::network_manager) struct NetworkConnection { pub(in crate::network_manager) struct NetworkConnection {
/// A unique id for this connection
connection_id: NetworkConnectionId, connection_id: NetworkConnectionId,
/// The dial info used to make this connection if it was made with 'connect'
/// None if the connection was 'accepted'
opt_dial_info: Option<DialInfo>,
/// The network flow 5-tuple this connection is over
flow: Flow, flow: Flow,
/// Each connection has a processor and this is the task we wait for to ensure it exits cleanly
processor: Option<MustJoinHandle<()>>, processor: Option<MustJoinHandle<()>>,
/// When this connection was connected or accepted
established_time: Timestamp, established_time: Timestamp,
/// Statistics about network traffic
stats: Arc<Mutex<NetworkConnectionStats>>, stats: Arc<Mutex<NetworkConnectionStats>>,
/// To send data out this connection, it is places in this channel
sender: flume::Sender<(Option<Id>, Vec<u8>)>, sender: flume::Sender<(Option<Id>, Vec<u8>)>,
/// Drop this when we want to drop the connection
stop_source: Option<StopSource>, stop_source: Option<StopSource>,
/// The node we are responsible for protecting the connection for if it is protected
protected_nr: Option<NodeRef>, protected_nr: Option<NodeRef>,
/// The number of references to the network connection that exist (handles)
ref_count: usize, ref_count: usize,
} }
@ -110,6 +123,7 @@ impl NetworkConnection {
Self { Self {
connection_id: id, connection_id: id,
opt_dial_info: None,
flow, flow,
processor: None, processor: None,
established_time: Timestamp::now(), established_time: Timestamp::now(),
@ -129,6 +143,7 @@ impl NetworkConnection {
manager_stop_token: StopToken, manager_stop_token: StopToken,
protocol_connection: ProtocolNetworkConnection, protocol_connection: ProtocolNetworkConnection,
connection_id: NetworkConnectionId, connection_id: NetworkConnectionId,
opt_dial_info: Option<DialInfo>,
) -> Self { ) -> Self {
// Get flow // Get flow
let flow = protocol_connection.flow(); let flow = protocol_connection.flow();
@ -164,6 +179,7 @@ impl NetworkConnection {
// Return the connection // Return the connection
Self { Self {
connection_id, connection_id,
opt_dial_info,
flow, flow,
processor: Some(processor), processor: Some(processor),
established_time: Timestamp::now(), established_time: Timestamp::now(),
@ -183,6 +199,10 @@ impl NetworkConnection {
self.flow self.flow
} }
pub fn dial_info(&self) -> Option<DialInfo> {
self.opt_dial_info.clone()
}
#[expect(dead_code)] #[expect(dead_code)]
pub fn unique_flow(&self) -> UniqueFlow { pub fn unique_flow(&self) -> UniqueFlow {
UniqueFlow { UniqueFlow {

View File

@ -230,6 +230,64 @@ impl RoutingTable {
out out
} }
pub(crate) fn debug_info_entries_fastest(
&self,
min_state: BucketEntryState,
capabilities: Vec<FourCC>,
node_count: usize,
) -> String {
let cur_ts = Timestamp::now();
let mut filters = VecDeque::new();
filters.push_front(
Box::new(|rti: &RoutingTableInner, e: Option<Arc<BucketEntry>>| {
let Some(e) = e else {
return false;
};
let cap_match = e.with(rti, |_rti, e| {
e.has_all_capabilities(RoutingDomain::PublicInternet, &capabilities)
});
let state = e.with(rti, |_rti, e| e.state(cur_ts));
state >= min_state && cap_match
}) as RoutingTableEntryFilter,
);
let nodes = self.find_preferred_fastest_nodes(
node_count,
filters,
|_rti, entry: Option<Arc<BucketEntry>>| {
NodeRef::new(self.clone(), entry.unwrap().clone())
},
);
let mut out = String::new();
for node in nodes {
out += &node.operate(|_rti, e| {
let state_reason = e.state_reason(cur_ts);
format!(
" {} [{}] {} [{}]\n",
node,
Self::format_state_reason(state_reason),
e.peer_stats()
.latency
.as_ref()
.map(|l| {
format!("{:.2}ms", timestamp_to_secs(l.average.as_u64()) * 1000.0)
})
.unwrap_or_else(|| "???.??ms".to_string()),
if let Some(ni) = e.node_info(RoutingDomain::PublicInternet) {
ni.capabilities()
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(",")
} else {
"???".to_owned()
}
)
});
}
out
}
pub(crate) fn debug_info_entry(&self, node_ref: NodeRef) -> String { pub(crate) fn debug_info_entry(&self, node_ref: NodeRef) -> String {
let cur_ts = Timestamp::now(); let cur_ts = Timestamp::now();

View File

@ -2,6 +2,7 @@ use super::*;
use futures_util::stream::{FuturesUnordered, StreamExt}; use futures_util::stream::{FuturesUnordered, StreamExt};
use futures_util::FutureExt; use futures_util::FutureExt;
use stop_token::future::FutureExt as _;
const BACKGROUND_SAFETY_ROUTE_COUNT: usize = 2; const BACKGROUND_SAFETY_ROUTE_COUNT: usize = 2;
@ -102,10 +103,10 @@ impl RoutingTable {
} }
/// Test set of routes and remove the ones that don't test clean /// Test set of routes and remove the ones that don't test clean
#[instrument(level = "trace", skip(self, _stop_token), err)] #[instrument(level = "trace", skip(self, stop_token), err)]
async fn test_route_set( async fn test_route_set(
&self, &self,
_stop_token: StopToken, stop_token: StopToken,
routes_needing_testing: Vec<RouteId>, routes_needing_testing: Vec<RouteId>,
) -> EyreResult<()> { ) -> EyreResult<()> {
if routes_needing_testing.is_empty() { if routes_needing_testing.is_empty() {
@ -152,7 +153,7 @@ impl RoutingTable {
} }
// Wait for test_route futures to complete in parallel // Wait for test_route futures to complete in parallel
while unord.next().await.is_some() {} while let Ok(Some(())) = unord.next().timeout_at(stop_token.clone()).await {}
} }
// Process failed routes // Process failed routes

View File

@ -1,12 +1,18 @@
use super::*; use super::*;
// Keep member order appropriate for sorting < preference // Keep member order appropriate for sorting < preference
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize)]
pub struct DialInfoDetail { pub struct DialInfoDetail {
pub class: DialInfoClass, pub class: DialInfoClass,
pub dial_info: DialInfo, pub dial_info: DialInfo,
} }
impl fmt::Debug for DialInfoDetail {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}:{}", self.class, self.dial_info)
}
}
impl MatchesDialInfoFilter for DialInfoDetail { impl MatchesDialInfoFilter for DialInfoDetail {
fn matches_filter(&self, filter: &DialInfoFilter) -> bool { fn matches_filter(&self, filter: &DialInfoFilter) -> bool {
self.dial_info.matches_filter(filter) self.dial_info.matches_filter(filter)

View File

@ -15,7 +15,7 @@ pub const CAP_BLOCKSTORE: Capability = FourCC(*b"BLOC");
pub const DISTANCE_METRIC_CAPABILITIES: &[Capability] = &[CAP_DHT, CAP_DHT_WATCH]; pub const DISTANCE_METRIC_CAPABILITIES: &[Capability] = &[CAP_DHT, CAP_DHT_WATCH];
#[derive(Clone, Default, PartialEq, Eq, Debug, Serialize, Deserialize)] #[derive(Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct NodeInfo { pub struct NodeInfo {
network_class: NetworkClass, network_class: NetworkClass,
outbound_protocols: ProtocolTypeSet, outbound_protocols: ProtocolTypeSet,
@ -26,6 +26,24 @@ pub struct NodeInfo {
dial_info_detail_list: Vec<DialInfoDetail>, dial_info_detail_list: Vec<DialInfoDetail>,
} }
impl fmt::Debug for NodeInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// xxx: use field_with once that is on stable. trying to make this structure use fewer log lines when pretty printed
f.debug_struct("NodeInfo")
.field("network_class", &self.network_class)
.field(
"outbound_protocols",
&format!("{:?}", &self.outbound_protocols),
)
.field("address_types", &format!("{:?}", &self.address_types))
.field("envelope_support", &format!("{:?}", &self.envelope_support))
.field("crypto_support", &format!("{:?}", &self.crypto_support))
.field("capabilities", &format!("{:?}", &self.capabilities))
.field("dial_info_detail_list", &self.dial_info_detail_list)
.finish()
}
}
impl NodeInfo { impl NodeInfo {
pub fn new( pub fn new(
network_class: NetworkClass, network_class: NetworkClass,

View File

@ -743,6 +743,7 @@ impl StorageManagerInner {
receiver: flume::Receiver<T>, receiver: flume::Receiver<T>,
handler: impl FnMut(T) -> SendPinBoxFuture<bool> + Send + 'static, handler: impl FnMut(T) -> SendPinBoxFuture<bool> + Send + 'static,
) -> bool { ) -> bool {
self.deferred_result_processor.add(receiver, handler) self.deferred_result_processor
.add(receiver.into_stream(), handler)
} }
} }

View File

@ -204,9 +204,7 @@ impl StorageManager {
} }
} }
std::collections::hash_map::Entry::Vacant(_) => { std::collections::hash_map::Entry::Vacant(_) => {
panic!( warn!("offline write work items should always be on offline_subkey_writes entries that exist: ignoring key {}", result.key)
"offline write work items should always be on offline_subkey_writes entries that exist"
)
} }
} }

View File

@ -610,9 +610,12 @@ impl VeilidAPI {
let mut min_state = BucketEntryState::Unreliable; let mut min_state = BucketEntryState::Unreliable;
let mut capabilities = vec![]; let mut capabilities = vec![];
let mut fastest = false;
for arg in args { for arg in args {
if let Some(ms) = get_bucket_entry_state(&arg) { if let Some(ms) = get_bucket_entry_state(&arg) {
min_state = ms; min_state = ms;
} else if arg == "fastest" {
fastest = true;
} else { } else {
for cap in arg.split(',') { for cap in arg.split(',') {
if let Ok(capfcc) = FourCC::from_str(cap) { if let Ok(capfcc) = FourCC::from_str(cap) {
@ -626,7 +629,10 @@ impl VeilidAPI {
// Dump routing table entries // Dump routing table entries
let routing_table = self.network_manager()?.routing_table(); let routing_table = self.network_manager()?.routing_table();
Ok(routing_table.debug_info_entries(min_state, capabilities)) Ok(match fastest {
true => routing_table.debug_info_entries_fastest(min_state, capabilities, 100000),
false => routing_table.debug_info_entries(min_state, capabilities),
})
} }
async fn debug_entry(&self, args: String) -> VeilidAPIResult<String> { async fn debug_entry(&self, args: String) -> VeilidAPIResult<String> {

View File

@ -398,10 +398,12 @@ async def test_dht_integration_writer_reader():
for desc0 in records: for desc0 in records:
while True: while True:
rr = await rc0.inspect_dht_record(desc0.key, []) rr = await rc0.inspect_dht_record(desc0.key, [])
if len(rr.offline_subkeys) == 0: left = len(rr.offline_subkeys)
if left == 0:
await rc0.close_dht_record(desc0.key) await rc0.close_dht_record(desc0.key)
break break
time.sleep(1) print(f' {left} left')
time.sleep(0.1)
# read dht records on server 1 # read dht records on server 1
print(f'reading {COUNT} records') print(f'reading {COUNT} records')
@ -455,13 +457,15 @@ async def test_dht_write_read_local():
print(f' {n}') print(f' {n}')
print(f'syncing records to the network') print('syncing records to the network')
for desc0 in records: for desc0 in records:
while True: while True:
rr = await rc0.inspect_dht_record(desc0.key, []) rr = await rc0.inspect_dht_record(desc0.key, [])
if len(rr.offline_subkeys) == 0: left = len(rr.offline_subkeys)
if left == 0:
await rc0.close_dht_record(desc0.key) await rc0.close_dht_record(desc0.key)
break break
print(f' {left} left')
time.sleep(0.1) time.sleep(0.1)
# read dht records on server 0 # read dht records on server 0

View File

@ -9,6 +9,7 @@ use super::*;
/// Background processor for streams /// Background processor for streams
/// Handles streams to completion, passing each item from the stream to a callback /// Handles streams to completion, passing each item from the stream to a callback
#[derive(Debug)]
pub struct DeferredStreamProcessor { pub struct DeferredStreamProcessor {
pub opt_deferred_stream_channel: Option<flume::Sender<SendPinBoxFuture<()>>>, pub opt_deferred_stream_channel: Option<flume::Sender<SendPinBoxFuture<()>>>,
pub opt_stopper: Option<StopSource>, pub opt_stopper: Option<StopSource>,
@ -98,9 +99,9 @@ impl DeferredStreamProcessor {
/// * 'handler' is the callback to handle each item from the stream /// * 'handler' is the callback to handle each item from the stream
/// ///
/// Returns 'true' if the stream was added for processing, and 'false' if the stream could not be added, possibly due to not being initialized. /// Returns 'true' if the stream was added for processing, and 'false' if the stream could not be added, possibly due to not being initialized.
pub fn add<T: Send + 'static>( pub fn add<T: Send + 'static, S: futures_util::Stream<Item = T> + Unpin + Send + 'static>(
&mut self, &mut self,
receiver: flume::Receiver<T>, mut receiver: S,
mut handler: impl FnMut(T) -> SendPinBoxFuture<bool> + Send + 'static, mut handler: impl FnMut(T) -> SendPinBoxFuture<bool> + Send + 'static,
) -> bool { ) -> bool {
let Some(st) = self.opt_stopper.as_ref().map(|s| s.token()) else { let Some(st) = self.opt_stopper.as_ref().map(|s| s.token()) else {
@ -110,7 +111,7 @@ impl DeferredStreamProcessor {
return false; return false;
}; };
let drp = Box::pin(async move { let drp = Box::pin(async move {
while let Ok(Ok(res)) = receiver.recv_async().timeout_at(st.clone()).await { while let Ok(Some(res)) = receiver.next().timeout_at(st.clone()).await {
if !handler(res).await { if !handler(res).await {
break; break;
} }