From 66f41d8780b592b82448fea73e44aaeea0f0b388 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Thu, 12 Oct 2023 16:40:33 +0200 Subject: [PATCH] Code move --- .../src/net/routing/{ => dispatcher}/face.rs | 70 +- zenoh/src/net/routing/dispatcher/mod.rs | 24 + zenoh/src/net/routing/dispatcher/pubsub.rs | 785 +++++++++ zenoh/src/net/routing/dispatcher/queries.rs | 1076 ++++++++++++ .../net/routing/{ => dispatcher}/resource.rs | 124 +- zenoh/src/net/routing/dispatcher/tables.rs | 316 ++++ zenoh/src/net/routing/hat/mod.rs | 195 +++ zenoh/src/net/routing/{ => hat}/network.rs | 4 +- zenoh/src/net/routing/{ => hat}/pubsub.rs | 830 +-------- zenoh/src/net/routing/{ => hat}/queries.rs | 1123 +----------- zenoh/src/net/routing/mod.rs | 7 +- zenoh/src/net/routing/router.rs | 545 +----- zenoh/src/net/runtime/adminspace.rs | 8 +- zenoh/src/net/runtime/mod.rs | 4 +- zenoh/src/net/tests/tables.rs | 1504 ++++++++--------- zenoh/src/session.rs | 2 +- 16 files changed, 3412 insertions(+), 3205 deletions(-) rename zenoh/src/net/routing/{ => dispatcher}/face.rs (90%) create mode 100644 zenoh/src/net/routing/dispatcher/mod.rs create mode 100644 zenoh/src/net/routing/dispatcher/pubsub.rs create mode 100644 zenoh/src/net/routing/dispatcher/queries.rs rename zenoh/src/net/routing/{ => dispatcher}/resource.rs (88%) create mode 100644 zenoh/src/net/routing/dispatcher/tables.rs create mode 100644 zenoh/src/net/routing/hat/mod.rs rename zenoh/src/net/routing/{ => hat}/network.rs (99%) rename zenoh/src/net/routing/{ => hat}/pubsub.rs (58%) rename zenoh/src/net/routing/{ => hat}/queries.rs (53%) diff --git a/zenoh/src/net/routing/face.rs b/zenoh/src/net/routing/dispatcher/face.rs similarity index 90% rename from zenoh/src/net/routing/face.rs rename to zenoh/src/net/routing/dispatcher/face.rs index d84f173d26..17bf398bc1 100644 --- a/zenoh/src/net/routing/face.rs +++ b/zenoh/src/net/routing/dispatcher/face.rs @@ -11,7 +11,9 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::router::*; +use super::super::router::*; +use super::tables::{Tables, TablesLock}; +use super::{resource::*, tables}; use std::collections::{HashMap, HashSet}; use std::fmt; use std::sync::Arc; @@ -28,26 +30,26 @@ use zenoh_transport::stats::TransportStats; use zenoh_transport::{Primitives, TransportMulticast}; pub struct FaceState { - pub(super) id: usize, - pub(super) zid: ZenohId, - pub(super) whatami: WhatAmI, + pub(crate) id: usize, + pub(crate) zid: ZenohId, + pub(crate) whatami: WhatAmI, #[cfg(feature = "stats")] - pub(super) stats: Option>, - pub(super) primitives: Arc, - pub(super) link_id: usize, - pub(super) local_mappings: HashMap>, - pub(super) remote_mappings: HashMap>, - pub(super) local_subs: HashSet>, - pub(super) remote_subs: HashSet>, - pub(super) local_qabls: HashMap, QueryableInfo>, - pub(super) remote_qabls: HashSet>, - pub(super) next_qid: RequestId, - pub(super) pending_queries: HashMap>, - pub(super) mcast_group: Option, + pub(crate) stats: Option>, + pub(crate) primitives: Arc, + pub(crate) link_id: usize, + pub(crate) local_mappings: HashMap>, + pub(crate) remote_mappings: HashMap>, + pub(crate) local_subs: HashSet>, + pub(crate) remote_subs: HashSet>, + pub(crate) local_qabls: HashMap, QueryableInfo>, + pub(crate) remote_qabls: HashSet>, + pub(crate) next_qid: RequestId, + pub(crate) pending_queries: HashMap>, + pub(crate) mcast_group: Option, } impl FaceState { - pub(super) fn new( + pub(crate) fn new( id: usize, zid: ZenohId, whatami: WhatAmI, @@ -78,7 +80,7 @@ impl FaceState { #[inline] #[allow(clippy::trivially_copy_pass_by_ref)] - pub(super) fn get_mapping( + pub(crate) fn get_mapping( &self, prefixid: &ExprId, mapping: Mapping, @@ -89,7 +91,7 @@ impl FaceState { } } - pub(super) fn get_next_local_id(&self) -> ExprId { + pub(crate) fn get_next_local_id(&self) -> ExprId { let mut id = 1; while self.local_mappings.get(&id).is_some() || self.remote_mappings.get(&id).is_some() { id += 1; @@ -97,8 +99,14 @@ impl FaceState { id } - pub(super) fn get_router(&self, tables: &Tables, nodeid: &u64) -> Option { - match tables.routers_net.as_ref().unwrap().get_link(self.link_id) { + pub(crate) fn get_router(&self, tables: &Tables, nodeid: &u64) -> Option { + match tables + .hat + .routers_net + .as_ref() + .unwrap() + .get_link(self.link_id) + { Some(link) => match link.get_zid(nodeid) { Some(router) => Some(*router), None => { @@ -119,8 +127,14 @@ impl FaceState { } } - pub(super) fn get_peer(&self, tables: &Tables, nodeid: &u64) -> Option { - match tables.peers_net.as_ref().unwrap().get_link(self.link_id) { + pub(crate) fn get_peer(&self, tables: &Tables, nodeid: &u64) -> Option { + match tables + .hat + .peers_net + .as_ref() + .unwrap() + .get_link(self.link_id) + { Some(link) => match link.get_zid(nodeid) { Some(router) => Some(*router), None => { @@ -185,7 +199,7 @@ impl Primitives for Face { (WhatAmI::Router, WhatAmI::Peer) | (WhatAmI::Peer, WhatAmI::Router) | (WhatAmI::Peer, WhatAmI::Peer) => { - if rtables.full_net(WhatAmI::Peer) { + if rtables.hat.full_net(WhatAmI::Peer) { if let Some(peer) = self .state .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) @@ -238,7 +252,7 @@ impl Primitives for Face { (WhatAmI::Router, WhatAmI::Peer) | (WhatAmI::Peer, WhatAmI::Router) | (WhatAmI::Peer, WhatAmI::Peer) => { - if rtables.full_net(WhatAmI::Peer) { + if rtables.hat.full_net(WhatAmI::Peer) { if let Some(peer) = self .state .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) @@ -289,7 +303,7 @@ impl Primitives for Face { (WhatAmI::Router, WhatAmI::Peer) | (WhatAmI::Peer, WhatAmI::Router) | (WhatAmI::Peer, WhatAmI::Peer) => { - if rtables.full_net(WhatAmI::Peer) { + if rtables.hat.full_net(WhatAmI::Peer) { if let Some(peer) = self .state .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) @@ -342,7 +356,7 @@ impl Primitives for Face { (WhatAmI::Router, WhatAmI::Peer) | (WhatAmI::Peer, WhatAmI::Router) | (WhatAmI::Peer, WhatAmI::Peer) => { - if rtables.full_net(WhatAmI::Peer) { + if rtables.hat.full_net(WhatAmI::Peer) { if let Some(peer) = self .state .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) @@ -432,7 +446,7 @@ impl Primitives for Face { } fn send_close(&self) { - super::router::close_face(&self.tables, &Arc::downgrade(&self.state)); + tables::close_face(&self.tables, &Arc::downgrade(&self.state)); } } diff --git a/zenoh/src/net/routing/dispatcher/mod.rs b/zenoh/src/net/routing/dispatcher/mod.rs new file mode 100644 index 0000000000..53c32fb5ff --- /dev/null +++ b/zenoh/src/net/routing/dispatcher/mod.rs @@ -0,0 +1,24 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +//! ⚠️ WARNING ⚠️ +//! +//! This module is intended for Zenoh's internal use. +//! +//! [Click here for Zenoh's documentation](../zenoh/index.html) +pub mod face; +pub mod pubsub; +pub mod queries; +pub mod resource; +pub mod tables; diff --git a/zenoh/src/net/routing/dispatcher/pubsub.rs b/zenoh/src/net/routing/dispatcher/pubsub.rs new file mode 100644 index 0000000000..277ed45843 --- /dev/null +++ b/zenoh/src/net/routing/dispatcher/pubsub.rs @@ -0,0 +1,785 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use super::super::hat::network::Network; +use super::face::FaceState; +use super::resource::{DataRoutes, Direction, PullCaches, Resource, Route}; +use super::tables::{RoutingExpr, Tables}; +use petgraph::graph::NodeIndex; +use std::borrow::Cow; +use std::collections::{HashMap, HashSet}; +use std::convert::TryFrom; +use std::sync::Arc; +use std::sync::RwLock; +use zenoh_core::zread; +use zenoh_protocol::{ + core::{key_expr::OwnedKeyExpr, WhatAmI, WireExpr, ZenohId}, + network::{ + declare::{ext, Mode}, + Push, + }, + zenoh::PushBody, +}; +use zenoh_sync::get_mut_unchecked; + +#[inline] +fn insert_faces_for_subs( + route: &mut Route, + expr: &RoutingExpr, + tables: &Tables, + net: &Network, + source: usize, + subs: &HashSet, +) { + if net.trees.len() > source { + for sub in subs { + if let Some(sub_idx) = net.get_idx(sub) { + if net.trees[source].directions.len() > sub_idx.index() { + if let Some(direction) = net.trees[source].directions[sub_idx.index()] { + if net.graph.contains_node(direction) { + if let Some(face) = tables.get_face(&net.graph[direction].zid) { + route.entry(face.id).or_insert_with(|| { + let key_expr = + Resource::get_best_key(expr.prefix, expr.suffix, face.id); + ( + face.clone(), + key_expr.to_owned(), + if source != 0 { + Some(source as u16) + } else { + None + }, + ) + }); + } + } + } + } + } + } + } else { + log::trace!("Tree for node sid:{} not yet ready", source); + } +} + +fn compute_data_route( + tables: &Tables, + expr: &mut RoutingExpr, + source: Option, + source_type: WhatAmI, +) -> Arc { + let mut route = HashMap::new(); + let key_expr = expr.full_expr(); + if key_expr.ends_with('/') { + return Arc::new(route); + } + log::trace!( + "compute_data_route({}, {:?}, {:?})", + key_expr, + source, + source_type + ); + let key_expr = match OwnedKeyExpr::try_from(key_expr) { + Ok(ke) => ke, + Err(e) => { + log::warn!("Invalid KE reached the system: {}", e); + return Arc::new(route); + } + }; + let res = Resource::get_resource(expr.prefix, expr.suffix); + let matches = res + .as_ref() + .and_then(|res| res.context.as_ref()) + .map(|ctx| Cow::from(&ctx.matches)) + .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &key_expr))); + + let master = tables.whatami != WhatAmI::Router + || !tables.hat.full_net(WhatAmI::Peer) + || *tables + .hat + .elect_router(&tables.zid, &key_expr, tables.hat.shared_nodes.iter()) + == tables.zid; + + for mres in matches.iter() { + let mres = mres.upgrade().unwrap(); + if tables.whatami == WhatAmI::Router { + if master || source_type == WhatAmI::Router { + let net = tables.hat.routers_net.as_ref().unwrap(); + let router_source = match source_type { + WhatAmI::Router => source.unwrap(), + _ => net.idx.index(), + }; + insert_faces_for_subs( + &mut route, + expr, + tables, + net, + router_source, + &mres.context().router_subs, + ); + } + + if (master || source_type != WhatAmI::Router) && tables.hat.full_net(WhatAmI::Peer) { + let net = tables.hat.peers_net.as_ref().unwrap(); + let peer_source = match source_type { + WhatAmI::Peer => source.unwrap(), + _ => net.idx.index(), + }; + insert_faces_for_subs( + &mut route, + expr, + tables, + net, + peer_source, + &mres.context().peer_subs, + ); + } + } + + if tables.whatami == WhatAmI::Peer && tables.hat.full_net(WhatAmI::Peer) { + let net = tables.hat.peers_net.as_ref().unwrap(); + let peer_source = match source_type { + WhatAmI::Router | WhatAmI::Peer => source.unwrap(), + _ => net.idx.index(), + }; + insert_faces_for_subs( + &mut route, + expr, + tables, + net, + peer_source, + &mres.context().peer_subs, + ); + } + + if tables.whatami != WhatAmI::Router || master || source_type == WhatAmI::Router { + for (sid, context) in &mres.session_ctxs { + if let Some(subinfo) = &context.subs { + if match tables.whatami { + WhatAmI::Router => context.face.whatami != WhatAmI::Router, + _ => { + source_type == WhatAmI::Client + || context.face.whatami == WhatAmI::Client + } + } && subinfo.mode == Mode::Push + { + route.entry(*sid).or_insert_with(|| { + let key_expr = Resource::get_best_key(expr.prefix, expr.suffix, *sid); + (context.face.clone(), key_expr.to_owned(), None) + }); + } + } + } + } + } + for mcast_group in &tables.mcast_groups { + route.insert( + mcast_group.id, + ( + mcast_group.clone(), + expr.full_expr().to_string().into(), + None, + ), + ); + } + Arc::new(route) +} + +fn compute_matching_pulls(tables: &Tables, expr: &mut RoutingExpr) -> Arc { + let mut pull_caches = vec![]; + let ke = if let Ok(ke) = OwnedKeyExpr::try_from(expr.full_expr()) { + ke + } else { + return Arc::new(pull_caches); + }; + let res = Resource::get_resource(expr.prefix, expr.suffix); + let matches = res + .as_ref() + .and_then(|res| res.context.as_ref()) + .map(|ctx| Cow::from(&ctx.matches)) + .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &ke))); + + for mres in matches.iter() { + let mres = mres.upgrade().unwrap(); + for context in mres.session_ctxs.values() { + if let Some(subinfo) = &context.subs { + if subinfo.mode == Mode::Pull { + pull_caches.push(context.clone()); + } + } + } + } + Arc::new(pull_caches) +} + +pub(crate) fn compute_data_routes_(tables: &Tables, res: &Arc) -> DataRoutes { + let mut routes = DataRoutes { + matching_pulls: None, + routers_data_routes: vec![], + peers_data_routes: vec![], + peer_data_route: None, + client_data_route: None, + }; + let mut expr = RoutingExpr::new(res, ""); + if tables.whatami == WhatAmI::Router { + let indexes = tables + .hat + .routers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + routes + .routers_data_routes + .resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); + + for idx in &indexes { + routes.routers_data_routes[idx.index()] = + compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); + } + + routes.peer_data_route = Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) + && tables.hat.full_net(WhatAmI::Peer) + { + let indexes = tables + .hat + .peers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + routes + .peers_data_routes + .resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); + + for idx in &indexes { + routes.peers_data_routes[idx.index()] = + compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); + } + } + if tables.whatami == WhatAmI::Peer && !tables.hat.full_net(WhatAmI::Peer) { + routes.client_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); + routes.peer_data_route = Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if tables.whatami == WhatAmI::Client { + routes.client_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); + } + routes.matching_pulls = Some(compute_matching_pulls(tables, &mut expr)); + routes +} + +pub(crate) fn compute_data_routes(tables: &mut Tables, res: &mut Arc) { + if res.context.is_some() { + let mut res_mut = res.clone(); + let res_mut = get_mut_unchecked(&mut res_mut); + let mut expr = RoutingExpr::new(res, ""); + if tables.whatami == WhatAmI::Router { + let indexes = tables + .hat + .routers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + let routers_data_routes = &mut res_mut.context_mut().routers_data_routes; + routers_data_routes.clear(); + routers_data_routes.resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); + + for idx in &indexes { + routers_data_routes[idx.index()] = + compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); + } + + res_mut.context_mut().peer_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) + && tables.hat.full_net(WhatAmI::Peer) + { + let indexes = tables + .hat + .peers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + let peers_data_routes = &mut res_mut.context_mut().peers_data_routes; + peers_data_routes.clear(); + peers_data_routes.resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); + + for idx in &indexes { + peers_data_routes[idx.index()] = + compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); + } + } + if tables.whatami == WhatAmI::Peer && !tables.hat.full_net(WhatAmI::Peer) { + res_mut.context_mut().client_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); + res_mut.context_mut().peer_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if tables.whatami == WhatAmI::Client { + res_mut.context_mut().client_data_route = + Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); + } + res_mut.context_mut().matching_pulls = compute_matching_pulls(tables, &mut expr); + } +} + +pub(crate) fn compute_data_routes_from(tables: &mut Tables, res: &mut Arc) { + compute_data_routes(tables, res); + let res = get_mut_unchecked(res); + for child in res.childs.values_mut() { + compute_data_routes_from(tables, child); + } +} + +pub(crate) fn compute_matches_data_routes_<'a>( + tables: &'a Tables, + res: &'a Arc, +) -> Vec<(Arc, DataRoutes)> { + let mut routes = vec![]; + if res.context.is_some() { + routes.push((res.clone(), compute_data_routes_(tables, res))); + for match_ in &res.context().matches { + let match_ = match_.upgrade().unwrap(); + if !Arc::ptr_eq(&match_, res) { + let match_routes = compute_data_routes_(tables, &match_); + routes.push((match_, match_routes)); + } + } + } + routes +} + +pub(crate) fn disable_matches_data_routes(_tables: &mut Tables, res: &mut Arc) { + if res.context.is_some() { + get_mut_unchecked(res).context_mut().valid_data_routes = false; + for match_ in &res.context().matches { + let mut match_ = match_.upgrade().unwrap(); + if !Arc::ptr_eq(&match_, res) { + get_mut_unchecked(&mut match_) + .context_mut() + .valid_data_routes = false; + } + } + } +} + +macro_rules! treat_timestamp { + ($hlc:expr, $payload:expr, $drop:expr) => { + // if an HLC was configured (via Config.add_timestamp), + // check DataInfo and add a timestamp if there isn't + if let Some(hlc) = $hlc { + if let PushBody::Put(data) = &mut $payload { + if let Some(ref ts) = data.timestamp { + // Timestamp is present; update HLC with it (possibly raising error if delta exceed) + match hlc.update_with_timestamp(ts) { + Ok(()) => (), + Err(e) => { + if $drop { + log::error!( + "Error treating timestamp for received Data ({}). Drop it!", + e + ); + return; + } else { + data.timestamp = Some(hlc.new_timestamp()); + log::error!( + "Error treating timestamp for received Data ({}). Replace timestamp: {:?}", + e, + data.timestamp); + } + } + } + } else { + // Timestamp not present; add one + data.timestamp = Some(hlc.new_timestamp()); + log::trace!("Adding timestamp to DataInfo: {:?}", data.timestamp); + } + } + } + } +} + +#[inline] +fn get_data_route( + tables: &Tables, + face: &FaceState, + res: &Option>, + expr: &mut RoutingExpr, + routing_context: u64, +) -> Arc { + match tables.whatami { + WhatAmI::Router => match face.whatami { + WhatAmI::Router => { + let routers_net = tables.hat.routers_net.as_ref().unwrap(); + let local_context = routers_net.get_local_context(routing_context, face.link_id); + res.as_ref() + .and_then(|res| res.routers_data_route(local_context)) + .unwrap_or_else(|| { + compute_data_route(tables, expr, Some(local_context), face.whatami) + }) + } + WhatAmI::Peer => { + if tables.hat.full_net(WhatAmI::Peer) { + let peers_net = tables.hat.peers_net.as_ref().unwrap(); + let local_context = peers_net.get_local_context(routing_context, face.link_id); + res.as_ref() + .and_then(|res| res.peers_data_route(local_context)) + .unwrap_or_else(|| { + compute_data_route(tables, expr, Some(local_context), face.whatami) + }) + } else { + res.as_ref() + .and_then(|res| res.peer_data_route()) + .unwrap_or_else(|| compute_data_route(tables, expr, None, face.whatami)) + } + } + _ => res + .as_ref() + .and_then(|res| res.routers_data_route(0)) + .unwrap_or_else(|| compute_data_route(tables, expr, None, face.whatami)), + }, + WhatAmI::Peer => { + if tables.hat.full_net(WhatAmI::Peer) { + match face.whatami { + WhatAmI::Router | WhatAmI::Peer => { + let peers_net = tables.hat.peers_net.as_ref().unwrap(); + let local_context = + peers_net.get_local_context(routing_context, face.link_id); + res.as_ref() + .and_then(|res| res.peers_data_route(local_context)) + .unwrap_or_else(|| { + compute_data_route(tables, expr, Some(local_context), face.whatami) + }) + } + _ => res + .as_ref() + .and_then(|res| res.peers_data_route(0)) + .unwrap_or_else(|| compute_data_route(tables, expr, None, face.whatami)), + } + } else { + res.as_ref() + .and_then(|res| match face.whatami { + WhatAmI::Client => res.client_data_route(), + _ => res.peer_data_route(), + }) + .unwrap_or_else(|| compute_data_route(tables, expr, None, face.whatami)) + } + } + _ => res + .as_ref() + .and_then(|res| res.client_data_route()) + .unwrap_or_else(|| compute_data_route(tables, expr, None, face.whatami)), + } +} + +#[inline] +fn get_matching_pulls( + tables: &Tables, + res: &Option>, + expr: &mut RoutingExpr, +) -> Arc { + res.as_ref() + .and_then(|res| res.context.as_ref()) + .map(|ctx| ctx.matching_pulls.clone()) + .unwrap_or_else(|| compute_matching_pulls(tables, expr)) +} + +macro_rules! cache_data { + ( + $matching_pulls:expr, + $expr:expr, + $payload:expr + ) => { + for context in $matching_pulls.iter() { + get_mut_unchecked(&mut context.clone()) + .last_values + .insert($expr.full_expr().to_string(), $payload.clone()); + } + }; +} + +#[inline] +fn should_route( + tables: &Tables, + src_face: &FaceState, + outface: &Arc, + expr: &mut RoutingExpr, +) -> bool { + if src_face.id != outface.id + && match (src_face.mcast_group.as_ref(), outface.mcast_group.as_ref()) { + (Some(l), Some(r)) => l != r, + _ => true, + } + { + let dst_master = tables.whatami != WhatAmI::Router + || outface.whatami != WhatAmI::Peer + || tables.hat.peers_net.is_none() + || tables.zid + == *tables.hat.elect_router( + &tables.zid, + expr.full_expr(), + tables.hat.get_router_links(outface.zid), + ); + + return dst_master + && (src_face.whatami != WhatAmI::Peer + || outface.whatami != WhatAmI::Peer + || tables.hat.full_net(WhatAmI::Peer) + || tables.hat.failover_brokering(src_face.zid, outface.zid)); + } + false +} + +#[cfg(feature = "stats")] +macro_rules! inc_stats { + ( + $face:expr, + $txrx:ident, + $space:ident, + $body:expr + ) => { + paste::paste! { + if let Some(stats) = $face.stats.as_ref() { + use zenoh_buffers::SplitBuffer; + match &$body { + PushBody::Put(p) => { + stats.[<$txrx _z_put_msgs>].[](1); + stats.[<$txrx _z_put_pl_bytes>].[](p.payload.len()); + } + PushBody::Del(_) => { + stats.[<$txrx _z_del_msgs>].[](1); + } + } + } + } + }; +} + +#[allow(clippy::too_many_arguments)] +pub fn full_reentrant_route_data( + tables_ref: &RwLock, + face: &FaceState, + expr: &WireExpr, + ext_qos: ext::QoSType, + mut payload: PushBody, + routing_context: u64, +) { + let tables = zread!(tables_ref); + match tables.get_mapping(face, &expr.scope, expr.mapping).cloned() { + Some(prefix) => { + log::trace!( + "Route data for res {}{}", + prefix.expr(), + expr.suffix.as_ref() + ); + let mut expr = RoutingExpr::new(&prefix, expr.suffix.as_ref()); + + #[cfg(feature = "stats")] + let admin = expr.full_expr().starts_with("@/"); + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, rx, user, payload) + } else { + inc_stats!(face, rx, admin, payload) + } + + if tables.whatami != WhatAmI::Router + || face.whatami != WhatAmI::Peer + || tables.hat.peers_net.is_none() + || tables.zid + == *tables.hat.elect_router( + &tables.zid, + expr.full_expr(), + tables.hat.get_router_links(face.zid), + ) + { + let res = Resource::get_resource(&prefix, expr.suffix); + let route = get_data_route(&tables, face, &res, &mut expr, routing_context); + let matching_pulls = get_matching_pulls(&tables, &res, &mut expr); + + if !(route.is_empty() && matching_pulls.is_empty()) { + treat_timestamp!(&tables.hlc, payload, tables.drop_future_timestamp); + + if route.len() == 1 && matching_pulls.len() == 0 { + let (outface, key_expr, context) = route.values().next().unwrap(); + if should_route(&tables, face, outface, &mut expr) { + drop(tables); + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, tx, user, payload) + } else { + inc_stats!(face, tx, admin, payload) + } + + outface.primitives.send_push(Push { + wire_expr: key_expr.into(), + ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + payload, + }) + } + } else { + if !matching_pulls.is_empty() { + let lock = zlock!(tables.pull_caches_lock); + cache_data!(matching_pulls, expr, payload); + drop(lock); + } + + if tables.whatami == WhatAmI::Router { + let route = route + .values() + .filter(|(outface, _key_expr, _context)| { + should_route(&tables, face, outface, &mut expr) + }) + .cloned() + .collect::>(); + + drop(tables); + for (outface, key_expr, context) in route { + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, tx, user, payload) + } else { + inc_stats!(face, tx, admin, payload) + } + + outface.primitives.send_push(Push { + wire_expr: key_expr, + ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + payload: payload.clone(), + }) + } + } else { + drop(tables); + for (outface, key_expr, context) in route.values() { + if face.id != outface.id + && match ( + face.mcast_group.as_ref(), + outface.mcast_group.as_ref(), + ) { + (Some(l), Some(r)) => l != r, + _ => true, + } + { + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, tx, user, payload) + } else { + inc_stats!(face, tx, admin, payload) + } + + outface.primitives.send_push(Push { + wire_expr: key_expr.into(), + ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + payload: payload.clone(), + }) + } + } + } + } + } + } + } + None => { + log::error!("Route data with unknown scope {}!", expr.scope); + } + } +} + +pub fn pull_data(tables_ref: &RwLock, face: &Arc, expr: WireExpr) { + let tables = zread!(tables_ref); + match tables.get_mapping(face, &expr.scope, expr.mapping) { + Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { + Some(mut res) => { + let res = get_mut_unchecked(&mut res); + match res.session_ctxs.get_mut(&face.id) { + Some(ctx) => match &ctx.subs { + Some(_subinfo) => { + // let reliability = subinfo.reliability; + let lock = zlock!(tables.pull_caches_lock); + let route = get_mut_unchecked(ctx) + .last_values + .drain() + .map(|(name, sample)| { + ( + Resource::get_best_key(&tables.root_res, &name, face.id) + .to_owned(), + sample, + ) + }) + .collect::>(); + drop(lock); + drop(tables); + for (key_expr, payload) in route { + face.primitives.send_push(Push { + wire_expr: key_expr, + ext_qos: ext::QoSType::push_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload, + }); + } + } + None => { + log::error!( + "Pull data for unknown subscription {} (no info)!", + prefix.expr() + expr.suffix.as_ref() + ); + } + }, + None => { + log::error!( + "Pull data for unknown subscription {} (no context)!", + prefix.expr() + expr.suffix.as_ref() + ); + } + } + } + None => { + log::error!( + "Pull data for unknown subscription {} (no resource)!", + prefix.expr() + expr.suffix.as_ref() + ); + } + }, + None => { + log::error!("Pull data with unknown scope {}!", expr.scope); + } + }; +} diff --git a/zenoh/src/net/routing/dispatcher/queries.rs b/zenoh/src/net/routing/dispatcher/queries.rs new file mode 100644 index 0000000000..3d7264dc12 --- /dev/null +++ b/zenoh/src/net/routing/dispatcher/queries.rs @@ -0,0 +1,1076 @@ +use crate::net::routing::PREFIX_LIVELINESS; + +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use super::super::hat::network::Network; +use super::face::FaceState; +use super::resource::{QueryRoute, QueryRoutes, QueryTargetQabl, QueryTargetQablSet, Resource}; +use super::tables::{RoutingExpr, Tables, TablesLock}; +use async_trait::async_trait; +use ordered_float::OrderedFloat; +use petgraph::graph::NodeIndex; +use std::borrow::Cow; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::sync::{Arc, Weak}; +use zenoh_buffers::ZBuf; +use zenoh_protocol::{ + core::{ + key_expr::{ + include::{Includer, DEFAULT_INCLUDER}, + OwnedKeyExpr, + }, + Encoding, WhatAmI, WireExpr, ZenohId, + }, + network::{ + declare::{ext, queryable::ext::QueryableInfo}, + request::{ext::TargetType, Request, RequestId}, + response::{self, ext::ResponderIdType, Response, ResponseFinal}, + }, + zenoh::{reply::ext::ConsolidationType, Reply, RequestBody, ResponseBody}, +}; +use zenoh_sync::get_mut_unchecked; +use zenoh_util::Timed; + +pub(crate) struct Query { + src_face: Arc, + src_qid: RequestId, +} + +#[inline] +#[allow(clippy::too_many_arguments)] +fn insert_target_for_qabls( + route: &mut QueryTargetQablSet, + expr: &mut RoutingExpr, + tables: &Tables, + net: &Network, + source: usize, + qabls: &HashMap, + complete: bool, +) { + if net.trees.len() > source { + for (qabl, qabl_info) in qabls { + if let Some(qabl_idx) = net.get_idx(qabl) { + if net.trees[source].directions.len() > qabl_idx.index() { + if let Some(direction) = net.trees[source].directions[qabl_idx.index()] { + if net.graph.contains_node(direction) { + if let Some(face) = tables.get_face(&net.graph[direction].zid) { + if net.distances.len() > qabl_idx.index() { + let key_expr = + Resource::get_best_key(expr.prefix, expr.suffix, face.id); + route.push(QueryTargetQabl { + direction: ( + face.clone(), + key_expr.to_owned(), + if source != 0 { + Some(source as u16) + } else { + None + }, + ), + complete: if complete { + qabl_info.complete as u64 + } else { + 0 + }, + distance: net.distances[qabl_idx.index()], + }); + } + } + } + } + } + } + } + } else { + log::trace!("Tree for node sid:{} not yet ready", source); + } +} + +lazy_static::lazy_static! { + static ref EMPTY_ROUTE: Arc = Arc::new(Vec::new()); +} +fn compute_query_route( + tables: &Tables, + expr: &mut RoutingExpr, + source: Option, + source_type: WhatAmI, +) -> Arc { + let mut route = QueryTargetQablSet::new(); + let key_expr = expr.full_expr(); + if key_expr.ends_with('/') { + return EMPTY_ROUTE.clone(); + } + log::trace!( + "compute_query_route({}, {:?}, {:?})", + key_expr, + source, + source_type + ); + let key_expr = match OwnedKeyExpr::try_from(key_expr) { + Ok(ke) => ke, + Err(e) => { + log::warn!("Invalid KE reached the system: {}", e); + return EMPTY_ROUTE.clone(); + } + }; + let res = Resource::get_resource(expr.prefix, expr.suffix); + let matches = res + .as_ref() + .and_then(|res| res.context.as_ref()) + .map(|ctx| Cow::from(&ctx.matches)) + .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &key_expr))); + + let master = tables.whatami != WhatAmI::Router + || !tables.hat.full_net(WhatAmI::Peer) + || *tables + .hat + .elect_router(&tables.zid, &key_expr, tables.hat.shared_nodes.iter()) + == tables.zid; + + for mres in matches.iter() { + let mres = mres.upgrade().unwrap(); + let complete = DEFAULT_INCLUDER.includes(mres.expr().as_bytes(), key_expr.as_bytes()); + if tables.whatami == WhatAmI::Router { + if master || source_type == WhatAmI::Router { + let net = tables.hat.routers_net.as_ref().unwrap(); + let router_source = match source_type { + WhatAmI::Router => source.unwrap(), + _ => net.idx.index(), + }; + insert_target_for_qabls( + &mut route, + expr, + tables, + net, + router_source, + &mres.context().router_qabls, + complete, + ); + } + + if (master || source_type != WhatAmI::Router) && tables.hat.full_net(WhatAmI::Peer) { + let net = tables.hat.peers_net.as_ref().unwrap(); + let peer_source = match source_type { + WhatAmI::Peer => source.unwrap(), + _ => net.idx.index(), + }; + insert_target_for_qabls( + &mut route, + expr, + tables, + net, + peer_source, + &mres.context().peer_qabls, + complete, + ); + } + } + + if tables.whatami == WhatAmI::Peer && tables.hat.full_net(WhatAmI::Peer) { + let net = tables.hat.peers_net.as_ref().unwrap(); + let peer_source = match source_type { + WhatAmI::Router | WhatAmI::Peer => source.unwrap(), + _ => net.idx.index(), + }; + insert_target_for_qabls( + &mut route, + expr, + tables, + net, + peer_source, + &mres.context().peer_qabls, + complete, + ); + } + + if tables.whatami != WhatAmI::Router || master || source_type == WhatAmI::Router { + for (sid, context) in &mres.session_ctxs { + if match tables.whatami { + WhatAmI::Router => context.face.whatami != WhatAmI::Router, + _ => source_type == WhatAmI::Client || context.face.whatami == WhatAmI::Client, + } { + let key_expr = Resource::get_best_key(expr.prefix, expr.suffix, *sid); + if let Some(qabl_info) = context.qabl.as_ref() { + route.push(QueryTargetQabl { + direction: (context.face.clone(), key_expr.to_owned(), None), + complete: if complete { + qabl_info.complete as u64 + } else { + 0 + }, + distance: 0.5, + }); + } + } + } + } + } + route.sort_by_key(|qabl| OrderedFloat(qabl.distance)); + Arc::new(route) +} + +pub(crate) fn compute_query_routes_(tables: &Tables, res: &Arc) -> QueryRoutes { + let mut routes = QueryRoutes { + routers_query_routes: vec![], + peers_query_routes: vec![], + peer_query_route: None, + client_query_route: None, + }; + let mut expr = RoutingExpr::new(res, ""); + if tables.whatami == WhatAmI::Router { + let indexes = tables + .hat + .routers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + routes + .routers_query_routes + .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); + + for idx in &indexes { + routes.routers_query_routes[idx.index()] = + compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); + } + + routes.peer_query_route = Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) + && tables.hat.full_net(WhatAmI::Peer) + { + let indexes = tables + .hat + .peers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + routes + .peers_query_routes + .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); + + for idx in &indexes { + routes.peers_query_routes[idx.index()] = + compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); + } + } + if tables.whatami == WhatAmI::Peer && !tables.hat.full_net(WhatAmI::Peer) { + routes.client_query_route = Some(compute_query_route( + tables, + &mut expr, + None, + WhatAmI::Client, + )); + routes.peer_query_route = Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if tables.whatami == WhatAmI::Client { + routes.client_query_route = Some(compute_query_route( + tables, + &mut expr, + None, + WhatAmI::Client, + )); + } + routes +} + +pub(crate) fn compute_query_routes(tables: &mut Tables, res: &mut Arc) { + if res.context.is_some() { + let mut res_mut = res.clone(); + let res_mut = get_mut_unchecked(&mut res_mut); + let mut expr = RoutingExpr::new(res, ""); + if tables.whatami == WhatAmI::Router { + let indexes = tables + .hat + .routers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + let routers_query_routes = &mut res_mut.context_mut().routers_query_routes; + routers_query_routes.clear(); + routers_query_routes + .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); + + for idx in &indexes { + routers_query_routes[idx.index()] = + compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); + } + + res_mut.context_mut().peer_query_route = + Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) + && tables.hat.full_net(WhatAmI::Peer) + { + let indexes = tables + .hat + .peers_net + .as_ref() + .unwrap() + .graph + .node_indices() + .collect::>(); + let max_idx = indexes.iter().max().unwrap(); + let peers_query_routes = &mut res_mut.context_mut().peers_query_routes; + peers_query_routes.clear(); + peers_query_routes + .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); + + for idx in &indexes { + peers_query_routes[idx.index()] = + compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); + } + } + if tables.whatami == WhatAmI::Peer && !tables.hat.full_net(WhatAmI::Peer) { + res_mut.context_mut().client_query_route = Some(compute_query_route( + tables, + &mut expr, + None, + WhatAmI::Client, + )); + res_mut.context_mut().peer_query_route = + Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); + } + if tables.whatami == WhatAmI::Client { + res_mut.context_mut().client_query_route = Some(compute_query_route( + tables, + &mut expr, + None, + WhatAmI::Client, + )); + } + } +} + +pub(crate) fn compute_query_routes_from(tables: &mut Tables, res: &mut Arc) { + compute_query_routes(tables, res); + let res = get_mut_unchecked(res); + for child in res.childs.values_mut() { + compute_query_routes_from(tables, child); + } +} + +pub(crate) fn compute_matches_query_routes_( + tables: &Tables, + res: &Arc, +) -> Vec<(Arc, QueryRoutes)> { + let mut routes = vec![]; + if res.context.is_some() { + routes.push((res.clone(), compute_query_routes_(tables, res))); + for match_ in &res.context().matches { + let match_ = match_.upgrade().unwrap(); + if !Arc::ptr_eq(&match_, res) { + let match_routes = compute_query_routes_(tables, &match_); + routes.push((match_, match_routes)); + } + } + } + routes +} + +#[inline] +fn insert_pending_query(outface: &mut Arc, query: Arc) -> RequestId { + let outface_mut = get_mut_unchecked(outface); + outface_mut.next_qid += 1; + let qid = outface_mut.next_qid; + outface_mut.pending_queries.insert(qid, query); + qid +} + +#[inline] +fn should_route( + tables: &Tables, + src_face: &FaceState, + outface: &Arc, + expr: &mut RoutingExpr, +) -> bool { + if src_face.id != outface.id { + let dst_master = tables.whatami != WhatAmI::Router + || outface.whatami != WhatAmI::Peer + || tables.hat.peers_net.is_none() + || tables.zid + == *tables.hat.elect_router( + &tables.zid, + expr.full_expr(), + tables.hat.get_router_links(outface.zid), + ); + + return dst_master + && (src_face.whatami != WhatAmI::Peer + || outface.whatami != WhatAmI::Peer + || tables.hat.full_net(WhatAmI::Peer) + || tables.hat.failover_brokering(src_face.zid, outface.zid)); + } + false +} + +#[inline] +fn compute_final_route( + tables: &Tables, + qabls: &Arc, + src_face: &Arc, + expr: &mut RoutingExpr, + target: &TargetType, + query: Arc, +) -> QueryRoute { + match target { + TargetType::All => { + let mut route = HashMap::new(); + for qabl in qabls.iter() { + if should_route(tables, src_face, &qabl.direction.0, expr) { + #[cfg(feature = "complete_n")] + { + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid, *target) + }); + } + #[cfg(not(feature = "complete_n"))] + { + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid) + }); + } + } + } + route + } + TargetType::AllComplete => { + let mut route = HashMap::new(); + for qabl in qabls.iter() { + if qabl.complete > 0 && should_route(tables, src_face, &qabl.direction.0, expr) { + #[cfg(feature = "complete_n")] + { + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid, *target) + }); + } + #[cfg(not(feature = "complete_n"))] + { + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid) + }); + } + } + } + route + } + #[cfg(feature = "complete_n")] + TargetType::Complete(n) => { + let mut route = HashMap::new(); + let mut remaining = *n; + if src_face.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { + let source_links = tables + .peers_net + .as_ref() + .map(|net| net.get_links(src_face.zid)) + .unwrap_or_default(); + for qabl in qabls.iter() { + if qabl.direction.0.id != src_face.id + && qabl.complete > 0 + && (qabl.direction.0.whatami != WhatAmI::Peer + || (tables.router_peers_failover_brokering + && Tables::failover_brokering_to( + source_links, + qabl.direction.0.zid, + ))) + { + let nb = std::cmp::min(qabl.complete, remaining); + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid, TargetType::Complete(nb)) + }); + remaining -= nb; + if remaining == 0 { + break; + } + } + } + } else { + for qabl in qabls.iter() { + if qabl.direction.0.id != src_face.id && qabl.complete > 0 { + let nb = std::cmp::min(qabl.complete, remaining); + route.entry(qabl.direction.0.id).or_insert_with(|| { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query.clone()); + (direction, qid, TargetType::Complete(nb)) + }); + remaining -= nb; + if remaining == 0 { + break; + } + } + } + } + route + } + TargetType::BestMatching => { + if let Some(qabl) = qabls + .iter() + .find(|qabl| qabl.direction.0.id != src_face.id && qabl.complete > 0) + { + let mut route = HashMap::new(); + #[cfg(feature = "complete_n")] + { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query); + route.insert(direction.0.id, (direction, qid, *target)); + } + #[cfg(not(feature = "complete_n"))] + { + let mut direction = qabl.direction.clone(); + let qid = insert_pending_query(&mut direction.0, query); + route.insert(direction.0.id, (direction, qid)); + } + route + } else { + compute_final_route(tables, qabls, src_face, expr, &TargetType::All, query) + } + } + } +} + +#[inline] +fn compute_local_replies( + tables: &Tables, + prefix: &Arc, + suffix: &str, + face: &Arc, +) -> Vec<(WireExpr<'static>, ZBuf)> { + let mut result = vec![]; + // Only the first routing point in the query route + // should return the liveliness tokens + if face.whatami == WhatAmI::Client { + let key_expr = prefix.expr() + suffix; + let key_expr = match OwnedKeyExpr::try_from(key_expr) { + Ok(ke) => ke, + Err(e) => { + log::warn!("Invalid KE reached the system: {}", e); + return result; + } + }; + if key_expr.starts_with(PREFIX_LIVELINESS) { + let res = Resource::get_resource(prefix, suffix); + let matches = res + .as_ref() + .and_then(|res| res.context.as_ref()) + .map(|ctx| Cow::from(&ctx.matches)) + .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &key_expr))); + for mres in matches.iter() { + let mres = mres.upgrade().unwrap(); + if (mres.context.is_some() + && (!mres.context().router_subs.is_empty() + || !mres.context().peer_subs.is_empty())) + || mres.session_ctxs.values().any(|ctx| ctx.subs.is_some()) + { + result.push((Resource::get_best_key(&mres, "", face.id), ZBuf::default())); + } + } + } + } + result +} + +#[derive(Clone)] +struct QueryCleanup { + tables: Arc, + face: Weak, + qid: RequestId, +} + +#[async_trait] +impl Timed for QueryCleanup { + async fn run(&mut self) { + if let Some(mut face) = self.face.upgrade() { + let tables_lock = zwrite!(self.tables.tables); + if let Some(query) = get_mut_unchecked(&mut face) + .pending_queries + .remove(&self.qid) + { + drop(tables_lock); + log::warn!( + "Didn't receive final reply {}:{} from {}: Timeout!", + query.src_face, + self.qid, + face + ); + finalize_pending_query(query); + } + } + } +} + +pub(crate) fn disable_matches_query_routes(_tables: &mut Tables, res: &mut Arc) { + if res.context.is_some() { + get_mut_unchecked(res).context_mut().valid_query_routes = false; + for match_ in &res.context().matches { + let mut match_ = match_.upgrade().unwrap(); + if !Arc::ptr_eq(&match_, res) { + get_mut_unchecked(&mut match_) + .context_mut() + .valid_query_routes = false; + } + } + } +} + +#[inline] +fn get_query_route( + tables: &Tables, + face: &FaceState, + res: &Option>, + expr: &mut RoutingExpr, + routing_context: u64, +) -> Arc { + match tables.whatami { + WhatAmI::Router => match face.whatami { + WhatAmI::Router => { + let routers_net = tables.hat.routers_net.as_ref().unwrap(); + let local_context = routers_net.get_local_context(routing_context, face.link_id); + res.as_ref() + .and_then(|res| res.routers_query_route(local_context)) + .unwrap_or_else(|| { + compute_query_route(tables, expr, Some(local_context), face.whatami) + }) + } + WhatAmI::Peer => { + if tables.hat.full_net(WhatAmI::Peer) { + let peers_net = tables.hat.peers_net.as_ref().unwrap(); + let local_context = peers_net.get_local_context(routing_context, face.link_id); + res.as_ref() + .and_then(|res| res.peers_query_route(local_context)) + .unwrap_or_else(|| { + compute_query_route(tables, expr, Some(local_context), face.whatami) + }) + } else { + res.as_ref() + .and_then(|res| res.peer_query_route()) + .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)) + } + } + _ => res + .as_ref() + .and_then(|res| res.routers_query_route(0)) + .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)), + }, + WhatAmI::Peer => { + if tables.hat.full_net(WhatAmI::Peer) { + match face.whatami { + WhatAmI::Router | WhatAmI::Peer => { + let peers_net = tables.hat.peers_net.as_ref().unwrap(); + let local_context = + peers_net.get_local_context(routing_context, face.link_id); + res.as_ref() + .and_then(|res| res.peers_query_route(local_context)) + .unwrap_or_else(|| { + compute_query_route(tables, expr, Some(local_context), face.whatami) + }) + } + _ => res + .as_ref() + .and_then(|res| res.peers_query_route(0)) + .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)), + } + } else { + res.as_ref() + .and_then(|res| match face.whatami { + WhatAmI::Client => res.client_query_route(), + _ => res.peer_query_route(), + }) + .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)) + } + } + _ => res + .as_ref() + .and_then(|res| res.client_query_route()) + .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)), + } +} + +#[cfg(feature = "stats")] +macro_rules! inc_req_stats { + ( + $face:expr, + $txrx:ident, + $space:ident, + $body:expr + ) => { + paste::paste! { + if let Some(stats) = $face.stats.as_ref() { + use zenoh_buffers::SplitBuffer; + match &$body { + RequestBody::Put(p) => { + stats.[<$txrx _z_put_msgs>].[](1); + stats.[<$txrx _z_put_pl_bytes>].[](p.payload.len()); + } + RequestBody::Del(_) => { + stats.[<$txrx _z_del_msgs>].[](1); + } + RequestBody::Query(q) => { + stats.[<$txrx _z_query_msgs>].[](1); + stats.[<$txrx _z_query_pl_bytes>].[]( + q.ext_body.as_ref().map(|b| b.payload.len()).unwrap_or(0), + ); + } + RequestBody::Pull(_) => (), + } + } + } + }; +} + +#[cfg(feature = "stats")] +macro_rules! inc_res_stats { + ( + $face:expr, + $txrx:ident, + $space:ident, + $body:expr + ) => { + paste::paste! { + if let Some(stats) = $face.stats.as_ref() { + use zenoh_buffers::SplitBuffer; + match &$body { + ResponseBody::Put(p) => { + stats.[<$txrx _z_put_msgs>].[](1); + stats.[<$txrx _z_put_pl_bytes>].[](p.payload.len()); + } + ResponseBody::Reply(r) => { + stats.[<$txrx _z_reply_msgs>].[](1); + stats.[<$txrx _z_reply_pl_bytes>].[](r.payload.len()); + } + ResponseBody::Err(e) => { + stats.[<$txrx _z_reply_msgs>].[](1); + stats.[<$txrx _z_reply_pl_bytes>].[]( + e.ext_body.as_ref().map(|b| b.payload.len()).unwrap_or(0), + ); + } + ResponseBody::Ack(_) => (), + } + } + } + }; +} + +#[allow(clippy::too_many_arguments)] +pub fn route_query( + tables_ref: &Arc, + face: &Arc, + expr: &WireExpr, + qid: RequestId, + target: TargetType, + body: RequestBody, + routing_context: u64, +) { + let rtables = zread!(tables_ref.tables); + match rtables.get_mapping(face, &expr.scope, expr.mapping) { + Some(prefix) => { + log::debug!( + "Route query {}:{} for res {}{}", + face, + qid, + prefix.expr(), + expr.suffix.as_ref(), + ); + let prefix = prefix.clone(); + let mut expr = RoutingExpr::new(&prefix, expr.suffix.as_ref()); + + #[cfg(feature = "stats")] + let admin = expr.full_expr().starts_with("@/"); + #[cfg(feature = "stats")] + if !admin { + inc_req_stats!(face, rx, user, body) + } else { + inc_req_stats!(face, rx, admin, body) + } + + if rtables.whatami != WhatAmI::Router + || face.whatami != WhatAmI::Peer + || rtables.hat.peers_net.is_none() + || rtables.zid + == *rtables.hat.elect_router( + &rtables.zid, + expr.full_expr(), + rtables.hat.get_router_links(face.zid), + ) + { + let res = Resource::get_resource(&prefix, expr.suffix); + let route = get_query_route(&rtables, face, &res, &mut expr, routing_context); + + let query = Arc::new(Query { + src_face: face.clone(), + src_qid: qid, + }); + + let queries_lock = zwrite!(tables_ref.queries_lock); + let route = compute_final_route(&rtables, &route, face, &mut expr, &target, query); + let local_replies = compute_local_replies(&rtables, &prefix, expr.suffix, face); + let zid = rtables.zid; + + drop(queries_lock); + drop(rtables); + + for (expr, payload) in local_replies { + let payload = ResponseBody::Reply(Reply { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + ext_consolidation: ConsolidationType::default(), + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload, + }); + #[cfg(feature = "stats")] + if !admin { + inc_res_stats!(face, tx, user, payload) + } else { + inc_res_stats!(face, tx, admin, payload) + } + + face.primitives.clone().send_response(Response { + rid: qid, + wire_expr: expr, + payload, + ext_qos: response::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_respid: Some(response::ext::ResponderIdType { + zid, + eid: 0, // TODO + }), + }); + } + + if route.is_empty() { + log::debug!( + "Send final reply {}:{} (no matching queryables or not master)", + face, + qid + ); + face.primitives.clone().send_response_final(ResponseFinal { + rid: qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); + } else { + // let timer = tables.timer.clone(); + // let timeout = tables.queries_default_timeout; + #[cfg(feature = "complete_n")] + { + for ((outface, key_expr, context), qid, t) in route.values() { + // timer.add(TimedEvent::once( + // Instant::now() + timeout, + // QueryCleanup { + // tables: tables_ref.clone(), + // face: Arc::downgrade(&outface), + // *qid, + // }, + // )); + #[cfg(feature = "stats")] + if !admin { + inc_req_stats!(outface, tx, user, body) + } else { + inc_req_stats!(outface, tx, admin, body) + } + + log::trace!("Propagate query {}:{} to {}", face, qid, outface); + outface.primitives.send_request(Request { + id: *qid, + wire_expr: key_expr.into(), + ext_qos: ext::QoSType::request_default(), // TODO + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + ext_target: *t, + ext_budget: None, + ext_timeout: None, + payload: body.clone(), + }); + } + } + + #[cfg(not(feature = "complete_n"))] + { + for ((outface, key_expr, context), qid) in route.values() { + // timer.add(TimedEvent::once( + // Instant::now() + timeout, + // QueryCleanup { + // tables: tables_ref.clone(), + // face: Arc::downgrade(&outface), + // *qid, + // }, + // )); + #[cfg(feature = "stats")] + if !admin { + inc_req_stats!(outface, tx, user, body) + } else { + inc_req_stats!(outface, tx, admin, body) + } + + log::trace!("Propagate query {}:{} to {}", face, qid, outface); + outface.primitives.send_request(Request { + id: *qid, + wire_expr: key_expr.into(), + ext_qos: ext::QoSType::request_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + ext_target: target, + ext_budget: None, + ext_timeout: None, + payload: body.clone(), + }); + } + } + } + } else { + log::debug!("Send final reply {}:{} (not master)", face, qid); + drop(rtables); + face.primitives.clone().send_response_final(ResponseFinal { + rid: qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); + } + } + None => { + log::error!( + "Route query with unknown scope {}! Send final reply.", + expr.scope + ); + drop(rtables); + face.primitives.clone().send_response_final(ResponseFinal { + rid: qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); + } + } +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn route_send_response( + tables_ref: &Arc, + face: &mut Arc, + qid: RequestId, + ext_respid: Option, + key_expr: WireExpr, + body: ResponseBody, +) { + let queries_lock = zread!(tables_ref.queries_lock); + #[cfg(feature = "stats")] + let admin = key_expr.as_str().starts_with("@/"); + #[cfg(feature = "stats")] + if !admin { + inc_res_stats!(face, rx, user, body) + } else { + inc_res_stats!(face, rx, admin, body) + } + + match face.pending_queries.get(&qid) { + Some(query) => { + drop(queries_lock); + + #[cfg(feature = "stats")] + if !admin { + inc_res_stats!(query.src_face, tx, user, body) + } else { + inc_res_stats!(query.src_face, tx, admin, body) + } + + query.src_face.primitives.clone().send_response(Response { + rid: query.src_qid, + wire_expr: key_expr.to_owned(), + payload: body, + ext_qos: response::ext::QoSType::response_default(), + ext_tstamp: None, + ext_respid, + }); + } + None => log::warn!( + "Route reply {}:{} from {}: Query nof found!", + face, + qid, + face + ), + } +} + +pub(crate) fn route_send_response_final( + tables_ref: &Arc, + face: &mut Arc, + qid: RequestId, +) { + let queries_lock = zwrite!(tables_ref.queries_lock); + match get_mut_unchecked(face).pending_queries.remove(&qid) { + Some(query) => { + drop(queries_lock); + log::debug!( + "Received final reply {}:{} from {}", + query.src_face, + qid, + face + ); + finalize_pending_query(query); + } + None => log::warn!( + "Route final reply {}:{} from {}: Query nof found!", + face, + qid, + face + ), + } +} + +pub(crate) fn finalize_pending_queries(tables_ref: &TablesLock, face: &mut Arc) { + let queries_lock = zwrite!(tables_ref.queries_lock); + for (_, query) in get_mut_unchecked(face).pending_queries.drain() { + finalize_pending_query(query); + } + drop(queries_lock); +} + +pub(crate) fn finalize_pending_query(query: Arc) { + if let Some(query) = Arc::into_inner(query) { + log::debug!("Propagate final reply {}:{}", query.src_face, query.src_qid); + query + .src_face + .primitives + .clone() + .send_response_final(ResponseFinal { + rid: query.src_qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); + } +} diff --git a/zenoh/src/net/routing/resource.rs b/zenoh/src/net/routing/dispatcher/resource.rs similarity index 88% rename from zenoh/src/net/routing/resource.rs rename to zenoh/src/net/routing/dispatcher/resource.rs index e26a9217f3..85872e61b6 100644 --- a/zenoh/src/net/routing/resource.rs +++ b/zenoh/src/net/routing/dispatcher/resource.rs @@ -12,7 +12,7 @@ // ZettaScale Zenoh Team, // use super::face::FaceState; -use super::router::{Tables, TablesLock}; +use super::tables::{Tables, TablesLock}; use std::collections::{HashMap, HashSet}; use std::convert::TryInto; use std::hash::{Hash, Hasher}; @@ -33,63 +33,63 @@ use zenoh_protocol::{ }; use zenoh_sync::get_mut_unchecked; -pub(super) type RoutingContext = u16; +pub(crate) type RoutingContext = u16; -pub(super) type Direction = (Arc, WireExpr<'static>, Option); -pub(super) type Route = HashMap; +pub(crate) type Direction = (Arc, WireExpr<'static>, Option); +pub(crate) type Route = HashMap; #[cfg(feature = "complete_n")] -pub(super) type QueryRoute = HashMap; +pub(crate) type QueryRoute = HashMap; #[cfg(not(feature = "complete_n"))] -pub(super) type QueryRoute = HashMap; -pub(super) struct QueryTargetQabl { - pub(super) direction: Direction, - pub(super) complete: u64, - pub(super) distance: f64, +pub(crate) type QueryRoute = HashMap; +pub(crate) struct QueryTargetQabl { + pub(crate) direction: Direction, + pub(crate) complete: u64, + pub(crate) distance: f64, } -pub(super) type QueryTargetQablSet = Vec; -pub(super) type PullCaches = Vec>; - -pub(super) struct SessionContext { - pub(super) face: Arc, - pub(super) local_expr_id: Option, - pub(super) remote_expr_id: Option, - pub(super) subs: Option, - pub(super) qabl: Option, - pub(super) last_values: HashMap, +pub(crate) type QueryTargetQablSet = Vec; +pub(crate) type PullCaches = Vec>; + +pub(crate) struct SessionContext { + pub(crate) face: Arc, + pub(crate) local_expr_id: Option, + pub(crate) remote_expr_id: Option, + pub(crate) subs: Option, + pub(crate) qabl: Option, + pub(crate) last_values: HashMap, } -pub(super) struct DataRoutes { - pub(super) matching_pulls: Option>, - pub(super) routers_data_routes: Vec>, - pub(super) peers_data_routes: Vec>, - pub(super) peer_data_route: Option>, - pub(super) client_data_route: Option>, +pub(crate) struct DataRoutes { + pub(crate) matching_pulls: Option>, + pub(crate) routers_data_routes: Vec>, + pub(crate) peers_data_routes: Vec>, + pub(crate) peer_data_route: Option>, + pub(crate) client_data_route: Option>, } -pub(super) struct QueryRoutes { - pub(super) routers_query_routes: Vec>, - pub(super) peers_query_routes: Vec>, - pub(super) peer_query_route: Option>, - pub(super) client_query_route: Option>, +pub(crate) struct QueryRoutes { + pub(crate) routers_query_routes: Vec>, + pub(crate) peers_query_routes: Vec>, + pub(crate) peer_query_route: Option>, + pub(crate) client_query_route: Option>, } -pub(super) struct ResourceContext { - pub(super) router_subs: HashSet, - pub(super) peer_subs: HashSet, - pub(super) router_qabls: HashMap, - pub(super) peer_qabls: HashMap, - pub(super) matches: Vec>, - pub(super) matching_pulls: Arc, - pub(super) valid_data_routes: bool, - pub(super) routers_data_routes: Vec>, - pub(super) peers_data_routes: Vec>, - pub(super) peer_data_route: Option>, - pub(super) client_data_route: Option>, - pub(super) valid_query_routes: bool, - pub(super) routers_query_routes: Vec>, - pub(super) peers_query_routes: Vec>, - pub(super) peer_query_route: Option>, - pub(super) client_query_route: Option>, +pub(crate) struct ResourceContext { + pub(crate) router_subs: HashSet, + pub(crate) peer_subs: HashSet, + pub(crate) router_qabls: HashMap, + pub(crate) peer_qabls: HashMap, + pub(crate) matches: Vec>, + pub(crate) matching_pulls: Arc, + pub(crate) valid_data_routes: bool, + pub(crate) routers_data_routes: Vec>, + pub(crate) peers_data_routes: Vec>, + pub(crate) peer_data_route: Option>, + pub(crate) client_data_route: Option>, + pub(crate) valid_query_routes: bool, + pub(crate) routers_query_routes: Vec>, + pub(crate) peers_query_routes: Vec>, + pub(crate) peer_query_route: Option>, + pub(crate) client_query_route: Option>, } impl ResourceContext { @@ -114,7 +114,7 @@ impl ResourceContext { } } - pub(super) fn update_data_routes(&mut self, data_routes: DataRoutes) { + pub(crate) fn update_data_routes(&mut self, data_routes: DataRoutes) { self.valid_data_routes = true; if let Some(matching_pulls) = data_routes.matching_pulls { self.matching_pulls = matching_pulls; @@ -125,7 +125,7 @@ impl ResourceContext { self.client_data_route = data_routes.client_data_route; } - pub(super) fn update_query_routes(&mut self, query_routes: QueryRoutes) { + pub(crate) fn update_query_routes(&mut self, query_routes: QueryRoutes) { self.valid_query_routes = true; self.routers_query_routes = query_routes.routers_query_routes; self.peers_query_routes = query_routes.peers_query_routes; @@ -135,12 +135,12 @@ impl ResourceContext { } pub struct Resource { - pub(super) parent: Option>, - pub(super) suffix: String, - pub(super) nonwild_prefix: Option<(Arc, String)>, - pub(super) childs: HashMap>, - pub(super) context: Option, - pub(super) session_ctxs: HashMap>, + pub(crate) parent: Option>, + pub(crate) suffix: String, + pub(crate) nonwild_prefix: Option<(Arc, String)>, + pub(crate) childs: HashMap>, + pub(crate) context: Option, + pub(crate) session_ctxs: HashMap>, } impl PartialEq for Resource { @@ -187,12 +187,12 @@ impl Resource { } #[inline(always)] - pub(super) fn context(&self) -> &ResourceContext { + pub(crate) fn context(&self) -> &ResourceContext { self.context.as_ref().unwrap() } #[inline(always)] - pub(super) fn context_mut(&mut self) -> &mut ResourceContext { + pub(crate) fn context_mut(&mut self) -> &mut ResourceContext { self.context.as_mut().unwrap() } @@ -269,7 +269,7 @@ impl Resource { } #[inline(always)] - pub(super) fn routers_query_route(&self, context: usize) -> Option> { + pub(crate) fn routers_query_route(&self, context: usize) -> Option> { match &self.context { Some(ctx) => { if ctx.valid_query_routes { @@ -284,7 +284,7 @@ impl Resource { } #[inline(always)] - pub(super) fn peers_query_route(&self, context: usize) -> Option> { + pub(crate) fn peers_query_route(&self, context: usize) -> Option> { match &self.context { Some(ctx) => { if ctx.valid_query_routes { @@ -299,7 +299,7 @@ impl Resource { } #[inline(always)] - pub(super) fn peer_query_route(&self) -> Option> { + pub(crate) fn peer_query_route(&self) -> Option> { match &self.context { Some(ctx) => { if ctx.valid_query_routes { @@ -313,7 +313,7 @@ impl Resource { } #[inline(always)] - pub(super) fn client_query_route(&self) -> Option> { + pub(crate) fn client_query_route(&self) -> Option> { match &self.context { Some(ctx) => { if ctx.valid_query_routes { diff --git a/zenoh/src/net/routing/dispatcher/tables.rs b/zenoh/src/net/routing/dispatcher/tables.rs new file mode 100644 index 0000000000..bc2eb520a4 --- /dev/null +++ b/zenoh/src/net/routing/dispatcher/tables.rs @@ -0,0 +1,316 @@ +use crate::net::routing::hat::HatTables; + +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +pub use super::super::hat::pubsub::*; +pub use super::super::hat::queries::*; +use super::face::FaceState; +pub use super::pubsub::*; +pub use super::queries::*; +pub use super::resource::*; +use std::collections::HashMap; +use std::sync::{Arc, Weak}; +use std::sync::{Mutex, RwLock}; +use std::time::Duration; +use uhlc::HLC; +use zenoh_protocol::core::{ExprId, WhatAmI, ZenohId}; +use zenoh_protocol::network::Mapping; +#[cfg(feature = "stats")] +use zenoh_transport::stats::TransportStats; +use zenoh_transport::Primitives; +// use zenoh_collections::Timer; +use zenoh_sync::get_mut_unchecked; + +pub(crate) struct RoutingExpr<'a> { + pub(crate) prefix: &'a Arc, + pub(crate) suffix: &'a str, + full: Option, +} + +impl<'a> RoutingExpr<'a> { + #[inline] + pub(crate) fn new(prefix: &'a Arc, suffix: &'a str) -> Self { + RoutingExpr { + prefix, + suffix, + full: None, + } + } + + #[inline] + pub(crate) fn full_expr(&mut self) -> &str { + if self.full.is_none() { + self.full = Some(self.prefix.expr() + self.suffix); + } + self.full.as_ref().unwrap() + } +} + +pub struct Tables { + pub(crate) zid: ZenohId, + pub(crate) whatami: WhatAmI, + pub(crate) face_counter: usize, + #[allow(dead_code)] + pub(crate) hlc: Option>, + pub(crate) drop_future_timestamp: bool, + // pub(crate) timer: Timer, + // pub(crate) queries_default_timeout: Duration, + pub(crate) root_res: Arc, + pub(crate) faces: HashMap>, + pub(crate) mcast_groups: Vec>, + pub(crate) mcast_faces: Vec>, + pub(crate) pull_caches_lock: Mutex<()>, + pub(crate) hat: HatTables, +} + +impl Tables { + pub fn new( + zid: ZenohId, + whatami: WhatAmI, + hlc: Option>, + drop_future_timestamp: bool, + router_peers_failover_brokering: bool, + _queries_default_timeout: Duration, + ) -> Self { + Tables { + zid, + whatami, + face_counter: 0, + hlc, + drop_future_timestamp, + // timer: Timer::new(true), + // queries_default_timeout, + root_res: Resource::root(), + faces: HashMap::new(), + mcast_groups: vec![], + mcast_faces: vec![], + pull_caches_lock: Mutex::new(()), + hat: HatTables::new(router_peers_failover_brokering), + } + } + + #[doc(hidden)] + pub fn _get_root(&self) -> &Arc { + &self.root_res + } + + pub fn print(&self) -> String { + Resource::print_tree(&self.root_res) + } + + #[inline] + #[allow(clippy::trivially_copy_pass_by_ref)] + pub(crate) fn get_mapping<'a>( + &'a self, + face: &'a FaceState, + expr_id: &ExprId, + mapping: Mapping, + ) -> Option<&'a Arc> { + match expr_id { + 0 => Some(&self.root_res), + expr_id => face.get_mapping(expr_id, mapping), + } + } + + #[inline] + pub(crate) fn get_face(&self, zid: &ZenohId) -> Option<&Arc> { + self.faces.values().find(|face| face.zid == *zid) + } + + pub(crate) fn open_net_face( + &mut self, + zid: ZenohId, + whatami: WhatAmI, + #[cfg(feature = "stats")] stats: Arc, + primitives: Arc, + link_id: usize, + ) -> Weak { + let fid = self.face_counter; + self.face_counter += 1; + let mut newface = self + .faces + .entry(fid) + .or_insert_with(|| { + FaceState::new( + fid, + zid, + whatami, + #[cfg(feature = "stats")] + Some(stats), + primitives.clone(), + link_id, + None, + ) + }) + .clone(); + log::debug!("New {}", newface); + + pubsub_new_face(self, &mut newface); + queries_new_face(self, &mut newface); + + Arc::downgrade(&newface) + } + + pub fn open_face( + &mut self, + zid: ZenohId, + whatami: WhatAmI, + primitives: Arc, + ) -> Weak { + let fid = self.face_counter; + self.face_counter += 1; + let mut newface = self + .faces + .entry(fid) + .or_insert_with(|| { + FaceState::new( + fid, + zid, + whatami, + #[cfg(feature = "stats")] + None, + primitives.clone(), + 0, + None, + ) + }) + .clone(); + log::debug!("New {}", newface); + + pubsub_new_face(self, &mut newface); + queries_new_face(self, &mut newface); + + Arc::downgrade(&newface) + } + + fn compute_routes(&mut self, res: &mut Arc) { + compute_data_routes(self, res); + compute_query_routes(self, res); + } + + pub(crate) fn compute_matches_routes(&mut self, res: &mut Arc) { + if res.context.is_some() { + self.compute_routes(res); + + let resclone = res.clone(); + for match_ in &mut get_mut_unchecked(res).context_mut().matches { + let match_ = &mut match_.upgrade().unwrap(); + if !Arc::ptr_eq(match_, &resclone) && match_.context.is_some() { + self.compute_routes(match_); + } + } + } + } +} + +pub fn close_face(tables: &TablesLock, face: &Weak) { + match face.upgrade() { + Some(mut face) => { + log::debug!("Close {}", face); + finalize_pending_queries(tables, &mut face); + + let ctrl_lock = zlock!(tables.ctrl_lock); + let mut wtables = zwrite!(tables.tables); + let mut face_clone = face.clone(); + let face = get_mut_unchecked(&mut face); + for res in face.remote_mappings.values_mut() { + get_mut_unchecked(res).session_ctxs.remove(&face.id); + Resource::clean(res); + } + face.remote_mappings.clear(); + for res in face.local_mappings.values_mut() { + get_mut_unchecked(res).session_ctxs.remove(&face.id); + Resource::clean(res); + } + face.local_mappings.clear(); + + let mut subs_matches = vec![]; + for mut res in face.remote_subs.drain() { + get_mut_unchecked(&mut res).session_ctxs.remove(&face.id); + undeclare_client_subscription(&mut wtables, &mut face_clone, &mut res); + + if res.context.is_some() { + for match_ in &res.context().matches { + let mut match_ = match_.upgrade().unwrap(); + if !Arc::ptr_eq(&match_, &res) { + get_mut_unchecked(&mut match_) + .context_mut() + .valid_data_routes = false; + subs_matches.push(match_); + } + } + get_mut_unchecked(&mut res).context_mut().valid_data_routes = false; + subs_matches.push(res); + } + } + + let mut qabls_matches = vec![]; + for mut res in face.remote_qabls.drain() { + get_mut_unchecked(&mut res).session_ctxs.remove(&face.id); + undeclare_client_queryable(&mut wtables, &mut face_clone, &mut res); + + if res.context.is_some() { + for match_ in &res.context().matches { + let mut match_ = match_.upgrade().unwrap(); + if !Arc::ptr_eq(&match_, &res) { + get_mut_unchecked(&mut match_) + .context_mut() + .valid_query_routes = false; + qabls_matches.push(match_); + } + } + get_mut_unchecked(&mut res).context_mut().valid_query_routes = false; + qabls_matches.push(res); + } + } + drop(wtables); + + let mut matches_data_routes = vec![]; + let mut matches_query_routes = vec![]; + let rtables = zread!(tables.tables); + for _match in subs_matches.drain(..) { + matches_data_routes.push((_match.clone(), compute_data_routes_(&rtables, &_match))); + } + for _match in qabls_matches.drain(..) { + matches_query_routes + .push((_match.clone(), compute_query_routes_(&rtables, &_match))); + } + drop(rtables); + + let mut wtables = zwrite!(tables.tables); + for (mut res, data_routes) in matches_data_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_data_routes(data_routes); + Resource::clean(&mut res); + } + for (mut res, query_routes) in matches_query_routes { + get_mut_unchecked(&mut res) + .context_mut() + .update_query_routes(query_routes); + Resource::clean(&mut res); + } + wtables.faces.remove(&face.id); + drop(wtables); + drop(ctrl_lock); + } + None => log::error!("Face already closed!"), + } +} + +pub struct TablesLock { + pub tables: RwLock, + pub ctrl_lock: Mutex<()>, + pub queries_lock: RwLock<()>, +} diff --git a/zenoh/src/net/routing/hat/mod.rs b/zenoh/src/net/routing/hat/mod.rs new file mode 100644 index 0000000000..905c4ff4fd --- /dev/null +++ b/zenoh/src/net/routing/hat/mod.rs @@ -0,0 +1,195 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +//! ⚠️ WARNING ⚠️ +//! +//! This module is intended for Zenoh's internal use. +//! +//! [Click here for Zenoh's documentation](../zenoh/index.html) +use self::network::Network; +use super::dispatcher::tables::{Resource, TablesLock}; +use async_std::task::JoinHandle; +use std::{ + collections::{hash_map::DefaultHasher, HashSet}, + hash::Hasher, + sync::Arc, +}; +use zenoh_config::{WhatAmI, ZenohId}; + +pub mod network; +pub mod pubsub; +pub mod queries; + +zconfigurable! { + static ref TREES_COMPUTATION_DELAY: u64 = 100; +} + +pub struct HatTables { + pub(crate) router_subs: HashSet>, + pub(crate) peer_subs: HashSet>, + pub(crate) router_qabls: HashSet>, + pub(crate) peer_qabls: HashSet>, + pub(crate) routers_net: Option, + pub(crate) peers_net: Option, + pub(crate) shared_nodes: Vec, + pub(crate) routers_trees_task: Option>, + pub(crate) peers_trees_task: Option>, + pub(crate) router_peers_failover_brokering: bool, +} + +impl HatTables { + pub fn new(router_peers_failover_brokering: bool) -> Self { + Self { + router_subs: HashSet::new(), + peer_subs: HashSet::new(), + router_qabls: HashSet::new(), + peer_qabls: HashSet::new(), + routers_net: None, + peers_net: None, + shared_nodes: vec![], + routers_trees_task: None, + peers_trees_task: None, + router_peers_failover_brokering, + } + } + + #[inline] + pub(crate) fn get_net(&self, net_type: WhatAmI) -> Option<&Network> { + match net_type { + WhatAmI::Router => self.routers_net.as_ref(), + WhatAmI::Peer => self.peers_net.as_ref(), + _ => None, + } + } + + #[inline] + pub(crate) fn full_net(&self, net_type: WhatAmI) -> bool { + match net_type { + WhatAmI::Router => self + .routers_net + .as_ref() + .map(|net| net.full_linkstate) + .unwrap_or(false), + WhatAmI::Peer => self + .peers_net + .as_ref() + .map(|net| net.full_linkstate) + .unwrap_or(false), + _ => false, + } + } + + #[inline] + pub(crate) fn get_router_links(&self, peer: ZenohId) -> impl Iterator + '_ { + self.peers_net + .as_ref() + .unwrap() + .get_links(peer) + .iter() + .filter(move |nid| { + if let Some(node) = self.routers_net.as_ref().unwrap().get_node(nid) { + node.whatami.unwrap_or(WhatAmI::Router) == WhatAmI::Router + } else { + false + } + }) + } + + #[inline] + pub(crate) fn elect_router<'a>( + &'a self, + self_zid: &'a ZenohId, + key_expr: &str, + mut routers: impl Iterator, + ) -> &'a ZenohId { + match routers.next() { + None => self_zid, + Some(router) => { + let hash = |r: &ZenohId| { + let mut hasher = DefaultHasher::new(); + for b in key_expr.as_bytes() { + hasher.write_u8(*b); + } + for b in &r.to_le_bytes()[..r.size()] { + hasher.write_u8(*b); + } + hasher.finish() + }; + let mut res = router; + let mut h = None; + for router2 in routers { + let h2 = hash(router2); + if h2 > *h.get_or_insert_with(|| hash(res)) { + res = router2; + h = Some(h2); + } + } + res + } + } + } + + #[inline] + pub(crate) fn failover_brokering_to(source_links: &[ZenohId], dest: ZenohId) -> bool { + // if source_links is empty then gossip is probably disabled in source peer + !source_links.is_empty() && !source_links.contains(&dest) + } + + #[inline] + pub(crate) fn failover_brokering(&self, peer1: ZenohId, peer2: ZenohId) -> bool { + self.router_peers_failover_brokering + && self + .peers_net + .as_ref() + .map(|net| HatTables::failover_brokering_to(net.get_links(peer1), peer2)) + .unwrap_or(false) + } + + pub(crate) fn schedule_compute_trees( + &mut self, + tables_ref: Arc, + net_type: WhatAmI, + ) { + log::trace!("Schedule computations"); + if (net_type == WhatAmI::Router && self.routers_trees_task.is_none()) + || (net_type == WhatAmI::Peer && self.peers_trees_task.is_none()) + { + let task = Some(async_std::task::spawn(async move { + async_std::task::sleep(std::time::Duration::from_millis(*TREES_COMPUTATION_DELAY)) + .await; + let mut tables = zwrite!(tables_ref.tables); + + log::trace!("Compute trees"); + let new_childs = match net_type { + WhatAmI::Router => tables.hat.routers_net.as_mut().unwrap().compute_trees(), + _ => tables.hat.peers_net.as_mut().unwrap().compute_trees(), + }; + + log::trace!("Compute routes"); + pubsub::pubsub_tree_change(&mut tables, &new_childs, net_type); + queries::queries_tree_change(&mut tables, &new_childs, net_type); + + log::trace!("Computations completed"); + match net_type { + WhatAmI::Router => tables.hat.routers_trees_task = None, + _ => tables.hat.peers_trees_task = None, + }; + })); + match net_type { + WhatAmI::Router => self.routers_trees_task = task, + _ => self.peers_trees_task = task, + }; + } + } +} diff --git a/zenoh/src/net/routing/network.rs b/zenoh/src/net/routing/hat/network.rs similarity index 99% rename from zenoh/src/net/routing/network.rs rename to zenoh/src/net/routing/hat/network.rs index 3af1e0a87c..2c1a9746da 100644 --- a/zenoh/src/net/routing/network.rs +++ b/zenoh/src/net/routing/hat/network.rs @@ -985,7 +985,7 @@ impl Network { } #[inline] - pub(super) fn get_links(&self, node: ZenohId) -> &[ZenohId] { + pub(crate) fn get_links(&self, node: ZenohId) -> &[ZenohId] { self.get_node(&node) .map(|node| &node.links[..]) .unwrap_or_default() @@ -993,7 +993,7 @@ impl Network { } #[inline] -pub(super) fn shared_nodes(net1: &Network, net2: &Network) -> Vec { +pub(crate) fn shared_nodes(net1: &Network, net2: &Network) -> Vec { net1.graph .node_references() .filter_map(|(_, node1)| { diff --git a/zenoh/src/net/routing/pubsub.rs b/zenoh/src/net/routing/hat/pubsub.rs similarity index 58% rename from zenoh/src/net/routing/pubsub.rs rename to zenoh/src/net/routing/hat/pubsub.rs index 3deba60260..a92d8a4640 100644 --- a/zenoh/src/net/routing/pubsub.rs +++ b/zenoh/src/net/routing/hat/pubsub.rs @@ -11,32 +11,23 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::face::FaceState; +use super::super::dispatcher::face::FaceState; +use super::super::dispatcher::pubsub::*; +use super::super::dispatcher::resource::{Resource, RoutingContext, SessionContext}; +use super::super::dispatcher::tables::{Tables, TablesLock}; +use super::super::PREFIX_LIVELINESS; use super::network::Network; -use super::resource::{ - DataRoutes, Direction, PullCaches, Resource, Route, RoutingContext, SessionContext, -}; -use super::router::{RoutingExpr, Tables, TablesLock}; +use super::HatTables; use petgraph::graph::NodeIndex; -use std::borrow::Cow; -use std::collections::{HashMap, HashSet}; -use std::convert::TryFrom; -use std::sync::RwLock; +use std::collections::HashMap; use std::sync::{Arc, RwLockReadGuard}; use zenoh_core::zread; use zenoh_protocol::{ - core::{ - key_expr::{keyexpr, OwnedKeyExpr}, - Reliability, WhatAmI, WireExpr, ZenohId, - }, - network::{ - declare::{ - common::ext::WireExprType, ext, subscriber::ext::SubscriberInfo, Declare, DeclareBody, - DeclareSubscriber, Mode, UndeclareSubscriber, - }, - Push, + core::{key_expr::keyexpr, Reliability, WhatAmI, WireExpr, ZenohId}, + network::declare::{ + common::ext::WireExprType, ext, subscriber::ext::SubscriberInfo, Declare, DeclareBody, + DeclareSubscriber, Mode, UndeclareSubscriber, }, - zenoh::PushBody, }; use zenoh_sync::get_mut_unchecked; @@ -88,7 +79,7 @@ fn propagate_simple_subscription_to( src_face: &mut Arc, full_peer_net: bool, ) { - if (src_face.id != dst_face.id || res.expr().starts_with(super::PREFIX_LIVELINESS)) + if (src_face.id != dst_face.id || res.expr().starts_with(PREFIX_LIVELINESS)) && !dst_face.local_subs.contains(res) && match tables.whatami { WhatAmI::Router => { @@ -98,7 +89,7 @@ fn propagate_simple_subscription_to( dst_face.whatami != WhatAmI::Router && (src_face.whatami != WhatAmI::Peer || dst_face.whatami != WhatAmI::Peer - || tables.failover_brokering(src_face.zid, dst_face.zid)) + || tables.hat.failover_brokering(src_face.zid, dst_face.zid)) } } WhatAmI::Peer => { @@ -132,7 +123,7 @@ fn propagate_simple_subscription( sub_info: &SubscriberInfo, src_face: &mut Arc, ) { - let full_peer_net = tables.full_net(WhatAmI::Peer); + let full_peer_net = tables.hat.full_net(WhatAmI::Peer); for mut dst_face in tables .faces .values() @@ -158,7 +149,7 @@ fn propagate_sourced_subscription( source: &ZenohId, net_type: WhatAmI, ) { - let net = tables.get_net(net_type).unwrap(); + let net = tables.hat.get_net(net_type).unwrap(); match net.get_idx(source) { Some(tree_sid) => { if net.trees.len() > tree_sid.index() { @@ -207,14 +198,14 @@ fn register_router_subscription( .context_mut() .router_subs .insert(router); - tables.router_subs.insert(res.clone()); + tables.hat.router_subs.insert(res.clone()); } // Propagate subscription to routers propagate_sourced_subscription(tables, res, sub_info, Some(face), &router, WhatAmI::Router); } // Propagate subscription to peers - if tables.full_net(WhatAmI::Peer) && face.whatami != WhatAmI::Peer { + if tables.hat.full_net(WhatAmI::Peer) && face.whatami != WhatAmI::Peer { register_peer_subscription(tables, face, res, sub_info, tables.zid) } @@ -290,7 +281,7 @@ fn register_peer_subscription( { log::debug!("Register peer subscription {} (peer: {})", res.expr(), peer); get_mut_unchecked(res).context_mut().peer_subs.insert(peer); - tables.peer_subs.insert(res.clone()); + tables.hat.peer_subs.insert(res.clone()); } // Propagate subscription to peers @@ -453,7 +444,7 @@ pub fn declare_client_subscription( ); } WhatAmI::Peer => { - if wtables.full_net(WhatAmI::Peer) { + if wtables.hat.full_net(WhatAmI::Peer) { let zid = wtables.zid; register_peer_subscription( &mut wtables, @@ -609,7 +600,7 @@ fn propagate_forget_simple_subscription(tables: &mut Tables, res: &Arc } fn propagate_forget_simple_subscription_to_peers(tables: &mut Tables, res: &Arc) { - if !tables.full_net(WhatAmI::Peer) + if !tables.hat.full_net(WhatAmI::Peer) && res.context().router_subs.len() == 1 && res.context().router_subs.contains(&tables.zid) { @@ -626,7 +617,7 @@ fn propagate_forget_simple_subscription_to_peers(tables: &mut Tables, res: &Arc< && s.subs.is_some() && (s.face.whatami == WhatAmI::Client || (s.face.whatami == WhatAmI::Peer - && tables.failover_brokering(s.face.zid, face.zid))) + && tables.hat.failover_brokering(s.face.zid, face.zid))) }) { let wire_expr = Resource::get_best_key(res, "", face.id); @@ -653,7 +644,7 @@ fn propagate_forget_sourced_subscription( source: &ZenohId, net_type: WhatAmI, ) { - let net = tables.get_net(net_type).unwrap(); + let net = tables.hat.get_net(net_type).unwrap(); match net.get_idx(source) { Some(tree_sid) => { if net.trees.len() > tree_sid.index() { @@ -694,9 +685,9 @@ fn unregister_router_subscription(tables: &mut Tables, res: &mut Arc, .retain(|sub| sub != router); if res.context().router_subs.is_empty() { - tables.router_subs.retain(|sub| !Arc::ptr_eq(sub, res)); + tables.hat.router_subs.retain(|sub| !Arc::ptr_eq(sub, res)); - if tables.full_net(WhatAmI::Peer) { + if tables.hat.full_net(WhatAmI::Peer) { undeclare_peer_subscription(tables, None, res, &tables.zid.clone()); } propagate_forget_simple_subscription(tables, res); @@ -763,7 +754,7 @@ fn unregister_peer_subscription(tables: &mut Tables, res: &mut Arc, pe .retain(|sub| sub != peer); if res.context().peer_subs.is_empty() { - tables.peer_subs.retain(|sub| !Arc::ptr_eq(sub, res)); + tables.hat.peer_subs.retain(|sub| !Arc::ptr_eq(sub, res)); if tables.whatami == WhatAmI::Peer { propagate_forget_simple_subscription(tables, res); @@ -849,7 +840,7 @@ pub(crate) fn undeclare_client_subscription( } WhatAmI::Peer => { if client_subs.is_empty() { - if tables.full_net(WhatAmI::Peer) { + if tables.hat.full_net(WhatAmI::Peer) { undeclare_peer_subscription(tables, None, res, &tables.zid.clone()); } else { propagate_forget_simple_subscription(tables, res); @@ -865,8 +856,7 @@ pub(crate) fn undeclare_client_subscription( if client_subs.len() == 1 && !router_subs && !peer_subs { let face = &mut client_subs[0]; if face.local_subs.contains(res) - && !(face.whatami == WhatAmI::Client - && res.expr().starts_with(super::PREFIX_LIVELINESS)) + && !(face.whatami == WhatAmI::Client && res.expr().starts_with(PREFIX_LIVELINESS)) { let wire_expr = Resource::get_best_key(res, "", face.id); face.primitives.send_declare(Declare { @@ -926,7 +916,7 @@ pub(crate) fn pubsub_new_face(tables: &mut Tables, face: &mut Arc) { match tables.whatami { WhatAmI::Router => { if face.whatami == WhatAmI::Client { - for sub in &tables.router_subs { + for sub in &tables.hat.router_subs { get_mut_unchecked(face).local_subs.insert(sub.clone()); let key_expr = Resource::decl_key(sub, face); face.primitives.send_declare(Declare { @@ -940,15 +930,15 @@ pub(crate) fn pubsub_new_face(tables: &mut Tables, face: &mut Arc) { }), }); } - } else if face.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { - for sub in &tables.router_subs { + } else if face.whatami == WhatAmI::Peer && !tables.hat.full_net(WhatAmI::Peer) { + for sub in &tables.hat.router_subs { if sub.context.is_some() && (sub.context().router_subs.iter().any(|r| *r != tables.zid) || sub.session_ctxs.values().any(|s| { s.subs.is_some() && (s.face.whatami == WhatAmI::Client || (s.face.whatami == WhatAmI::Peer - && tables.failover_brokering(s.face.zid, face.zid))) + && tables.hat.failover_brokering(s.face.zid, face.zid))) })) { get_mut_unchecked(face).local_subs.insert(sub.clone()); @@ -968,9 +958,9 @@ pub(crate) fn pubsub_new_face(tables: &mut Tables, face: &mut Arc) { } } WhatAmI::Peer => { - if tables.full_net(WhatAmI::Peer) { + if tables.hat.full_net(WhatAmI::Peer) { if face.whatami == WhatAmI::Client { - for sub in &tables.peer_subs { + for sub in &tables.hat.peer_subs { get_mut_unchecked(face).local_subs.insert(sub.clone()); let key_expr = Resource::decl_key(sub, face); face.primitives.send_declare(Declare { @@ -1031,6 +1021,7 @@ pub(crate) fn pubsub_remove_node(tables: &mut Tables, node: &ZenohId, net_type: match net_type { WhatAmI::Router => { for mut res in tables + .hat .router_subs .iter() .filter(|res| res.context().router_subs.contains(node)) @@ -1050,6 +1041,7 @@ pub(crate) fn pubsub_remove_node(tables: &mut Tables, node: &ZenohId, net_type: } WhatAmI::Peer => { for mut res in tables + .hat .peer_subs .iter() .filter(|res| res.context().peer_subs.contains(node)) @@ -1088,14 +1080,14 @@ pub(crate) fn pubsub_tree_change( // propagate subs to new childs for (tree_sid, tree_childs) in new_childs.iter().enumerate() { if !tree_childs.is_empty() { - let net = tables.get_net(net_type).unwrap(); + let net = tables.hat.get_net(net_type).unwrap(); let tree_idx = NodeIndex::new(tree_sid); if net.graph.contains_node(tree_idx) { let tree_id = net.graph[tree_idx].zid; let subs_res = match net_type { - WhatAmI::Router => &tables.router_subs, - _ => &tables.peer_subs, + WhatAmI::Router => &tables.hat.router_subs, + _ => &tables.hat.peer_subs, }; for res in subs_res { @@ -1131,7 +1123,7 @@ pub(crate) fn pubsub_tree_change( pub(crate) fn pubsub_linkstate_change(tables: &mut Tables, zid: &ZenohId, links: &[ZenohId]) { if let Some(src_face) = tables.get_face(zid).cloned() { - if tables.router_peers_failover_brokering + if tables.hat.router_peers_failover_brokering && tables.whatami == WhatAmI::Router && src_face.whatami == WhatAmI::Peer { @@ -1148,9 +1140,10 @@ pub(crate) fn pubsub_linkstate_change(tables: &mut Tables, zid: &ZenohId, links: let dst_face = &mut get_mut_unchecked(ctx).face; if dst_face.whatami == WhatAmI::Peer && src_face.zid != dst_face.zid { if dst_face.local_subs.contains(res) { - let forget = !Tables::failover_brokering_to(links, dst_face.zid) + let forget = !HatTables::failover_brokering_to(links, dst_face.zid) && { let ctx_links = tables + .hat .peers_net .as_ref() .map(|net| net.get_links(dst_face.zid)) @@ -1158,7 +1151,7 @@ pub(crate) fn pubsub_linkstate_change(tables: &mut Tables, zid: &ZenohId, links: res.session_ctxs.values().any(|ctx2| { ctx2.face.whatami == WhatAmI::Peer && ctx2.subs.is_some() - && Tables::failover_brokering_to( + && HatTables::failover_brokering_to( ctx_links, ctx2.face.zid, ) @@ -1180,7 +1173,7 @@ pub(crate) fn pubsub_linkstate_change(tables: &mut Tables, zid: &ZenohId, links: get_mut_unchecked(dst_face).local_subs.remove(res); } - } else if Tables::failover_brokering_to(links, ctx.face.zid) { + } else if HatTables::failover_brokering_to(links, ctx.face.zid) { let dst_face = &mut get_mut_unchecked(ctx).face; get_mut_unchecked(dst_face).local_subs.insert(res.clone()); let key_expr = Resource::decl_key(res, dst_face); @@ -1206,740 +1199,3 @@ pub(crate) fn pubsub_linkstate_change(tables: &mut Tables, zid: &ZenohId, links: } } } - -#[inline] -fn insert_faces_for_subs( - route: &mut Route, - expr: &RoutingExpr, - tables: &Tables, - net: &Network, - source: usize, - subs: &HashSet, -) { - if net.trees.len() > source { - for sub in subs { - if let Some(sub_idx) = net.get_idx(sub) { - if net.trees[source].directions.len() > sub_idx.index() { - if let Some(direction) = net.trees[source].directions[sub_idx.index()] { - if net.graph.contains_node(direction) { - if let Some(face) = tables.get_face(&net.graph[direction].zid) { - route.entry(face.id).or_insert_with(|| { - let key_expr = - Resource::get_best_key(expr.prefix, expr.suffix, face.id); - ( - face.clone(), - key_expr.to_owned(), - if source != 0 { - Some(source as u16) - } else { - None - }, - ) - }); - } - } - } - } - } - } - } else { - log::trace!("Tree for node sid:{} not yet ready", source); - } -} - -fn compute_data_route( - tables: &Tables, - expr: &mut RoutingExpr, - source: Option, - source_type: WhatAmI, -) -> Arc { - let mut route = HashMap::new(); - let key_expr = expr.full_expr(); - if key_expr.ends_with('/') { - return Arc::new(route); - } - log::trace!( - "compute_data_route({}, {:?}, {:?})", - key_expr, - source, - source_type - ); - let key_expr = match OwnedKeyExpr::try_from(key_expr) { - Ok(ke) => ke, - Err(e) => { - log::warn!("Invalid KE reached the system: {}", e); - return Arc::new(route); - } - }; - let res = Resource::get_resource(expr.prefix, expr.suffix); - let matches = res - .as_ref() - .and_then(|res| res.context.as_ref()) - .map(|ctx| Cow::from(&ctx.matches)) - .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &key_expr))); - - let master = tables.whatami != WhatAmI::Router - || !tables.full_net(WhatAmI::Peer) - || *tables.elect_router(&key_expr, tables.shared_nodes.iter()) == tables.zid; - - for mres in matches.iter() { - let mres = mres.upgrade().unwrap(); - if tables.whatami == WhatAmI::Router { - if master || source_type == WhatAmI::Router { - let net = tables.routers_net.as_ref().unwrap(); - let router_source = match source_type { - WhatAmI::Router => source.unwrap(), - _ => net.idx.index(), - }; - insert_faces_for_subs( - &mut route, - expr, - tables, - net, - router_source, - &mres.context().router_subs, - ); - } - - if (master || source_type != WhatAmI::Router) && tables.full_net(WhatAmI::Peer) { - let net = tables.peers_net.as_ref().unwrap(); - let peer_source = match source_type { - WhatAmI::Peer => source.unwrap(), - _ => net.idx.index(), - }; - insert_faces_for_subs( - &mut route, - expr, - tables, - net, - peer_source, - &mres.context().peer_subs, - ); - } - } - - if tables.whatami == WhatAmI::Peer && tables.full_net(WhatAmI::Peer) { - let net = tables.peers_net.as_ref().unwrap(); - let peer_source = match source_type { - WhatAmI::Router | WhatAmI::Peer => source.unwrap(), - _ => net.idx.index(), - }; - insert_faces_for_subs( - &mut route, - expr, - tables, - net, - peer_source, - &mres.context().peer_subs, - ); - } - - if tables.whatami != WhatAmI::Router || master || source_type == WhatAmI::Router { - for (sid, context) in &mres.session_ctxs { - if let Some(subinfo) = &context.subs { - if match tables.whatami { - WhatAmI::Router => context.face.whatami != WhatAmI::Router, - _ => { - source_type == WhatAmI::Client - || context.face.whatami == WhatAmI::Client - } - } && subinfo.mode == Mode::Push - { - route.entry(*sid).or_insert_with(|| { - let key_expr = Resource::get_best_key(expr.prefix, expr.suffix, *sid); - (context.face.clone(), key_expr.to_owned(), None) - }); - } - } - } - } - } - for mcast_group in &tables.mcast_groups { - route.insert( - mcast_group.id, - ( - mcast_group.clone(), - expr.full_expr().to_string().into(), - None, - ), - ); - } - Arc::new(route) -} - -fn compute_matching_pulls(tables: &Tables, expr: &mut RoutingExpr) -> Arc { - let mut pull_caches = vec![]; - let ke = if let Ok(ke) = OwnedKeyExpr::try_from(expr.full_expr()) { - ke - } else { - return Arc::new(pull_caches); - }; - let res = Resource::get_resource(expr.prefix, expr.suffix); - let matches = res - .as_ref() - .and_then(|res| res.context.as_ref()) - .map(|ctx| Cow::from(&ctx.matches)) - .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &ke))); - - for mres in matches.iter() { - let mres = mres.upgrade().unwrap(); - for context in mres.session_ctxs.values() { - if let Some(subinfo) = &context.subs { - if subinfo.mode == Mode::Pull { - pull_caches.push(context.clone()); - } - } - } - } - Arc::new(pull_caches) -} - -pub(super) fn compute_data_routes_(tables: &Tables, res: &Arc) -> DataRoutes { - let mut routes = DataRoutes { - matching_pulls: None, - routers_data_routes: vec![], - peers_data_routes: vec![], - peer_data_route: None, - client_data_route: None, - }; - let mut expr = RoutingExpr::new(res, ""); - if tables.whatami == WhatAmI::Router { - let indexes = tables - .routers_net - .as_ref() - .unwrap() - .graph - .node_indices() - .collect::>(); - let max_idx = indexes.iter().max().unwrap(); - routes - .routers_data_routes - .resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); - - for idx in &indexes { - routes.routers_data_routes[idx.index()] = - compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); - } - - routes.peer_data_route = Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); - } - if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) - && tables.full_net(WhatAmI::Peer) - { - let indexes = tables - .peers_net - .as_ref() - .unwrap() - .graph - .node_indices() - .collect::>(); - let max_idx = indexes.iter().max().unwrap(); - routes - .peers_data_routes - .resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); - - for idx in &indexes { - routes.peers_data_routes[idx.index()] = - compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); - } - } - if tables.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { - routes.client_data_route = - Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); - routes.peer_data_route = Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); - } - if tables.whatami == WhatAmI::Client { - routes.client_data_route = - Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); - } - routes.matching_pulls = Some(compute_matching_pulls(tables, &mut expr)); - routes -} - -pub(crate) fn compute_data_routes(tables: &mut Tables, res: &mut Arc) { - if res.context.is_some() { - let mut res_mut = res.clone(); - let res_mut = get_mut_unchecked(&mut res_mut); - let mut expr = RoutingExpr::new(res, ""); - if tables.whatami == WhatAmI::Router { - let indexes = tables - .routers_net - .as_ref() - .unwrap() - .graph - .node_indices() - .collect::>(); - let max_idx = indexes.iter().max().unwrap(); - let routers_data_routes = &mut res_mut.context_mut().routers_data_routes; - routers_data_routes.clear(); - routers_data_routes.resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); - - for idx in &indexes { - routers_data_routes[idx.index()] = - compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); - } - - res_mut.context_mut().peer_data_route = - Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); - } - if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) - && tables.full_net(WhatAmI::Peer) - { - let indexes = tables - .peers_net - .as_ref() - .unwrap() - .graph - .node_indices() - .collect::>(); - let max_idx = indexes.iter().max().unwrap(); - let peers_data_routes = &mut res_mut.context_mut().peers_data_routes; - peers_data_routes.clear(); - peers_data_routes.resize_with(max_idx.index() + 1, || Arc::new(HashMap::new())); - - for idx in &indexes { - peers_data_routes[idx.index()] = - compute_data_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); - } - } - if tables.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { - res_mut.context_mut().client_data_route = - Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); - res_mut.context_mut().peer_data_route = - Some(compute_data_route(tables, &mut expr, None, WhatAmI::Peer)); - } - if tables.whatami == WhatAmI::Client { - res_mut.context_mut().client_data_route = - Some(compute_data_route(tables, &mut expr, None, WhatAmI::Client)); - } - res_mut.context_mut().matching_pulls = compute_matching_pulls(tables, &mut expr); - } -} - -pub(super) fn compute_data_routes_from(tables: &mut Tables, res: &mut Arc) { - compute_data_routes(tables, res); - let res = get_mut_unchecked(res); - for child in res.childs.values_mut() { - compute_data_routes_from(tables, child); - } -} - -pub(super) fn compute_matches_data_routes_<'a>( - tables: &'a Tables, - res: &'a Arc, -) -> Vec<(Arc, DataRoutes)> { - let mut routes = vec![]; - if res.context.is_some() { - routes.push((res.clone(), compute_data_routes_(tables, res))); - for match_ in &res.context().matches { - let match_ = match_.upgrade().unwrap(); - if !Arc::ptr_eq(&match_, res) { - let match_routes = compute_data_routes_(tables, &match_); - routes.push((match_, match_routes)); - } - } - } - routes -} - -pub(super) fn disable_matches_data_routes(_tables: &mut Tables, res: &mut Arc) { - if res.context.is_some() { - get_mut_unchecked(res).context_mut().valid_data_routes = false; - for match_ in &res.context().matches { - let mut match_ = match_.upgrade().unwrap(); - if !Arc::ptr_eq(&match_, res) { - get_mut_unchecked(&mut match_) - .context_mut() - .valid_data_routes = false; - } - } - } -} - -macro_rules! treat_timestamp { - ($hlc:expr, $payload:expr, $drop:expr) => { - // if an HLC was configured (via Config.add_timestamp), - // check DataInfo and add a timestamp if there isn't - if let Some(hlc) = $hlc { - if let PushBody::Put(data) = &mut $payload { - if let Some(ref ts) = data.timestamp { - // Timestamp is present; update HLC with it (possibly raising error if delta exceed) - match hlc.update_with_timestamp(ts) { - Ok(()) => (), - Err(e) => { - if $drop { - log::error!( - "Error treating timestamp for received Data ({}). Drop it!", - e - ); - return; - } else { - data.timestamp = Some(hlc.new_timestamp()); - log::error!( - "Error treating timestamp for received Data ({}). Replace timestamp: {:?}", - e, - data.timestamp); - } - } - } - } else { - // Timestamp not present; add one - data.timestamp = Some(hlc.new_timestamp()); - log::trace!("Adding timestamp to DataInfo: {:?}", data.timestamp); - } - } - } - } -} - -#[inline] -fn get_data_route( - tables: &Tables, - face: &FaceState, - res: &Option>, - expr: &mut RoutingExpr, - routing_context: u64, -) -> Arc { - match tables.whatami { - WhatAmI::Router => match face.whatami { - WhatAmI::Router => { - let routers_net = tables.routers_net.as_ref().unwrap(); - let local_context = routers_net.get_local_context(routing_context, face.link_id); - res.as_ref() - .and_then(|res| res.routers_data_route(local_context)) - .unwrap_or_else(|| { - compute_data_route(tables, expr, Some(local_context), face.whatami) - }) - } - WhatAmI::Peer => { - if tables.full_net(WhatAmI::Peer) { - let peers_net = tables.peers_net.as_ref().unwrap(); - let local_context = peers_net.get_local_context(routing_context, face.link_id); - res.as_ref() - .and_then(|res| res.peers_data_route(local_context)) - .unwrap_or_else(|| { - compute_data_route(tables, expr, Some(local_context), face.whatami) - }) - } else { - res.as_ref() - .and_then(|res| res.peer_data_route()) - .unwrap_or_else(|| compute_data_route(tables, expr, None, face.whatami)) - } - } - _ => res - .as_ref() - .and_then(|res| res.routers_data_route(0)) - .unwrap_or_else(|| compute_data_route(tables, expr, None, face.whatami)), - }, - WhatAmI::Peer => { - if tables.full_net(WhatAmI::Peer) { - match face.whatami { - WhatAmI::Router | WhatAmI::Peer => { - let peers_net = tables.peers_net.as_ref().unwrap(); - let local_context = - peers_net.get_local_context(routing_context, face.link_id); - res.as_ref() - .and_then(|res| res.peers_data_route(local_context)) - .unwrap_or_else(|| { - compute_data_route(tables, expr, Some(local_context), face.whatami) - }) - } - _ => res - .as_ref() - .and_then(|res| res.peers_data_route(0)) - .unwrap_or_else(|| compute_data_route(tables, expr, None, face.whatami)), - } - } else { - res.as_ref() - .and_then(|res| match face.whatami { - WhatAmI::Client => res.client_data_route(), - _ => res.peer_data_route(), - }) - .unwrap_or_else(|| compute_data_route(tables, expr, None, face.whatami)) - } - } - _ => res - .as_ref() - .and_then(|res| res.client_data_route()) - .unwrap_or_else(|| compute_data_route(tables, expr, None, face.whatami)), - } -} - -#[inline] -fn get_matching_pulls( - tables: &Tables, - res: &Option>, - expr: &mut RoutingExpr, -) -> Arc { - res.as_ref() - .and_then(|res| res.context.as_ref()) - .map(|ctx| ctx.matching_pulls.clone()) - .unwrap_or_else(|| compute_matching_pulls(tables, expr)) -} - -macro_rules! cache_data { - ( - $matching_pulls:expr, - $expr:expr, - $payload:expr - ) => { - for context in $matching_pulls.iter() { - get_mut_unchecked(&mut context.clone()) - .last_values - .insert($expr.full_expr().to_string(), $payload.clone()); - } - }; -} - -#[inline] -fn should_route( - tables: &Tables, - src_face: &FaceState, - outface: &Arc, - expr: &mut RoutingExpr, -) -> bool { - if src_face.id != outface.id - && match (src_face.mcast_group.as_ref(), outface.mcast_group.as_ref()) { - (Some(l), Some(r)) => l != r, - _ => true, - } - { - let dst_master = tables.whatami != WhatAmI::Router - || outface.whatami != WhatAmI::Peer - || tables.peers_net.is_none() - || tables.zid - == *tables.elect_router(expr.full_expr(), tables.get_router_links(outface.zid)); - - return dst_master - && (src_face.whatami != WhatAmI::Peer - || outface.whatami != WhatAmI::Peer - || tables.full_net(WhatAmI::Peer) - || tables.failover_brokering(src_face.zid, outface.zid)); - } - false -} - -#[cfg(feature = "stats")] -macro_rules! inc_stats { - ( - $face:expr, - $txrx:ident, - $space:ident, - $body:expr - ) => { - paste::paste! { - if let Some(stats) = $face.stats.as_ref() { - use zenoh_buffers::SplitBuffer; - match &$body { - PushBody::Put(p) => { - stats.[<$txrx _z_put_msgs>].[](1); - stats.[<$txrx _z_put_pl_bytes>].[](p.payload.len()); - } - PushBody::Del(_) => { - stats.[<$txrx _z_del_msgs>].[](1); - } - } - } - } - }; -} - -#[allow(clippy::too_many_arguments)] -pub fn full_reentrant_route_data( - tables_ref: &RwLock, - face: &FaceState, - expr: &WireExpr, - ext_qos: ext::QoSType, - mut payload: PushBody, - routing_context: u64, -) { - let tables = zread!(tables_ref); - match tables.get_mapping(face, &expr.scope, expr.mapping).cloned() { - Some(prefix) => { - log::trace!( - "Route data for res {}{}", - prefix.expr(), - expr.suffix.as_ref() - ); - let mut expr = RoutingExpr::new(&prefix, expr.suffix.as_ref()); - - #[cfg(feature = "stats")] - let admin = expr.full_expr().starts_with("@/"); - #[cfg(feature = "stats")] - if !admin { - inc_stats!(face, rx, user, payload) - } else { - inc_stats!(face, rx, admin, payload) - } - - if tables.whatami != WhatAmI::Router - || face.whatami != WhatAmI::Peer - || tables.peers_net.is_none() - || tables.zid - == *tables.elect_router(expr.full_expr(), tables.get_router_links(face.zid)) - { - let res = Resource::get_resource(&prefix, expr.suffix); - let route = get_data_route(&tables, face, &res, &mut expr, routing_context); - let matching_pulls = get_matching_pulls(&tables, &res, &mut expr); - - if !(route.is_empty() && matching_pulls.is_empty()) { - treat_timestamp!(&tables.hlc, payload, tables.drop_future_timestamp); - - if route.len() == 1 && matching_pulls.len() == 0 { - let (outface, key_expr, context) = route.values().next().unwrap(); - if should_route(&tables, face, outface, &mut expr) { - drop(tables); - #[cfg(feature = "stats")] - if !admin { - inc_stats!(face, tx, user, payload) - } else { - inc_stats!(face, tx, admin, payload) - } - - outface.primitives.send_push(Push { - wire_expr: key_expr.into(), - ext_qos, - ext_tstamp: None, - ext_nodeid: ext::NodeIdType { - node_id: context.unwrap_or(0), - }, - payload, - }) - } - } else { - if !matching_pulls.is_empty() { - let lock = zlock!(tables.pull_caches_lock); - cache_data!(matching_pulls, expr, payload); - drop(lock); - } - - if tables.whatami == WhatAmI::Router { - let route = route - .values() - .filter(|(outface, _key_expr, _context)| { - should_route(&tables, face, outface, &mut expr) - }) - .cloned() - .collect::>(); - - drop(tables); - for (outface, key_expr, context) in route { - #[cfg(feature = "stats")] - if !admin { - inc_stats!(face, tx, user, payload) - } else { - inc_stats!(face, tx, admin, payload) - } - - outface.primitives.send_push(Push { - wire_expr: key_expr, - ext_qos, - ext_tstamp: None, - ext_nodeid: ext::NodeIdType { - node_id: context.unwrap_or(0), - }, - payload: payload.clone(), - }) - } - } else { - drop(tables); - for (outface, key_expr, context) in route.values() { - if face.id != outface.id - && match ( - face.mcast_group.as_ref(), - outface.mcast_group.as_ref(), - ) { - (Some(l), Some(r)) => l != r, - _ => true, - } - { - #[cfg(feature = "stats")] - if !admin { - inc_stats!(face, tx, user, payload) - } else { - inc_stats!(face, tx, admin, payload) - } - - outface.primitives.send_push(Push { - wire_expr: key_expr.into(), - ext_qos, - ext_tstamp: None, - ext_nodeid: ext::NodeIdType { - node_id: context.unwrap_or(0), - }, - payload: payload.clone(), - }) - } - } - } - } - } - } - } - None => { - log::error!("Route data with unknown scope {}!", expr.scope); - } - } -} - -pub fn pull_data(tables_ref: &RwLock, face: &Arc, expr: WireExpr) { - let tables = zread!(tables_ref); - match tables.get_mapping(face, &expr.scope, expr.mapping) { - Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { - Some(mut res) => { - let res = get_mut_unchecked(&mut res); - match res.session_ctxs.get_mut(&face.id) { - Some(ctx) => match &ctx.subs { - Some(_subinfo) => { - // let reliability = subinfo.reliability; - let lock = zlock!(tables.pull_caches_lock); - let route = get_mut_unchecked(ctx) - .last_values - .drain() - .map(|(name, sample)| { - ( - Resource::get_best_key(&tables.root_res, &name, face.id) - .to_owned(), - sample, - ) - }) - .collect::>(); - drop(lock); - drop(tables); - for (key_expr, payload) in route { - face.primitives.send_push(Push { - wire_expr: key_expr, - ext_qos: ext::QoSType::push_default(), - ext_tstamp: None, - ext_nodeid: ext::NodeIdType::default(), - payload, - }); - } - } - None => { - log::error!( - "Pull data for unknown subscription {} (no info)!", - prefix.expr() + expr.suffix.as_ref() - ); - } - }, - None => { - log::error!( - "Pull data for unknown subscription {} (no context)!", - prefix.expr() + expr.suffix.as_ref() - ); - } - } - } - None => { - log::error!( - "Pull data for unknown subscription {} (no resource)!", - prefix.expr() + expr.suffix.as_ref() - ); - } - }, - None => { - log::error!("Pull data with unknown scope {}!", expr.scope); - } - }; -} diff --git a/zenoh/src/net/routing/queries.rs b/zenoh/src/net/routing/hat/queries.rs similarity index 53% rename from zenoh/src/net/routing/queries.rs rename to zenoh/src/net/routing/hat/queries.rs index c55cfc046c..3e4da6cab8 100644 --- a/zenoh/src/net/routing/queries.rs +++ b/zenoh/src/net/routing/hat/queries.rs @@ -11,46 +11,23 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::face::FaceState; +use super::super::dispatcher::face::FaceState; +use super::super::dispatcher::queries::*; +use super::super::dispatcher::resource::{Resource, RoutingContext, SessionContext}; +use super::super::dispatcher::tables::{Tables, TablesLock}; use super::network::Network; -use super::resource::{ - QueryRoute, QueryRoutes, QueryTargetQabl, QueryTargetQablSet, Resource, RoutingContext, - SessionContext, -}; -use super::router::{RoutingExpr, Tables, TablesLock}; -use async_trait::async_trait; -use ordered_float::OrderedFloat; +use super::HatTables; use petgraph::graph::NodeIndex; -use std::borrow::Cow; use std::collections::HashMap; -use std::convert::TryFrom; -use std::sync::{Arc, RwLockReadGuard, Weak}; -use zenoh_buffers::ZBuf; +use std::sync::{Arc, RwLockReadGuard}; use zenoh_protocol::{ - core::{ - key_expr::{ - include::{Includer, DEFAULT_INCLUDER}, - keyexpr, OwnedKeyExpr, - }, - Encoding, WhatAmI, WireExpr, ZenohId, + core::{key_expr::keyexpr, WhatAmI, WireExpr, ZenohId}, + network::declare::{ + common::ext::WireExprType, ext, queryable::ext::QueryableInfo, Declare, DeclareBody, + DeclareQueryable, UndeclareQueryable, }, - network::{ - declare::{ - common::ext::WireExprType, ext, queryable::ext::QueryableInfo, Declare, DeclareBody, - DeclareQueryable, UndeclareQueryable, - }, - request::{ext::TargetType, Request, RequestId}, - response::{self, ext::ResponderIdType, Response, ResponseFinal}, - }, - zenoh::{reply::ext::ConsolidationType, Reply, RequestBody, ResponseBody}, }; use zenoh_sync::get_mut_unchecked; -use zenoh_util::Timed; - -pub(crate) struct Query { - src_face: Arc, - src_qid: RequestId, -} #[cfg(feature = "complete_n")] #[inline] @@ -69,7 +46,7 @@ fn merge_qabl_infos(mut this: QueryableInfo, info: &QueryableInfo) -> QueryableI } fn local_router_qabl_info(tables: &Tables, res: &Arc) -> QueryableInfo { - let info = if tables.full_net(WhatAmI::Peer) { + let info = if tables.hat.full_net(WhatAmI::Peer) { res.context.as_ref().and_then(|ctx| { ctx.peer_qabls.iter().fold(None, |accu, (zid, info)| { if *zid != tables.zid { @@ -157,7 +134,7 @@ fn local_qabl_info(tables: &Tables, res: &Arc, face: &Arc) } else { None }; - if res.context.is_some() && tables.full_net(WhatAmI::Peer) { + if res.context.is_some() && tables.hat.full_net(WhatAmI::Peer) { info = res .context() .peer_qabls @@ -178,7 +155,7 @@ fn local_qabl_info(tables: &Tables, res: &Arc, face: &Arc) .fold(info, |accu, ctx| { if ctx.face.id != face.id && ctx.face.whatami != WhatAmI::Peer || face.whatami != WhatAmI::Peer - || tables.failover_brokering(ctx.face.zid, face.zid) + || tables.hat.failover_brokering(ctx.face.zid, face.zid) { if let Some(info) = ctx.qabl.as_ref() { Some(match accu { @@ -243,7 +220,7 @@ fn propagate_simple_queryable( res: &Arc, src_face: Option<&mut Arc>, ) { - let full_peers_net = tables.full_net(WhatAmI::Peer); + let full_peers_net = tables.hat.full_net(WhatAmI::Peer); let faces = tables.faces.values().cloned(); for mut dst_face in faces { let info = local_qabl_info(tables, res, &dst_face); @@ -259,7 +236,7 @@ fn propagate_simple_queryable( && (src_face.is_none() || src_face.as_ref().unwrap().whatami != WhatAmI::Peer || dst_face.whatami != WhatAmI::Peer - || tables.failover_brokering( + || tables.hat.failover_brokering( src_face.as_ref().unwrap().zid, dst_face.zid, )) @@ -307,7 +284,7 @@ fn propagate_sourced_queryable( source: &ZenohId, net_type: WhatAmI, ) { - let net = tables.get_net(net_type).unwrap(); + let net = tables.hat.get_net(net_type).unwrap(); match net.get_idx(source) { Some(tree_sid) => { if net.trees.len() > tree_sid.index() { @@ -357,7 +334,7 @@ fn register_router_queryable( .context_mut() .router_qabls .insert(router, *qabl_info); - tables.router_qabls.insert(res.clone()); + tables.hat.router_qabls.insert(res.clone()); } // Propagate queryable to routers @@ -371,7 +348,7 @@ fn register_router_queryable( ); } - if tables.full_net(WhatAmI::Peer) { + if tables.hat.full_net(WhatAmI::Peer) { // Propagate queryable to peers if face.is_none() || face.as_ref().unwrap().whatami != WhatAmI::Peer { let local_info = local_peer_qabl_info(tables, res); @@ -453,7 +430,7 @@ fn register_peer_queryable( .context_mut() .peer_qabls .insert(peer, *qabl_info); - tables.peer_qabls.insert(res.clone()); + tables.hat.peer_qabls.insert(res.clone()); } // Propagate queryable to peers @@ -607,7 +584,7 @@ pub fn declare_client_queryable( ); } WhatAmI::Peer => { - if wtables.full_net(WhatAmI::Peer) { + if wtables.hat.full_net(WhatAmI::Peer) { let local_details = local_peer_qabl_info(&wtables, &res); let zid = wtables.zid; register_peer_queryable( @@ -735,7 +712,7 @@ fn propagate_forget_simple_queryable(tables: &mut Tables, res: &mut Arc) { - if !tables.full_net(WhatAmI::Peer) + if !tables.hat.full_net(WhatAmI::Peer) && res.context().router_qabls.len() == 1 && res.context().router_qabls.contains_key(&tables.zid) { @@ -752,7 +729,7 @@ fn propagate_forget_simple_queryable_to_peers(tables: &mut Tables, res: &mut Arc && s.qabl.is_some() && (s.face.whatami == WhatAmI::Client || (s.face.whatami == WhatAmI::Peer - && tables.failover_brokering(s.face.zid, face.zid))) + && tables.hat.failover_brokering(s.face.zid, face.zid))) }) { let wire_expr = Resource::get_best_key(res, "", face.id); @@ -779,7 +756,7 @@ fn propagate_forget_sourced_queryable( source: &ZenohId, net_type: WhatAmI, ) { - let net = tables.get_net(net_type).unwrap(); + let net = tables.hat.get_net(net_type).unwrap(); match net.get_idx(source) { Some(tree_sid) => { if net.trees.len() > tree_sid.index() { @@ -820,9 +797,12 @@ fn unregister_router_queryable(tables: &mut Tables, res: &mut Arc, rou .remove(router); if res.context().router_qabls.is_empty() { - tables.router_qabls.retain(|qabl| !Arc::ptr_eq(qabl, res)); + tables + .hat + .router_qabls + .retain(|qabl| !Arc::ptr_eq(qabl, res)); - if tables.full_net(WhatAmI::Peer) { + if tables.hat.full_net(WhatAmI::Peer) { undeclare_peer_queryable(tables, None, res, &tables.zid.clone()); } propagate_forget_simple_queryable(tables, res); @@ -883,7 +863,7 @@ fn unregister_peer_queryable(tables: &mut Tables, res: &mut Arc, peer: get_mut_unchecked(res).context_mut().peer_qabls.remove(peer); if res.context().peer_qabls.is_empty() { - tables.peer_qabls.retain(|qabl| !Arc::ptr_eq(qabl, res)); + tables.hat.peer_qabls.retain(|qabl| !Arc::ptr_eq(qabl, res)); if tables.whatami == WhatAmI::Peer { propagate_forget_simple_queryable(tables, res); @@ -977,7 +957,7 @@ pub(crate) fn undeclare_client_queryable( } } WhatAmI::Peer => { - if tables.full_net(WhatAmI::Peer) { + if tables.hat.full_net(WhatAmI::Peer) { if client_qabls.is_empty() { undeclare_peer_queryable(tables, None, res, &tables.zid.clone()); } else { @@ -1056,7 +1036,7 @@ pub(crate) fn queries_new_face(tables: &mut Tables, face: &mut Arc) { match tables.whatami { WhatAmI::Router => { if face.whatami == WhatAmI::Client { - for qabl in tables.router_qabls.iter() { + for qabl in tables.hat.router_qabls.iter() { if qabl.context.is_some() { let info = local_qabl_info(tables, qabl, face); get_mut_unchecked(face) @@ -1075,15 +1055,15 @@ pub(crate) fn queries_new_face(tables: &mut Tables, face: &mut Arc) { }); } } - } else if face.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { - for qabl in tables.router_qabls.iter() { + } else if face.whatami == WhatAmI::Peer && !tables.hat.full_net(WhatAmI::Peer) { + for qabl in tables.hat.router_qabls.iter() { if qabl.context.is_some() && (qabl.context().router_qabls.keys().any(|r| *r != tables.zid) || qabl.session_ctxs.values().any(|s| { s.qabl.is_some() && (s.face.whatami == WhatAmI::Client || (s.face.whatami == WhatAmI::Peer - && tables.failover_brokering(s.face.zid, face.zid))) + && tables.hat.failover_brokering(s.face.zid, face.zid))) })) { let info = local_qabl_info(tables, qabl, face); @@ -1106,9 +1086,9 @@ pub(crate) fn queries_new_face(tables: &mut Tables, face: &mut Arc) { } } WhatAmI::Peer => { - if tables.full_net(WhatAmI::Peer) { + if tables.hat.full_net(WhatAmI::Peer) { if face.whatami == WhatAmI::Client { - for qabl in &tables.peer_qabls { + for qabl in &tables.hat.peer_qabls { if qabl.context.is_some() { let info = local_qabl_info(tables, qabl, face); get_mut_unchecked(face) @@ -1160,7 +1140,7 @@ pub(crate) fn queries_remove_node(tables: &mut Tables, node: &ZenohId, net_type: match net_type { WhatAmI::Router => { let mut qabls = vec![]; - for res in tables.router_qabls.iter() { + for res in tables.hat.router_qabls.iter() { for qabl in res.context().router_qabls.keys() { if qabl == node { qabls.push(res.clone()); @@ -1181,7 +1161,7 @@ pub(crate) fn queries_remove_node(tables: &mut Tables, node: &ZenohId, net_type: } WhatAmI::Peer => { let mut qabls = vec![]; - for res in tables.router_qabls.iter() { + for res in tables.hat.router_qabls.iter() { for qabl in res.context().router_qabls.keys() { if qabl == node { qabls.push(res.clone()); @@ -1217,7 +1197,7 @@ pub(crate) fn queries_remove_node(tables: &mut Tables, node: &ZenohId, net_type: pub(crate) fn queries_linkstate_change(tables: &mut Tables, zid: &ZenohId, links: &[ZenohId]) { if let Some(src_face) = tables.get_face(zid) { - if tables.router_peers_failover_brokering + if tables.hat.router_peers_failover_brokering && tables.whatami == WhatAmI::Router && src_face.whatami == WhatAmI::Peer { @@ -1234,9 +1214,10 @@ pub(crate) fn queries_linkstate_change(tables: &mut Tables, zid: &ZenohId, links let dst_face = &mut get_mut_unchecked(ctx).face; if dst_face.whatami == WhatAmI::Peer && src_face.zid != dst_face.zid { if dst_face.local_qabls.contains_key(res) { - let forget = !Tables::failover_brokering_to(links, dst_face.zid) + let forget = !HatTables::failover_brokering_to(links, dst_face.zid) && { let ctx_links = tables + .hat .peers_net .as_ref() .map(|net| net.get_links(dst_face.zid)) @@ -1244,7 +1225,7 @@ pub(crate) fn queries_linkstate_change(tables: &mut Tables, zid: &ZenohId, links res.session_ctxs.values().any(|ctx2| { ctx2.face.whatami == WhatAmI::Peer && ctx2.qabl.is_some() - && Tables::failover_brokering_to( + && HatTables::failover_brokering_to( ctx_links, ctx2.face.zid, ) @@ -1264,7 +1245,7 @@ pub(crate) fn queries_linkstate_change(tables: &mut Tables, zid: &ZenohId, links get_mut_unchecked(dst_face).local_qabls.remove(res); } - } else if Tables::failover_brokering_to(links, ctx.face.zid) { + } else if HatTables::failover_brokering_to(links, ctx.face.zid) { let dst_face = &mut get_mut_unchecked(ctx).face; let info = local_qabl_info(tables, res, dst_face); get_mut_unchecked(dst_face) @@ -1298,14 +1279,14 @@ pub(crate) fn queries_tree_change( // propagate qabls to new childs for (tree_sid, tree_childs) in new_childs.iter().enumerate() { if !tree_childs.is_empty() { - let net = tables.get_net(net_type).unwrap(); + let net = tables.hat.get_net(net_type).unwrap(); let tree_idx = NodeIndex::new(tree_sid); if net.graph.contains_node(tree_idx) { let tree_id = net.graph[tree_idx].zid; let qabls_res = match net_type { - WhatAmI::Router => &tables.router_qabls, - _ => &tables.peer_qabls, + WhatAmI::Router => &tables.hat.router_qabls, + _ => &tables.hat.peer_qabls, }; for res in qabls_res { @@ -1332,1015 +1313,3 @@ pub(crate) fn queries_tree_change( // recompute routes compute_query_routes_from(tables, &mut tables.root_res.clone()); } - -#[inline] -#[allow(clippy::too_many_arguments)] -fn insert_target_for_qabls( - route: &mut QueryTargetQablSet, - expr: &mut RoutingExpr, - tables: &Tables, - net: &Network, - source: usize, - qabls: &HashMap, - complete: bool, -) { - if net.trees.len() > source { - for (qabl, qabl_info) in qabls { - if let Some(qabl_idx) = net.get_idx(qabl) { - if net.trees[source].directions.len() > qabl_idx.index() { - if let Some(direction) = net.trees[source].directions[qabl_idx.index()] { - if net.graph.contains_node(direction) { - if let Some(face) = tables.get_face(&net.graph[direction].zid) { - if net.distances.len() > qabl_idx.index() { - let key_expr = - Resource::get_best_key(expr.prefix, expr.suffix, face.id); - route.push(QueryTargetQabl { - direction: ( - face.clone(), - key_expr.to_owned(), - if source != 0 { - Some(source as u16) - } else { - None - }, - ), - complete: if complete { - qabl_info.complete as u64 - } else { - 0 - }, - distance: net.distances[qabl_idx.index()], - }); - } - } - } - } - } - } - } - } else { - log::trace!("Tree for node sid:{} not yet ready", source); - } -} - -lazy_static::lazy_static! { - static ref EMPTY_ROUTE: Arc = Arc::new(Vec::new()); -} -fn compute_query_route( - tables: &Tables, - expr: &mut RoutingExpr, - source: Option, - source_type: WhatAmI, -) -> Arc { - let mut route = QueryTargetQablSet::new(); - let key_expr = expr.full_expr(); - if key_expr.ends_with('/') { - return EMPTY_ROUTE.clone(); - } - log::trace!( - "compute_query_route({}, {:?}, {:?})", - key_expr, - source, - source_type - ); - let key_expr = match OwnedKeyExpr::try_from(key_expr) { - Ok(ke) => ke, - Err(e) => { - log::warn!("Invalid KE reached the system: {}", e); - return EMPTY_ROUTE.clone(); - } - }; - let res = Resource::get_resource(expr.prefix, expr.suffix); - let matches = res - .as_ref() - .and_then(|res| res.context.as_ref()) - .map(|ctx| Cow::from(&ctx.matches)) - .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &key_expr))); - - let master = tables.whatami != WhatAmI::Router - || !tables.full_net(WhatAmI::Peer) - || *tables.elect_router(&key_expr, tables.shared_nodes.iter()) == tables.zid; - - for mres in matches.iter() { - let mres = mres.upgrade().unwrap(); - let complete = DEFAULT_INCLUDER.includes(mres.expr().as_bytes(), key_expr.as_bytes()); - if tables.whatami == WhatAmI::Router { - if master || source_type == WhatAmI::Router { - let net = tables.routers_net.as_ref().unwrap(); - let router_source = match source_type { - WhatAmI::Router => source.unwrap(), - _ => net.idx.index(), - }; - insert_target_for_qabls( - &mut route, - expr, - tables, - net, - router_source, - &mres.context().router_qabls, - complete, - ); - } - - if (master || source_type != WhatAmI::Router) && tables.full_net(WhatAmI::Peer) { - let net = tables.peers_net.as_ref().unwrap(); - let peer_source = match source_type { - WhatAmI::Peer => source.unwrap(), - _ => net.idx.index(), - }; - insert_target_for_qabls( - &mut route, - expr, - tables, - net, - peer_source, - &mres.context().peer_qabls, - complete, - ); - } - } - - if tables.whatami == WhatAmI::Peer && tables.full_net(WhatAmI::Peer) { - let net = tables.peers_net.as_ref().unwrap(); - let peer_source = match source_type { - WhatAmI::Router | WhatAmI::Peer => source.unwrap(), - _ => net.idx.index(), - }; - insert_target_for_qabls( - &mut route, - expr, - tables, - net, - peer_source, - &mres.context().peer_qabls, - complete, - ); - } - - if tables.whatami != WhatAmI::Router || master || source_type == WhatAmI::Router { - for (sid, context) in &mres.session_ctxs { - if match tables.whatami { - WhatAmI::Router => context.face.whatami != WhatAmI::Router, - _ => source_type == WhatAmI::Client || context.face.whatami == WhatAmI::Client, - } { - let key_expr = Resource::get_best_key(expr.prefix, expr.suffix, *sid); - if let Some(qabl_info) = context.qabl.as_ref() { - route.push(QueryTargetQabl { - direction: (context.face.clone(), key_expr.to_owned(), None), - complete: if complete { - qabl_info.complete as u64 - } else { - 0 - }, - distance: 0.5, - }); - } - } - } - } - } - route.sort_by_key(|qabl| OrderedFloat(qabl.distance)); - Arc::new(route) -} - -pub(super) fn compute_query_routes_(tables: &Tables, res: &Arc) -> QueryRoutes { - let mut routes = QueryRoutes { - routers_query_routes: vec![], - peers_query_routes: vec![], - peer_query_route: None, - client_query_route: None, - }; - let mut expr = RoutingExpr::new(res, ""); - if tables.whatami == WhatAmI::Router { - let indexes = tables - .routers_net - .as_ref() - .unwrap() - .graph - .node_indices() - .collect::>(); - let max_idx = indexes.iter().max().unwrap(); - routes - .routers_query_routes - .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); - - for idx in &indexes { - routes.routers_query_routes[idx.index()] = - compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); - } - - routes.peer_query_route = Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); - } - if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) - && tables.full_net(WhatAmI::Peer) - { - let indexes = tables - .peers_net - .as_ref() - .unwrap() - .graph - .node_indices() - .collect::>(); - let max_idx = indexes.iter().max().unwrap(); - routes - .peers_query_routes - .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); - - for idx in &indexes { - routes.peers_query_routes[idx.index()] = - compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); - } - } - if tables.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { - routes.client_query_route = Some(compute_query_route( - tables, - &mut expr, - None, - WhatAmI::Client, - )); - routes.peer_query_route = Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); - } - if tables.whatami == WhatAmI::Client { - routes.client_query_route = Some(compute_query_route( - tables, - &mut expr, - None, - WhatAmI::Client, - )); - } - routes -} - -pub(crate) fn compute_query_routes(tables: &mut Tables, res: &mut Arc) { - if res.context.is_some() { - let mut res_mut = res.clone(); - let res_mut = get_mut_unchecked(&mut res_mut); - let mut expr = RoutingExpr::new(res, ""); - if tables.whatami == WhatAmI::Router { - let indexes = tables - .routers_net - .as_ref() - .unwrap() - .graph - .node_indices() - .collect::>(); - let max_idx = indexes.iter().max().unwrap(); - let routers_query_routes = &mut res_mut.context_mut().routers_query_routes; - routers_query_routes.clear(); - routers_query_routes - .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); - - for idx in &indexes { - routers_query_routes[idx.index()] = - compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Router); - } - - res_mut.context_mut().peer_query_route = - Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); - } - if (tables.whatami == WhatAmI::Router || tables.whatami == WhatAmI::Peer) - && tables.full_net(WhatAmI::Peer) - { - let indexes = tables - .peers_net - .as_ref() - .unwrap() - .graph - .node_indices() - .collect::>(); - let max_idx = indexes.iter().max().unwrap(); - let peers_query_routes = &mut res_mut.context_mut().peers_query_routes; - peers_query_routes.clear(); - peers_query_routes - .resize_with(max_idx.index() + 1, || Arc::new(QueryTargetQablSet::new())); - - for idx in &indexes { - peers_query_routes[idx.index()] = - compute_query_route(tables, &mut expr, Some(idx.index()), WhatAmI::Peer); - } - } - if tables.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { - res_mut.context_mut().client_query_route = Some(compute_query_route( - tables, - &mut expr, - None, - WhatAmI::Client, - )); - res_mut.context_mut().peer_query_route = - Some(compute_query_route(tables, &mut expr, None, WhatAmI::Peer)); - } - if tables.whatami == WhatAmI::Client { - res_mut.context_mut().client_query_route = Some(compute_query_route( - tables, - &mut expr, - None, - WhatAmI::Client, - )); - } - } -} - -fn compute_query_routes_from(tables: &mut Tables, res: &mut Arc) { - compute_query_routes(tables, res); - let res = get_mut_unchecked(res); - for child in res.childs.values_mut() { - compute_query_routes_from(tables, child); - } -} - -pub(super) fn compute_matches_query_routes_( - tables: &Tables, - res: &Arc, -) -> Vec<(Arc, QueryRoutes)> { - let mut routes = vec![]; - if res.context.is_some() { - routes.push((res.clone(), compute_query_routes_(tables, res))); - for match_ in &res.context().matches { - let match_ = match_.upgrade().unwrap(); - if !Arc::ptr_eq(&match_, res) { - let match_routes = compute_query_routes_(tables, &match_); - routes.push((match_, match_routes)); - } - } - } - routes -} - -#[inline] -fn insert_pending_query(outface: &mut Arc, query: Arc) -> RequestId { - let outface_mut = get_mut_unchecked(outface); - outface_mut.next_qid += 1; - let qid = outface_mut.next_qid; - outface_mut.pending_queries.insert(qid, query); - qid -} - -#[inline] -fn should_route( - tables: &Tables, - src_face: &FaceState, - outface: &Arc, - expr: &mut RoutingExpr, -) -> bool { - if src_face.id != outface.id { - let dst_master = tables.whatami != WhatAmI::Router - || outface.whatami != WhatAmI::Peer - || tables.peers_net.is_none() - || tables.zid - == *tables.elect_router(expr.full_expr(), tables.get_router_links(outface.zid)); - - return dst_master - && (src_face.whatami != WhatAmI::Peer - || outface.whatami != WhatAmI::Peer - || tables.full_net(WhatAmI::Peer) - || tables.failover_brokering(src_face.zid, outface.zid)); - } - false -} - -#[inline] -fn compute_final_route( - tables: &Tables, - qabls: &Arc, - src_face: &Arc, - expr: &mut RoutingExpr, - target: &TargetType, - query: Arc, -) -> QueryRoute { - match target { - TargetType::All => { - let mut route = HashMap::new(); - for qabl in qabls.iter() { - if should_route(tables, src_face, &qabl.direction.0, expr) { - #[cfg(feature = "complete_n")] - { - route.entry(qabl.direction.0.id).or_insert_with(|| { - let mut direction = qabl.direction.clone(); - let qid = insert_pending_query(&mut direction.0, query.clone()); - (direction, qid, *target) - }); - } - #[cfg(not(feature = "complete_n"))] - { - route.entry(qabl.direction.0.id).or_insert_with(|| { - let mut direction = qabl.direction.clone(); - let qid = insert_pending_query(&mut direction.0, query.clone()); - (direction, qid) - }); - } - } - } - route - } - TargetType::AllComplete => { - let mut route = HashMap::new(); - for qabl in qabls.iter() { - if qabl.complete > 0 && should_route(tables, src_face, &qabl.direction.0, expr) { - #[cfg(feature = "complete_n")] - { - route.entry(qabl.direction.0.id).or_insert_with(|| { - let mut direction = qabl.direction.clone(); - let qid = insert_pending_query(&mut direction.0, query.clone()); - (direction, qid, *target) - }); - } - #[cfg(not(feature = "complete_n"))] - { - route.entry(qabl.direction.0.id).or_insert_with(|| { - let mut direction = qabl.direction.clone(); - let qid = insert_pending_query(&mut direction.0, query.clone()); - (direction, qid) - }); - } - } - } - route - } - #[cfg(feature = "complete_n")] - TargetType::Complete(n) => { - let mut route = HashMap::new(); - let mut remaining = *n; - if src_face.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { - let source_links = tables - .peers_net - .as_ref() - .map(|net| net.get_links(src_face.zid)) - .unwrap_or_default(); - for qabl in qabls.iter() { - if qabl.direction.0.id != src_face.id - && qabl.complete > 0 - && (qabl.direction.0.whatami != WhatAmI::Peer - || (tables.router_peers_failover_brokering - && Tables::failover_brokering_to( - source_links, - qabl.direction.0.zid, - ))) - { - let nb = std::cmp::min(qabl.complete, remaining); - route.entry(qabl.direction.0.id).or_insert_with(|| { - let mut direction = qabl.direction.clone(); - let qid = insert_pending_query(&mut direction.0, query.clone()); - (direction, qid, TargetType::Complete(nb)) - }); - remaining -= nb; - if remaining == 0 { - break; - } - } - } - } else { - for qabl in qabls.iter() { - if qabl.direction.0.id != src_face.id && qabl.complete > 0 { - let nb = std::cmp::min(qabl.complete, remaining); - route.entry(qabl.direction.0.id).or_insert_with(|| { - let mut direction = qabl.direction.clone(); - let qid = insert_pending_query(&mut direction.0, query.clone()); - (direction, qid, TargetType::Complete(nb)) - }); - remaining -= nb; - if remaining == 0 { - break; - } - } - } - } - route - } - TargetType::BestMatching => { - if let Some(qabl) = qabls - .iter() - .find(|qabl| qabl.direction.0.id != src_face.id && qabl.complete > 0) - { - let mut route = HashMap::new(); - #[cfg(feature = "complete_n")] - { - let mut direction = qabl.direction.clone(); - let qid = insert_pending_query(&mut direction.0, query); - route.insert(direction.0.id, (direction, qid, *target)); - } - #[cfg(not(feature = "complete_n"))] - { - let mut direction = qabl.direction.clone(); - let qid = insert_pending_query(&mut direction.0, query); - route.insert(direction.0.id, (direction, qid)); - } - route - } else { - compute_final_route(tables, qabls, src_face, expr, &TargetType::All, query) - } - } - } -} - -#[inline] -fn compute_local_replies( - tables: &Tables, - prefix: &Arc, - suffix: &str, - face: &Arc, -) -> Vec<(WireExpr<'static>, ZBuf)> { - let mut result = vec![]; - // Only the first routing point in the query route - // should return the liveliness tokens - if face.whatami == WhatAmI::Client { - let key_expr = prefix.expr() + suffix; - let key_expr = match OwnedKeyExpr::try_from(key_expr) { - Ok(ke) => ke, - Err(e) => { - log::warn!("Invalid KE reached the system: {}", e); - return result; - } - }; - if key_expr.starts_with(super::PREFIX_LIVELINESS) { - let res = Resource::get_resource(prefix, suffix); - let matches = res - .as_ref() - .and_then(|res| res.context.as_ref()) - .map(|ctx| Cow::from(&ctx.matches)) - .unwrap_or_else(|| Cow::from(Resource::get_matches(tables, &key_expr))); - for mres in matches.iter() { - let mres = mres.upgrade().unwrap(); - if (mres.context.is_some() - && (!mres.context().router_subs.is_empty() - || !mres.context().peer_subs.is_empty())) - || mres.session_ctxs.values().any(|ctx| ctx.subs.is_some()) - { - result.push((Resource::get_best_key(&mres, "", face.id), ZBuf::default())); - } - } - } - } - result -} - -#[derive(Clone)] -struct QueryCleanup { - tables: Arc, - face: Weak, - qid: RequestId, -} - -#[async_trait] -impl Timed for QueryCleanup { - async fn run(&mut self) { - if let Some(mut face) = self.face.upgrade() { - let tables_lock = zwrite!(self.tables.tables); - if let Some(query) = get_mut_unchecked(&mut face) - .pending_queries - .remove(&self.qid) - { - drop(tables_lock); - log::warn!( - "Didn't receive final reply {}:{} from {}: Timeout!", - query.src_face, - self.qid, - face - ); - finalize_pending_query(query); - } - } - } -} - -pub(super) fn disable_matches_query_routes(_tables: &mut Tables, res: &mut Arc) { - if res.context.is_some() { - get_mut_unchecked(res).context_mut().valid_query_routes = false; - for match_ in &res.context().matches { - let mut match_ = match_.upgrade().unwrap(); - if !Arc::ptr_eq(&match_, res) { - get_mut_unchecked(&mut match_) - .context_mut() - .valid_query_routes = false; - } - } - } -} - -#[inline] -fn get_query_route( - tables: &Tables, - face: &FaceState, - res: &Option>, - expr: &mut RoutingExpr, - routing_context: u64, -) -> Arc { - match tables.whatami { - WhatAmI::Router => match face.whatami { - WhatAmI::Router => { - let routers_net = tables.routers_net.as_ref().unwrap(); - let local_context = routers_net.get_local_context(routing_context, face.link_id); - res.as_ref() - .and_then(|res| res.routers_query_route(local_context)) - .unwrap_or_else(|| { - compute_query_route(tables, expr, Some(local_context), face.whatami) - }) - } - WhatAmI::Peer => { - if tables.full_net(WhatAmI::Peer) { - let peers_net = tables.peers_net.as_ref().unwrap(); - let local_context = peers_net.get_local_context(routing_context, face.link_id); - res.as_ref() - .and_then(|res| res.peers_query_route(local_context)) - .unwrap_or_else(|| { - compute_query_route(tables, expr, Some(local_context), face.whatami) - }) - } else { - res.as_ref() - .and_then(|res| res.peer_query_route()) - .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)) - } - } - _ => res - .as_ref() - .and_then(|res| res.routers_query_route(0)) - .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)), - }, - WhatAmI::Peer => { - if tables.full_net(WhatAmI::Peer) { - match face.whatami { - WhatAmI::Router | WhatAmI::Peer => { - let peers_net = tables.peers_net.as_ref().unwrap(); - let local_context = - peers_net.get_local_context(routing_context, face.link_id); - res.as_ref() - .and_then(|res| res.peers_query_route(local_context)) - .unwrap_or_else(|| { - compute_query_route(tables, expr, Some(local_context), face.whatami) - }) - } - _ => res - .as_ref() - .and_then(|res| res.peers_query_route(0)) - .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)), - } - } else { - res.as_ref() - .and_then(|res| match face.whatami { - WhatAmI::Client => res.client_query_route(), - _ => res.peer_query_route(), - }) - .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)) - } - } - _ => res - .as_ref() - .and_then(|res| res.client_query_route()) - .unwrap_or_else(|| compute_query_route(tables, expr, None, face.whatami)), - } -} - -#[cfg(feature = "stats")] -macro_rules! inc_req_stats { - ( - $face:expr, - $txrx:ident, - $space:ident, - $body:expr - ) => { - paste::paste! { - if let Some(stats) = $face.stats.as_ref() { - use zenoh_buffers::SplitBuffer; - match &$body { - RequestBody::Put(p) => { - stats.[<$txrx _z_put_msgs>].[](1); - stats.[<$txrx _z_put_pl_bytes>].[](p.payload.len()); - } - RequestBody::Del(_) => { - stats.[<$txrx _z_del_msgs>].[](1); - } - RequestBody::Query(q) => { - stats.[<$txrx _z_query_msgs>].[](1); - stats.[<$txrx _z_query_pl_bytes>].[]( - q.ext_body.as_ref().map(|b| b.payload.len()).unwrap_or(0), - ); - } - RequestBody::Pull(_) => (), - } - } - } - }; -} - -#[cfg(feature = "stats")] -macro_rules! inc_res_stats { - ( - $face:expr, - $txrx:ident, - $space:ident, - $body:expr - ) => { - paste::paste! { - if let Some(stats) = $face.stats.as_ref() { - use zenoh_buffers::SplitBuffer; - match &$body { - ResponseBody::Put(p) => { - stats.[<$txrx _z_put_msgs>].[](1); - stats.[<$txrx _z_put_pl_bytes>].[](p.payload.len()); - } - ResponseBody::Reply(r) => { - stats.[<$txrx _z_reply_msgs>].[](1); - stats.[<$txrx _z_reply_pl_bytes>].[](r.payload.len()); - } - ResponseBody::Err(e) => { - stats.[<$txrx _z_reply_msgs>].[](1); - stats.[<$txrx _z_reply_pl_bytes>].[]( - e.ext_body.as_ref().map(|b| b.payload.len()).unwrap_or(0), - ); - } - ResponseBody::Ack(_) => (), - } - } - } - }; -} - -#[allow(clippy::too_many_arguments)] -pub fn route_query( - tables_ref: &Arc, - face: &Arc, - expr: &WireExpr, - qid: RequestId, - target: TargetType, - body: RequestBody, - routing_context: u64, -) { - let rtables = zread!(tables_ref.tables); - match rtables.get_mapping(face, &expr.scope, expr.mapping) { - Some(prefix) => { - log::debug!( - "Route query {}:{} for res {}{}", - face, - qid, - prefix.expr(), - expr.suffix.as_ref(), - ); - let prefix = prefix.clone(); - let mut expr = RoutingExpr::new(&prefix, expr.suffix.as_ref()); - - #[cfg(feature = "stats")] - let admin = expr.full_expr().starts_with("@/"); - #[cfg(feature = "stats")] - if !admin { - inc_req_stats!(face, rx, user, body) - } else { - inc_req_stats!(face, rx, admin, body) - } - - if rtables.whatami != WhatAmI::Router - || face.whatami != WhatAmI::Peer - || rtables.peers_net.is_none() - || rtables.zid - == *rtables.elect_router(expr.full_expr(), rtables.get_router_links(face.zid)) - { - let res = Resource::get_resource(&prefix, expr.suffix); - let route = get_query_route(&rtables, face, &res, &mut expr, routing_context); - - let query = Arc::new(Query { - src_face: face.clone(), - src_qid: qid, - }); - - let queries_lock = zwrite!(tables_ref.queries_lock); - let route = compute_final_route(&rtables, &route, face, &mut expr, &target, query); - let local_replies = compute_local_replies(&rtables, &prefix, expr.suffix, face); - let zid = rtables.zid; - - drop(queries_lock); - drop(rtables); - - for (expr, payload) in local_replies { - let payload = ResponseBody::Reply(Reply { - timestamp: None, - encoding: Encoding::default(), - ext_sinfo: None, - ext_consolidation: ConsolidationType::default(), - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload, - }); - #[cfg(feature = "stats")] - if !admin { - inc_res_stats!(face, tx, user, payload) - } else { - inc_res_stats!(face, tx, admin, payload) - } - - face.primitives.clone().send_response(Response { - rid: qid, - wire_expr: expr, - payload, - ext_qos: response::ext::QoSType::declare_default(), - ext_tstamp: None, - ext_respid: Some(response::ext::ResponderIdType { - zid, - eid: 0, // TODO - }), - }); - } - - if route.is_empty() { - log::debug!( - "Send final reply {}:{} (no matching queryables or not master)", - face, - qid - ); - face.primitives.clone().send_response_final(ResponseFinal { - rid: qid, - ext_qos: response::ext::QoSType::response_final_default(), - ext_tstamp: None, - }); - } else { - // let timer = tables.timer.clone(); - // let timeout = tables.queries_default_timeout; - #[cfg(feature = "complete_n")] - { - for ((outface, key_expr, context), qid, t) in route.values() { - // timer.add(TimedEvent::once( - // Instant::now() + timeout, - // QueryCleanup { - // tables: tables_ref.clone(), - // face: Arc::downgrade(&outface), - // *qid, - // }, - // )); - #[cfg(feature = "stats")] - if !admin { - inc_req_stats!(outface, tx, user, body) - } else { - inc_req_stats!(outface, tx, admin, body) - } - - log::trace!("Propagate query {}:{} to {}", face, qid, outface); - outface.primitives.send_request(Request { - id: *qid, - wire_expr: key_expr.into(), - ext_qos: ext::QoSType::request_default(), // TODO - ext_tstamp: None, - ext_nodeid: ext::NodeIdType { - node_id: context.unwrap_or(0), - }, - ext_target: *t, - ext_budget: None, - ext_timeout: None, - payload: body.clone(), - }); - } - } - - #[cfg(not(feature = "complete_n"))] - { - for ((outface, key_expr, context), qid) in route.values() { - // timer.add(TimedEvent::once( - // Instant::now() + timeout, - // QueryCleanup { - // tables: tables_ref.clone(), - // face: Arc::downgrade(&outface), - // *qid, - // }, - // )); - #[cfg(feature = "stats")] - if !admin { - inc_req_stats!(outface, tx, user, body) - } else { - inc_req_stats!(outface, tx, admin, body) - } - - log::trace!("Propagate query {}:{} to {}", face, qid, outface); - outface.primitives.send_request(Request { - id: *qid, - wire_expr: key_expr.into(), - ext_qos: ext::QoSType::request_default(), - ext_tstamp: None, - ext_nodeid: ext::NodeIdType { - node_id: context.unwrap_or(0), - }, - ext_target: target, - ext_budget: None, - ext_timeout: None, - payload: body.clone(), - }); - } - } - } - } else { - log::debug!("Send final reply {}:{} (not master)", face, qid); - drop(rtables); - face.primitives.clone().send_response_final(ResponseFinal { - rid: qid, - ext_qos: response::ext::QoSType::response_final_default(), - ext_tstamp: None, - }); - } - } - None => { - log::error!( - "Route query with unknown scope {}! Send final reply.", - expr.scope - ); - drop(rtables); - face.primitives.clone().send_response_final(ResponseFinal { - rid: qid, - ext_qos: response::ext::QoSType::response_final_default(), - ext_tstamp: None, - }); - } - } -} - -#[allow(clippy::too_many_arguments)] -pub(crate) fn route_send_response( - tables_ref: &Arc, - face: &mut Arc, - qid: RequestId, - ext_respid: Option, - key_expr: WireExpr, - body: ResponseBody, -) { - let queries_lock = zread!(tables_ref.queries_lock); - #[cfg(feature = "stats")] - let admin = key_expr.as_str().starts_with("@/"); - #[cfg(feature = "stats")] - if !admin { - inc_res_stats!(face, rx, user, body) - } else { - inc_res_stats!(face, rx, admin, body) - } - - match face.pending_queries.get(&qid) { - Some(query) => { - drop(queries_lock); - - #[cfg(feature = "stats")] - if !admin { - inc_res_stats!(query.src_face, tx, user, body) - } else { - inc_res_stats!(query.src_face, tx, admin, body) - } - - query.src_face.primitives.clone().send_response(Response { - rid: query.src_qid, - wire_expr: key_expr.to_owned(), - payload: body, - ext_qos: response::ext::QoSType::response_default(), - ext_tstamp: None, - ext_respid, - }); - } - None => log::warn!( - "Route reply {}:{} from {}: Query nof found!", - face, - qid, - face - ), - } -} - -pub(crate) fn route_send_response_final( - tables_ref: &Arc, - face: &mut Arc, - qid: RequestId, -) { - let queries_lock = zwrite!(tables_ref.queries_lock); - match get_mut_unchecked(face).pending_queries.remove(&qid) { - Some(query) => { - drop(queries_lock); - log::debug!( - "Received final reply {}:{} from {}", - query.src_face, - qid, - face - ); - finalize_pending_query(query); - } - None => log::warn!( - "Route final reply {}:{} from {}: Query nof found!", - face, - qid, - face - ), - } -} - -pub(crate) fn finalize_pending_queries(tables_ref: &TablesLock, face: &mut Arc) { - let queries_lock = zwrite!(tables_ref.queries_lock); - for (_, query) in get_mut_unchecked(face).pending_queries.drain() { - finalize_pending_query(query); - } - drop(queries_lock); -} - -pub(crate) fn finalize_pending_query(query: Arc) { - if let Some(query) = Arc::into_inner(query) { - log::debug!("Propagate final reply {}:{}", query.src_face, query.src_qid); - query - .src_face - .primitives - .clone() - .send_response_final(ResponseFinal { - rid: query.src_qid, - ext_qos: response::ext::QoSType::response_final_default(), - ext_tstamp: None, - }); - } -} diff --git a/zenoh/src/net/routing/mod.rs b/zenoh/src/net/routing/mod.rs index 886b0b50dc..c0da3bc0a0 100644 --- a/zenoh/src/net/routing/mod.rs +++ b/zenoh/src/net/routing/mod.rs @@ -17,11 +17,8 @@ //! This module is intended for Zenoh's internal use. //! //! [Click here for Zenoh's documentation](../zenoh/index.html) -pub mod face; -pub mod network; -pub mod pubsub; -pub mod queries; -pub mod resource; +pub mod dispatcher; +pub mod hat; pub mod router; use super::runtime; diff --git a/zenoh/src/net/routing/router.rs b/zenoh/src/net/routing/router.rs index dbf687ba79..06745461e5 100644 --- a/zenoh/src/net/routing/router.rs +++ b/zenoh/src/net/routing/router.rs @@ -11,472 +11,35 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::face::{Face, FaceState}; -use super::network::{shared_nodes, Network}; -pub use super::pubsub::*; -pub use super::queries::*; -pub use super::resource::*; +use super::dispatcher::face::{Face, FaceState}; +pub use super::dispatcher::pubsub::*; +pub use super::dispatcher::queries::*; +pub use super::dispatcher::resource::*; +use super::dispatcher::tables::Tables; +use super::dispatcher::tables::TablesLock; +use super::hat::network::{shared_nodes, Network}; +pub use super::hat::pubsub::*; +pub use super::hat::queries::*; use super::runtime::Runtime; use crate::net::codec::Zenoh080Routing; use crate::net::protocol::linkstate::LinkStateList; -use async_std::task::JoinHandle; use std::any::Any; -use std::collections::hash_map::DefaultHasher; -use std::collections::{HashMap, HashSet}; -use std::hash::Hasher; use std::str::FromStr; -use std::sync::{Arc, Weak}; +use std::sync::Arc; use std::sync::{Mutex, RwLock}; use std::time::Duration; use uhlc::HLC; use zenoh_link::Link; use zenoh_protocol::common::ZExtBody; -use zenoh_protocol::core::{ExprId, WhatAmI, WhatAmIMatcher, ZenohId}; +use zenoh_protocol::core::{WhatAmI, WhatAmIMatcher, ZenohId}; use zenoh_protocol::network::oam::id::OAM_LINKSTATE; -use zenoh_protocol::network::{Mapping, NetworkBody, NetworkMessage}; -#[cfg(feature = "stats")] -use zenoh_transport::stats::TransportStats; +use zenoh_protocol::network::{NetworkBody, NetworkMessage}; use zenoh_transport::{ DeMux, DummyPrimitives, McastMux, Mux, Primitives, TransportMulticast, TransportPeer, TransportPeerEventHandler, TransportUnicast, }; // use zenoh_collections::Timer; -use zenoh_core::zconfigurable; use zenoh_result::ZResult; -use zenoh_sync::get_mut_unchecked; - -zconfigurable! { - static ref TREES_COMPUTATION_DELAY: u64 = 100; -} - -pub(crate) struct RoutingExpr<'a> { - pub(crate) prefix: &'a Arc, - pub(crate) suffix: &'a str, - full: Option, -} - -impl<'a> RoutingExpr<'a> { - #[inline] - pub(crate) fn new(prefix: &'a Arc, suffix: &'a str) -> Self { - RoutingExpr { - prefix, - suffix, - full: None, - } - } - - #[inline] - pub(crate) fn full_expr(&mut self) -> &str { - if self.full.is_none() { - self.full = Some(self.prefix.expr() + self.suffix); - } - self.full.as_ref().unwrap() - } -} - -pub struct Tables { - pub(crate) zid: ZenohId, - pub(crate) whatami: WhatAmI, - face_counter: usize, - #[allow(dead_code)] - pub(crate) hlc: Option>, - pub(crate) drop_future_timestamp: bool, - pub(crate) router_peers_failover_brokering: bool, - // pub(crate) timer: Timer, - // pub(crate) queries_default_timeout: Duration, - pub(crate) root_res: Arc, - pub(crate) faces: HashMap>, - pub(crate) mcast_groups: Vec>, - pub(crate) mcast_faces: Vec>, - pub(crate) pull_caches_lock: Mutex<()>, - pub(crate) router_subs: HashSet>, - pub(crate) peer_subs: HashSet>, - pub(crate) router_qabls: HashSet>, - pub(crate) peer_qabls: HashSet>, - pub(crate) routers_net: Option, - pub(crate) peers_net: Option, - pub(crate) shared_nodes: Vec, - pub(crate) routers_trees_task: Option>, - pub(crate) peers_trees_task: Option>, -} - -impl Tables { - pub fn new( - zid: ZenohId, - whatami: WhatAmI, - hlc: Option>, - drop_future_timestamp: bool, - router_peers_failover_brokering: bool, - _queries_default_timeout: Duration, - ) -> Self { - Tables { - zid, - whatami, - face_counter: 0, - hlc, - drop_future_timestamp, - router_peers_failover_brokering, - // timer: Timer::new(true), - // queries_default_timeout, - root_res: Resource::root(), - faces: HashMap::new(), - mcast_groups: vec![], - mcast_faces: vec![], - pull_caches_lock: Mutex::new(()), - router_subs: HashSet::new(), - peer_subs: HashSet::new(), - router_qabls: HashSet::new(), - peer_qabls: HashSet::new(), - routers_net: None, - peers_net: None, - shared_nodes: vec![], - routers_trees_task: None, - peers_trees_task: None, - } - } - - #[doc(hidden)] - pub fn _get_root(&self) -> &Arc { - &self.root_res - } - - pub fn print(&self) -> String { - Resource::print_tree(&self.root_res) - } - - #[inline] - #[allow(clippy::trivially_copy_pass_by_ref)] - pub(crate) fn get_mapping<'a>( - &'a self, - face: &'a FaceState, - expr_id: &ExprId, - mapping: Mapping, - ) -> Option<&'a Arc> { - match expr_id { - 0 => Some(&self.root_res), - expr_id => face.get_mapping(expr_id, mapping), - } - } - - #[inline] - pub(crate) fn get_net(&self, net_type: WhatAmI) -> Option<&Network> { - match net_type { - WhatAmI::Router => self.routers_net.as_ref(), - WhatAmI::Peer => self.peers_net.as_ref(), - _ => None, - } - } - - #[inline] - pub(crate) fn full_net(&self, net_type: WhatAmI) -> bool { - match net_type { - WhatAmI::Router => self - .routers_net - .as_ref() - .map(|net| net.full_linkstate) - .unwrap_or(false), - WhatAmI::Peer => self - .peers_net - .as_ref() - .map(|net| net.full_linkstate) - .unwrap_or(false), - _ => false, - } - } - - #[inline] - pub(crate) fn get_face(&self, zid: &ZenohId) -> Option<&Arc> { - self.faces.values().find(|face| face.zid == *zid) - } - - #[inline] - pub(crate) fn get_router_links(&self, peer: ZenohId) -> impl Iterator + '_ { - self.peers_net - .as_ref() - .unwrap() - .get_links(peer) - .iter() - .filter(move |nid| { - if let Some(node) = self.routers_net.as_ref().unwrap().get_node(nid) { - node.whatami.unwrap_or(WhatAmI::Router) == WhatAmI::Router - } else { - false - } - }) - } - - #[inline] - pub(crate) fn elect_router<'a>( - &'a self, - key_expr: &str, - mut routers: impl Iterator, - ) -> &'a ZenohId { - match routers.next() { - None => &self.zid, - Some(router) => { - let hash = |r: &ZenohId| { - let mut hasher = DefaultHasher::new(); - for b in key_expr.as_bytes() { - hasher.write_u8(*b); - } - for b in &r.to_le_bytes()[..r.size()] { - hasher.write_u8(*b); - } - hasher.finish() - }; - let mut res = router; - let mut h = None; - for router2 in routers { - let h2 = hash(router2); - if h2 > *h.get_or_insert_with(|| hash(res)) { - res = router2; - h = Some(h2); - } - } - res - } - } - } - - #[inline] - pub(crate) fn failover_brokering_to(source_links: &[ZenohId], dest: ZenohId) -> bool { - // if source_links is empty then gossip is probably disabled in source peer - !source_links.is_empty() && !source_links.contains(&dest) - } - - #[inline] - pub(crate) fn failover_brokering(&self, peer1: ZenohId, peer2: ZenohId) -> bool { - self.router_peers_failover_brokering - && self - .peers_net - .as_ref() - .map(|net| Tables::failover_brokering_to(net.get_links(peer1), peer2)) - .unwrap_or(false) - } - - fn open_net_face( - &mut self, - zid: ZenohId, - whatami: WhatAmI, - #[cfg(feature = "stats")] stats: Arc, - primitives: Arc, - link_id: usize, - ) -> Weak { - let fid = self.face_counter; - self.face_counter += 1; - let mut newface = self - .faces - .entry(fid) - .or_insert_with(|| { - FaceState::new( - fid, - zid, - whatami, - #[cfg(feature = "stats")] - Some(stats), - primitives.clone(), - link_id, - None, - ) - }) - .clone(); - log::debug!("New {}", newface); - - pubsub_new_face(self, &mut newface); - queries_new_face(self, &mut newface); - - Arc::downgrade(&newface) - } - - pub fn open_face( - &mut self, - zid: ZenohId, - whatami: WhatAmI, - primitives: Arc, - ) -> Weak { - let fid = self.face_counter; - self.face_counter += 1; - let mut newface = self - .faces - .entry(fid) - .or_insert_with(|| { - FaceState::new( - fid, - zid, - whatami, - #[cfg(feature = "stats")] - None, - primitives.clone(), - 0, - None, - ) - }) - .clone(); - log::debug!("New {}", newface); - - pubsub_new_face(self, &mut newface); - queries_new_face(self, &mut newface); - - Arc::downgrade(&newface) - } - - fn compute_routes(&mut self, res: &mut Arc) { - compute_data_routes(self, res); - compute_query_routes(self, res); - } - - pub(crate) fn compute_matches_routes(&mut self, res: &mut Arc) { - if res.context.is_some() { - self.compute_routes(res); - - let resclone = res.clone(); - for match_ in &mut get_mut_unchecked(res).context_mut().matches { - let match_ = &mut match_.upgrade().unwrap(); - if !Arc::ptr_eq(match_, &resclone) && match_.context.is_some() { - self.compute_routes(match_); - } - } - } - } - - pub(crate) fn schedule_compute_trees( - &mut self, - tables_ref: Arc, - net_type: WhatAmI, - ) { - log::trace!("Schedule computations"); - if (net_type == WhatAmI::Router && self.routers_trees_task.is_none()) - || (net_type == WhatAmI::Peer && self.peers_trees_task.is_none()) - { - let task = Some(async_std::task::spawn(async move { - async_std::task::sleep(std::time::Duration::from_millis(*TREES_COMPUTATION_DELAY)) - .await; - let mut tables = zwrite!(tables_ref.tables); - - log::trace!("Compute trees"); - let new_childs = match net_type { - WhatAmI::Router => tables.routers_net.as_mut().unwrap().compute_trees(), - _ => tables.peers_net.as_mut().unwrap().compute_trees(), - }; - - log::trace!("Compute routes"); - pubsub_tree_change(&mut tables, &new_childs, net_type); - queries_tree_change(&mut tables, &new_childs, net_type); - - log::trace!("Computations completed"); - match net_type { - WhatAmI::Router => tables.routers_trees_task = None, - _ => tables.peers_trees_task = None, - }; - })); - match net_type { - WhatAmI::Router => self.routers_trees_task = task, - _ => self.peers_trees_task = task, - }; - } - } -} - -pub fn close_face(tables: &TablesLock, face: &Weak) { - match face.upgrade() { - Some(mut face) => { - log::debug!("Close {}", face); - finalize_pending_queries(tables, &mut face); - - let ctrl_lock = zlock!(tables.ctrl_lock); - let mut wtables = zwrite!(tables.tables); - let mut face_clone = face.clone(); - let face = get_mut_unchecked(&mut face); - for res in face.remote_mappings.values_mut() { - get_mut_unchecked(res).session_ctxs.remove(&face.id); - Resource::clean(res); - } - face.remote_mappings.clear(); - for res in face.local_mappings.values_mut() { - get_mut_unchecked(res).session_ctxs.remove(&face.id); - Resource::clean(res); - } - face.local_mappings.clear(); - - let mut subs_matches = vec![]; - for mut res in face.remote_subs.drain() { - get_mut_unchecked(&mut res).session_ctxs.remove(&face.id); - undeclare_client_subscription(&mut wtables, &mut face_clone, &mut res); - - if res.context.is_some() { - for match_ in &res.context().matches { - let mut match_ = match_.upgrade().unwrap(); - if !Arc::ptr_eq(&match_, &res) { - get_mut_unchecked(&mut match_) - .context_mut() - .valid_data_routes = false; - subs_matches.push(match_); - } - } - get_mut_unchecked(&mut res).context_mut().valid_data_routes = false; - subs_matches.push(res); - } - } - - let mut qabls_matches = vec![]; - for mut res in face.remote_qabls.drain() { - get_mut_unchecked(&mut res).session_ctxs.remove(&face.id); - undeclare_client_queryable(&mut wtables, &mut face_clone, &mut res); - - if res.context.is_some() { - for match_ in &res.context().matches { - let mut match_ = match_.upgrade().unwrap(); - if !Arc::ptr_eq(&match_, &res) { - get_mut_unchecked(&mut match_) - .context_mut() - .valid_query_routes = false; - qabls_matches.push(match_); - } - } - get_mut_unchecked(&mut res).context_mut().valid_query_routes = false; - qabls_matches.push(res); - } - } - drop(wtables); - - let mut matches_data_routes = vec![]; - let mut matches_query_routes = vec![]; - let rtables = zread!(tables.tables); - for _match in subs_matches.drain(..) { - matches_data_routes.push((_match.clone(), compute_data_routes_(&rtables, &_match))); - } - for _match in qabls_matches.drain(..) { - matches_query_routes - .push((_match.clone(), compute_query_routes_(&rtables, &_match))); - } - drop(rtables); - - let mut wtables = zwrite!(tables.tables); - for (mut res, data_routes) in matches_data_routes { - get_mut_unchecked(&mut res) - .context_mut() - .update_data_routes(data_routes); - Resource::clean(&mut res); - } - for (mut res, query_routes) in matches_query_routes { - get_mut_unchecked(&mut res) - .context_mut() - .update_query_routes(query_routes); - Resource::clean(&mut res); - } - wtables.faces.remove(&face.id); - drop(wtables); - drop(ctrl_lock); - } - None => log::error!("Face already closed!"), - } -} - -pub struct TablesLock { - pub tables: RwLock, - pub ctrl_lock: Mutex<()>, - pub queries_lock: RwLock<()>, -} pub struct Router { whatami: WhatAmI, @@ -522,7 +85,7 @@ impl Router { ) { let mut tables = zwrite!(self.tables.tables); if router_full_linkstate | gossip { - tables.routers_net = Some(Network::new( + tables.hat.routers_net = Some(Network::new( "[Routers network]".to_string(), tables.zid, runtime.clone(), @@ -534,7 +97,7 @@ impl Router { )); } if peer_full_linkstate | gossip { - tables.peers_net = Some(Network::new( + tables.hat.peers_net = Some(Network::new( "[Peers network]".to_string(), tables.zid, runtime, @@ -546,9 +109,9 @@ impl Router { )); } if router_full_linkstate && peer_full_linkstate { - tables.shared_nodes = shared_nodes( - tables.routers_net.as_ref().unwrap(), - tables.peers_net.as_ref().unwrap(), + tables.hat.shared_nodes = shared_nodes( + tables.hat.routers_net.as_ref().unwrap(), + tables.hat.peers_net.as_ref().unwrap(), ); } } @@ -581,6 +144,7 @@ impl Router { let link_id = match (self.whatami, whatami) { (WhatAmI::Router, WhatAmI::Router) => tables + .hat .routers_net .as_mut() .unwrap() @@ -588,7 +152,7 @@ impl Router { (WhatAmI::Router, WhatAmI::Peer) | (WhatAmI::Peer, WhatAmI::Router) | (WhatAmI::Peer, WhatAmI::Peer) => { - if let Some(net) = tables.peers_net.as_mut() { + if let Some(net) = tables.hat.peers_net.as_mut() { net.add_link(transport.clone()) } else { 0 @@ -597,10 +161,10 @@ impl Router { _ => 0, }; - if tables.full_net(WhatAmI::Router) && tables.full_net(WhatAmI::Peer) { - tables.shared_nodes = shared_nodes( - tables.routers_net.as_ref().unwrap(), - tables.peers_net.as_ref().unwrap(), + if tables.hat.full_net(WhatAmI::Router) && tables.hat.full_net(WhatAmI::Peer) { + tables.hat.shared_nodes = shared_nodes( + tables.hat.routers_net.as_ref().unwrap(), + tables.hat.peers_net.as_ref().unwrap(), ); } @@ -625,13 +189,17 @@ impl Router { match (self.whatami, whatami) { (WhatAmI::Router, WhatAmI::Router) => { - tables.schedule_compute_trees(self.tables.clone(), WhatAmI::Router); + tables + .hat + .schedule_compute_trees(self.tables.clone(), WhatAmI::Router); } (WhatAmI::Router, WhatAmI::Peer) | (WhatAmI::Peer, WhatAmI::Router) | (WhatAmI::Peer, WhatAmI::Peer) => { - if tables.full_net(WhatAmI::Peer) { - tables.schedule_compute_trees(self.tables.clone(), WhatAmI::Peer); + if tables.hat.full_net(WhatAmI::Peer) { + tables + .hat + .schedule_compute_trees(self.tables.clone(), WhatAmI::Peer); } } _ => (), @@ -730,6 +298,7 @@ impl TransportPeerEventHandler for LinkStateInterceptor { match (tables.whatami, whatami) { (WhatAmI::Router, WhatAmI::Router) => { for (_, removed_node) in tables + .hat .routers_net .as_mut() .unwrap() @@ -748,14 +317,14 @@ impl TransportPeerEventHandler for LinkStateInterceptor { ); } - if tables.full_net(WhatAmI::Peer) { - tables.shared_nodes = shared_nodes( - tables.routers_net.as_ref().unwrap(), - tables.peers_net.as_ref().unwrap(), + if tables.hat.full_net(WhatAmI::Peer) { + tables.hat.shared_nodes = shared_nodes( + tables.hat.routers_net.as_ref().unwrap(), + tables.hat.peers_net.as_ref().unwrap(), ); } - tables.schedule_compute_trees( + tables.hat.schedule_compute_trees( self.tables.clone(), WhatAmI::Router, ); @@ -763,9 +332,9 @@ impl TransportPeerEventHandler for LinkStateInterceptor { (WhatAmI::Router, WhatAmI::Peer) | (WhatAmI::Peer, WhatAmI::Router) | (WhatAmI::Peer, WhatAmI::Peer) => { - if let Some(net) = tables.peers_net.as_mut() { + if let Some(net) = tables.hat.peers_net.as_mut() { let changes = net.link_states(list.link_states, zid); - if tables.full_net(WhatAmI::Peer) { + if tables.hat.full_net(WhatAmI::Peer) { for (_, removed_node) in changes.removed_nodes { pubsub_remove_node( &mut tables, @@ -780,13 +349,13 @@ impl TransportPeerEventHandler for LinkStateInterceptor { } if tables.whatami == WhatAmI::Router { - tables.shared_nodes = shared_nodes( - tables.routers_net.as_ref().unwrap(), - tables.peers_net.as_ref().unwrap(), + tables.hat.shared_nodes = shared_nodes( + tables.hat.routers_net.as_ref().unwrap(), + tables.hat.peers_net.as_ref().unwrap(), ); } - tables.schedule_compute_trees( + tables.hat.schedule_compute_trees( self.tables.clone(), WhatAmI::Peer, ); @@ -834,41 +403,45 @@ impl TransportPeerEventHandler for LinkStateInterceptor { match (tables.whatami, whatami) { (WhatAmI::Router, WhatAmI::Router) => { for (_, removed_node) in - tables.routers_net.as_mut().unwrap().remove_link(&zid) + tables.hat.routers_net.as_mut().unwrap().remove_link(&zid) { pubsub_remove_node(&mut tables, &removed_node.zid, WhatAmI::Router); queries_remove_node(&mut tables, &removed_node.zid, WhatAmI::Router); } - if tables.full_net(WhatAmI::Peer) { - tables.shared_nodes = shared_nodes( - tables.routers_net.as_ref().unwrap(), - tables.peers_net.as_ref().unwrap(), + if tables.hat.full_net(WhatAmI::Peer) { + tables.hat.shared_nodes = shared_nodes( + tables.hat.routers_net.as_ref().unwrap(), + tables.hat.peers_net.as_ref().unwrap(), ); } - tables.schedule_compute_trees(tables_ref.clone(), WhatAmI::Router); + tables + .hat + .schedule_compute_trees(tables_ref.clone(), WhatAmI::Router); } (WhatAmI::Router, WhatAmI::Peer) | (WhatAmI::Peer, WhatAmI::Router) | (WhatAmI::Peer, WhatAmI::Peer) => { - if tables.full_net(WhatAmI::Peer) { + if tables.hat.full_net(WhatAmI::Peer) { for (_, removed_node) in - tables.peers_net.as_mut().unwrap().remove_link(&zid) + tables.hat.peers_net.as_mut().unwrap().remove_link(&zid) { pubsub_remove_node(&mut tables, &removed_node.zid, WhatAmI::Peer); queries_remove_node(&mut tables, &removed_node.zid, WhatAmI::Peer); } if tables.whatami == WhatAmI::Router { - tables.shared_nodes = shared_nodes( - tables.routers_net.as_ref().unwrap(), - tables.peers_net.as_ref().unwrap(), + tables.hat.shared_nodes = shared_nodes( + tables.hat.routers_net.as_ref().unwrap(), + tables.hat.peers_net.as_ref().unwrap(), ); } - tables.schedule_compute_trees(tables_ref.clone(), WhatAmI::Peer); - } else if let Some(net) = tables.peers_net.as_mut() { + tables + .hat + .schedule_compute_trees(tables_ref.clone(), WhatAmI::Peer); + } else if let Some(net) = tables.hat.peers_net.as_mut() { net.remove_link(&zid); } } diff --git a/zenoh/src/net/runtime/adminspace.rs b/zenoh/src/net/runtime/adminspace.rs index 0eb099a098..f2396eec86 100644 --- a/zenoh/src/net/runtime/adminspace.rs +++ b/zenoh/src/net/runtime/adminspace.rs @@ -10,7 +10,7 @@ // // Contributors: // ZettaScale Zenoh Team, -use super::routing::face::Face; +use super::routing::dispatcher::face::Face; use super::Runtime; use crate::key_expr::KeyExpr; use crate::plugins::sealed as plugins; @@ -535,6 +535,7 @@ fn routers_linkstate_data(context: &AdminContext, query: Query) { reply_key, Value::from( tables + .hat .routers_net .as_ref() .map(|net| net.dot()) @@ -562,6 +563,7 @@ fn peers_linkstate_data(context: &AdminContext, query: Query) { reply_key, Value::from( tables + .hat .peers_net .as_ref() .map(|net| net.dot()) @@ -579,7 +581,7 @@ fn peers_linkstate_data(context: &AdminContext, query: Query) { fn subscribers_data(context: &AdminContext, query: Query) { let tables = zread!(context.runtime.router.tables.tables); - for sub in tables.router_subs.iter() { + for sub in tables.hat.router_subs.iter() { let key = KeyExpr::try_from(format!( "@/router/{}/subscriber/{}", context.zid_str, @@ -596,7 +598,7 @@ fn subscribers_data(context: &AdminContext, query: Query) { fn queryables_data(context: &AdminContext, query: Query) { let tables = zread!(context.runtime.router.tables.tables); - for qabl in tables.router_qabls.iter() { + for qabl in tables.hat.router_qabls.iter() { let key = KeyExpr::try_from(format!( "@/router/{}/queryable/{}", context.zid_str, diff --git a/zenoh/src/net/runtime/mod.rs b/zenoh/src/net/runtime/mod.rs index 92d369e998..7ca9297aa7 100644 --- a/zenoh/src/net/runtime/mod.rs +++ b/zenoh/src/net/runtime/mod.rs @@ -21,8 +21,8 @@ mod adminspace; pub mod orchestrator; use super::routing; -use super::routing::face::Face; -use super::routing::pubsub::full_reentrant_route_data; +use super::routing::dispatcher::face::Face; +use super::routing::dispatcher::pubsub::full_reentrant_route_data; use super::routing::router::{LinkStateInterceptor, Router}; use crate::config::{unwrap_or_default, Config, ModeDependent, Notifier}; use crate::GIT_VERSION; diff --git a/zenoh/src/net/tests/tables.rs b/zenoh/src/net/tests/tables.rs index 5dadf8d8a9..94f7856098 100644 --- a/zenoh/src/net/tests/tables.rs +++ b/zenoh/src/net/tests/tables.rs @@ -1,752 +1,752 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::net::routing::router::{self, *}; -use std::convert::{TryFrom, TryInto}; -use std::sync::{Arc, Mutex, RwLock}; -use std::time::Duration; -use uhlc::HLC; -use zenoh_buffers::ZBuf; -use zenoh_config::defaults::queries_default_timeout; -use zenoh_core::zlock; -use zenoh_protocol::core::Encoding; -use zenoh_protocol::core::{ - key_expr::keyexpr, ExprId, Reliability, WhatAmI, WireExpr, ZenohId, EMPTY_EXPR_ID, -}; -use zenoh_protocol::network::declare::subscriber::ext::SubscriberInfo; -use zenoh_protocol::network::declare::Mode; -use zenoh_protocol::network::{ext, Declare, DeclareBody, DeclareKeyExpr}; -use zenoh_protocol::zenoh::{PushBody, Put}; -use zenoh_transport::{DummyPrimitives, Primitives}; - -#[test] -fn base_test() { - let tables = TablesLock { - tables: RwLock::new(Tables::new( - ZenohId::try_from([1]).unwrap(), - WhatAmI::Client, - Some(Arc::new(HLC::default())), - false, - true, - Duration::from_millis(queries_default_timeout), - )), - ctrl_lock: Mutex::new(()), - queries_lock: RwLock::new(()), - }; - - let primitives = Arc::new(DummyPrimitives::new()); - let face = zwrite!(tables.tables).open_face( - ZenohId::try_from([1]).unwrap(), - WhatAmI::Client, - primitives, - ); - register_expr( - &tables, - &mut face.upgrade().unwrap(), - 1, - &"one/two/three".into(), - ); - register_expr( - &tables, - &mut face.upgrade().unwrap(), - 2, - &"one/deux/trois".into(), - ); - - let sub_info = SubscriberInfo { - reliability: Reliability::Reliable, - mode: Mode::Push, - }; - declare_client_subscription( - &tables, - zread!(tables.tables), - &mut face.upgrade().unwrap(), - &WireExpr::from(1).with_suffix("four/five"), - &sub_info, - ); - - Tables::print(&zread!(tables.tables)); -} - -#[test] -fn match_test() { - let key_exprs = [ - "**", - "a", - "a/b", - "*", - "a/*", - "a/b$*", - "abc", - "xx", - "ab$*", - "abcd", - "ab$*d", - "ab", - "ab/*", - "a/*/c/*/e", - "a/b/c/d/e", - "a/$*b/c/$*d/e", - "a/xb/c/xd/e", - "a/c/e", - "a/b/c/d/x/e", - "ab$*cd", - "abxxcxxd", - "abxxcxxcd", - "abxxcxxcdx", - "a/b/c", - "ab/**", - "**/xyz", - "a/b/xyz/d/e/f/xyz", - "**/xyz$*xyz", - "a/b/xyz/d/e/f/xyz", - "a/**/c/**/e", - "a/b/b/b/c/d/d/d/e", - "a/**/c/*/e/*", - "a/b/b/b/c/d/d/c/d/e/f", - "a/**/c/*/e/*", - "x/abc", - "x/*", - "x/abc$*", - "x/$*abc", - "x/a$*", - "x/a$*de", - "x/abc$*de", - "x/a$*d$*e", - "x/a$*e", - "x/a$*c$*e", - "x/ade", - "x/c$*", - "x/$*d", - "x/$*e", - ] - .map(|s| keyexpr::new(s).unwrap()); - - let tables = TablesLock { - tables: RwLock::new(Tables::new( - ZenohId::try_from([1]).unwrap(), - WhatAmI::Client, - Some(Arc::new(HLC::default())), - false, - true, - Duration::from_millis(queries_default_timeout), - )), - ctrl_lock: Mutex::new(()), - queries_lock: RwLock::new(()), - }; - let primitives = Arc::new(DummyPrimitives::new()); - let face = zwrite!(tables.tables).open_face( - ZenohId::try_from([1]).unwrap(), - WhatAmI::Client, - primitives, - ); - for (i, key_expr) in key_exprs.iter().enumerate() { - register_expr( - &tables, - &mut face.upgrade().unwrap(), - i.try_into().unwrap(), - &(*key_expr).into(), - ); - } - - for key_expr1 in key_exprs.iter() { - let res_matches = Resource::get_matches(&zread!(tables.tables), key_expr1); - dbg!(res_matches.len()); - for key_expr2 in key_exprs.iter() { - if res_matches - .iter() - .map(|m| m.upgrade().unwrap().expr()) - .any(|x| x.as_str() == key_expr2.as_str()) - { - assert!(dbg!(dbg!(key_expr1).intersects(dbg!(key_expr2)))); - } else { - assert!(!dbg!(dbg!(key_expr1).intersects(dbg!(key_expr2)))); - } - } - } -} - -#[test] -fn clean_test() { - let tables = TablesLock { - tables: RwLock::new(Tables::new( - ZenohId::try_from([1]).unwrap(), - WhatAmI::Client, - Some(Arc::new(HLC::default())), - false, - true, - Duration::from_millis(queries_default_timeout), - )), - ctrl_lock: Mutex::new(()), - queries_lock: RwLock::new(()), - }; - - let primitives = Arc::new(DummyPrimitives::new()); - let face0 = zwrite!(tables.tables).open_face( - ZenohId::try_from([1]).unwrap(), - WhatAmI::Client, - primitives, - ); - assert!(face0.upgrade().is_some()); - - // -------------- - register_expr(&tables, &mut face0.upgrade().unwrap(), 1, &"todrop1".into()); - let optres1 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop1") - .map(|res| Arc::downgrade(&res)); - assert!(optres1.is_some()); - let res1 = optres1.unwrap(); - assert!(res1.upgrade().is_some()); - - register_expr( - &tables, - &mut face0.upgrade().unwrap(), - 2, - &"todrop1/todrop11".into(), - ); - let optres2 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop1/todrop11") - .map(|res| Arc::downgrade(&res)); - assert!(optres2.is_some()); - let res2 = optres2.unwrap(); - assert!(res2.upgrade().is_some()); - - register_expr(&tables, &mut face0.upgrade().unwrap(), 3, &"**".into()); - let optres3 = Resource::get_resource(zread!(tables.tables)._get_root(), "**") - .map(|res| Arc::downgrade(&res)); - assert!(optres3.is_some()); - let res3 = optres3.unwrap(); - assert!(res3.upgrade().is_some()); - - unregister_expr(&tables, &mut face0.upgrade().unwrap(), 1); - assert!(res1.upgrade().is_some()); - assert!(res2.upgrade().is_some()); - assert!(res3.upgrade().is_some()); - - unregister_expr(&tables, &mut face0.upgrade().unwrap(), 2); - assert!(res1.upgrade().is_none()); - assert!(res2.upgrade().is_none()); - assert!(res3.upgrade().is_some()); - - unregister_expr(&tables, &mut face0.upgrade().unwrap(), 3); - assert!(res1.upgrade().is_none()); - assert!(res2.upgrade().is_none()); - assert!(res3.upgrade().is_none()); - - // -------------- - register_expr(&tables, &mut face0.upgrade().unwrap(), 1, &"todrop1".into()); - let optres1 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop1") - .map(|res| Arc::downgrade(&res)); - assert!(optres1.is_some()); - let res1 = optres1.unwrap(); - assert!(res1.upgrade().is_some()); - - let sub_info = SubscriberInfo { - reliability: Reliability::Reliable, - mode: Mode::Push, - }; - - declare_client_subscription( - &tables, - zread!(tables.tables), - &mut face0.upgrade().unwrap(), - &"todrop1/todrop11".into(), - &sub_info, - ); - let optres2 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop1/todrop11") - .map(|res| Arc::downgrade(&res)); - assert!(optres2.is_some()); - let res2 = optres2.unwrap(); - assert!(res2.upgrade().is_some()); - - declare_client_subscription( - &tables, - zread!(tables.tables), - &mut face0.upgrade().unwrap(), - &WireExpr::from(1).with_suffix("/todrop12"), - &sub_info, - ); - let optres3 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop1/todrop12") - .map(|res| Arc::downgrade(&res)); - assert!(optres3.is_some()); - let res3 = optres3.unwrap(); - assert!(res3.upgrade().is_some()); - - forget_client_subscription( - &tables, - zread!(tables.tables), - &mut face0.upgrade().unwrap(), - &WireExpr::from(1).with_suffix("/todrop12"), - ); - assert!(res1.upgrade().is_some()); - assert!(res2.upgrade().is_some()); - assert!(res3.upgrade().is_none()); - - forget_client_subscription( - &tables, - zread!(tables.tables), - &mut face0.upgrade().unwrap(), - &"todrop1/todrop11".into(), - ); - assert!(res1.upgrade().is_some()); - assert!(res2.upgrade().is_none()); - assert!(res3.upgrade().is_none()); - - unregister_expr(&tables, &mut face0.upgrade().unwrap(), 1); - assert!(res1.upgrade().is_none()); - assert!(res2.upgrade().is_none()); - assert!(res3.upgrade().is_none()); - - // -------------- - register_expr(&tables, &mut face0.upgrade().unwrap(), 2, &"todrop3".into()); - declare_client_subscription( - &tables, - zread!(tables.tables), - &mut face0.upgrade().unwrap(), - &"todrop3".into(), - &sub_info, - ); - let optres1 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop3") - .map(|res| Arc::downgrade(&res)); - assert!(optres1.is_some()); - let res1 = optres1.unwrap(); - assert!(res1.upgrade().is_some()); - - forget_client_subscription( - &tables, - zread!(tables.tables), - &mut face0.upgrade().unwrap(), - &"todrop3".into(), - ); - assert!(res1.upgrade().is_some()); - - unregister_expr(&tables, &mut face0.upgrade().unwrap(), 2); - assert!(res1.upgrade().is_none()); - - // -------------- - register_expr(&tables, &mut face0.upgrade().unwrap(), 3, &"todrop4".into()); - register_expr(&tables, &mut face0.upgrade().unwrap(), 4, &"todrop5".into()); - declare_client_subscription( - &tables, - zread!(tables.tables), - &mut face0.upgrade().unwrap(), - &"todrop5".into(), - &sub_info, - ); - declare_client_subscription( - &tables, - zread!(tables.tables), - &mut face0.upgrade().unwrap(), - &"todrop6".into(), - &sub_info, - ); - - let optres1 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop4") - .map(|res| Arc::downgrade(&res)); - assert!(optres1.is_some()); - let res1 = optres1.unwrap(); - let optres2 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop5") - .map(|res| Arc::downgrade(&res)); - assert!(optres2.is_some()); - let res2 = optres2.unwrap(); - let optres3 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop6") - .map(|res| Arc::downgrade(&res)); - assert!(optres3.is_some()); - let res3 = optres3.unwrap(); - - assert!(res1.upgrade().is_some()); - assert!(res2.upgrade().is_some()); - assert!(res3.upgrade().is_some()); - - router::close_face(&tables, &face0); - assert!(face0.upgrade().is_none()); - assert!(res1.upgrade().is_none()); - assert!(res2.upgrade().is_none()); - assert!(res3.upgrade().is_none()); -} - -pub struct ClientPrimitives { - data: std::sync::Mutex>>, - mapping: std::sync::Mutex>, -} - -impl ClientPrimitives { - pub fn new() -> ClientPrimitives { - ClientPrimitives { - data: std::sync::Mutex::new(None), - mapping: std::sync::Mutex::new(std::collections::HashMap::new()), - } - } - - pub fn clear_data(&self) { - *self.data.lock().unwrap() = None; - } -} - -impl Default for ClientPrimitives { - fn default() -> Self { - Self::new() - } -} - -impl ClientPrimitives { - fn get_name(&self, key_expr: &WireExpr) -> String { - let mapping = self.mapping.lock().unwrap(); - let (scope, suffix) = key_expr.as_id_and_suffix(); - if scope == EMPTY_EXPR_ID { - suffix.to_string() - } else if suffix.is_empty() { - mapping.get(&scope).unwrap().clone() - } else { - format!("{}{}", mapping.get(&scope).unwrap(), suffix) - } - } - - fn get_last_name(&self) -> Option { - self.data - .lock() - .unwrap() - .as_ref() - .map(|data| self.get_name(data)) - } - - #[allow(dead_code)] - fn get_last_key(&self) -> Option { - self.data.lock().unwrap().as_ref().cloned() - } -} - -impl Primitives for ClientPrimitives { - fn send_declare(&self, msg: zenoh_protocol::network::Declare) { - match msg.body { - DeclareBody::DeclareKeyExpr(d) => { - let name = self.get_name(&d.wire_expr); - zlock!(self.mapping).insert(d.id, name); - } - DeclareBody::UndeclareKeyExpr(u) => { - zlock!(self.mapping).remove(&u.id); - } - _ => (), - } - } - - fn send_push(&self, msg: zenoh_protocol::network::Push) { - *zlock!(self.data) = Some(msg.wire_expr.to_owned()); - } - - fn send_request(&self, _msg: zenoh_protocol::network::Request) {} - - fn send_response(&self, _msg: zenoh_protocol::network::Response) {} - - fn send_response_final(&self, _msg: zenoh_protocol::network::ResponseFinal) {} - - fn send_close(&self) {} -} - -#[test] -fn client_test() { - let tables = TablesLock { - tables: RwLock::new(Tables::new( - ZenohId::try_from([1]).unwrap(), - WhatAmI::Client, - Some(Arc::new(HLC::default())), - false, - true, - Duration::from_millis(queries_default_timeout), - )), - ctrl_lock: Mutex::new(()), - queries_lock: RwLock::new(()), - }; - - let sub_info = SubscriberInfo { - reliability: Reliability::Reliable, - mode: Mode::Push, - }; - - let primitives0 = Arc::new(ClientPrimitives::new()); - - let face0 = zwrite!(tables.tables).open_face( - ZenohId::try_from([1]).unwrap(), - WhatAmI::Client, - primitives0.clone(), - ); - register_expr( - &tables, - &mut face0.upgrade().unwrap(), - 11, - &"test/client".into(), - ); - primitives0.send_declare(Declare { - ext_qos: ext::QoSType::declare_default(), - ext_tstamp: None, - ext_nodeid: ext::NodeIdType::default(), - body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { - id: 11, - wire_expr: "test/client".into(), - }), - }); - declare_client_subscription( - &tables, - zread!(tables.tables), - &mut face0.upgrade().unwrap(), - &WireExpr::from(11).with_suffix("/**"), - &sub_info, - ); - register_expr( - &tables, - &mut face0.upgrade().unwrap(), - 12, - &WireExpr::from(11).with_suffix("/z1_pub1"), - ); - primitives0.send_declare(Declare { - ext_qos: ext::QoSType::declare_default(), - ext_tstamp: None, - ext_nodeid: ext::NodeIdType::default(), - body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { - id: 12, - wire_expr: WireExpr::from(11).with_suffix("/z1_pub1"), - }), - }); - - let primitives1 = Arc::new(ClientPrimitives::new()); - let face1 = zwrite!(tables.tables).open_face( - ZenohId::try_from([1]).unwrap(), - WhatAmI::Client, - primitives1.clone(), - ); - register_expr( - &tables, - &mut face1.upgrade().unwrap(), - 21, - &"test/client".into(), - ); - primitives1.send_declare(Declare { - ext_qos: ext::QoSType::declare_default(), - ext_tstamp: None, - ext_nodeid: ext::NodeIdType::default(), - body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { - id: 21, - wire_expr: "test/client".into(), - }), - }); - declare_client_subscription( - &tables, - zread!(tables.tables), - &mut face1.upgrade().unwrap(), - &WireExpr::from(21).with_suffix("/**"), - &sub_info, - ); - register_expr( - &tables, - &mut face1.upgrade().unwrap(), - 22, - &WireExpr::from(21).with_suffix("/z2_pub1"), - ); - primitives1.send_declare(Declare { - ext_qos: ext::QoSType::declare_default(), - ext_tstamp: None, - ext_nodeid: ext::NodeIdType::default(), - body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { - id: 22, - wire_expr: WireExpr::from(21).with_suffix("/z2_pub1"), - }), - }); - - let primitives2 = Arc::new(ClientPrimitives::new()); - let face2 = zwrite!(tables.tables).open_face( - ZenohId::try_from([1]).unwrap(), - WhatAmI::Client, - primitives2.clone(), - ); - register_expr( - &tables, - &mut face2.upgrade().unwrap(), - 31, - &"test/client".into(), - ); - primitives2.send_declare(Declare { - ext_qos: ext::QoSType::declare_default(), - ext_tstamp: None, - ext_nodeid: ext::NodeIdType::default(), - body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { - id: 31, - wire_expr: "test/client".into(), - }), - }); - declare_client_subscription( - &tables, - zread!(tables.tables), - &mut face2.upgrade().unwrap(), - &WireExpr::from(31).with_suffix("/**"), - &sub_info, - ); - - primitives0.clear_data(); - primitives1.clear_data(); - primitives2.clear_data(); - - full_reentrant_route_data( - &tables.tables, - &face0.upgrade().unwrap(), - &"test/client/z1_wr1".into(), - ext::QoSType::default(), - PushBody::Put(Put { - timestamp: None, - encoding: Encoding::default(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload: ZBuf::empty(), - }), - 0, - ); - - // functionnal check - assert!(primitives1.get_last_name().is_some()); - assert_eq!(primitives1.get_last_name().unwrap(), "test/client/z1_wr1"); - // mapping strategy check - // assert_eq!(primitives1.get_last_key().unwrap(), KeyExpr::IdWithSuffix(21, "/z1_wr1".to_string())); - - // functionnal check - assert!(primitives2.get_last_name().is_some()); - assert_eq!(primitives2.get_last_name().unwrap(), "test/client/z1_wr1"); - // mapping strategy check - // assert_eq!(primitives2.get_last_key().unwrap(), KeyExpr::IdWithSuffix(31, "/z1_wr1".to_string())); - - primitives0.clear_data(); - primitives1.clear_data(); - primitives2.clear_data(); - full_reentrant_route_data( - &tables.tables, - &face0.upgrade().unwrap(), - &WireExpr::from(11).with_suffix("/z1_wr2"), - ext::QoSType::default(), - PushBody::Put(Put { - timestamp: None, - encoding: Encoding::default(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload: ZBuf::empty(), - }), - 0, - ); - - // functionnal check - assert!(primitives1.get_last_name().is_some()); - assert_eq!(primitives1.get_last_name().unwrap(), "test/client/z1_wr2"); - // mapping strategy check - // assert_eq!(primitives1.get_last_key().unwrap(), KeyExpr::IdWithSuffix(21, "/z1_wr2".to_string())); - - // functionnal check - assert!(primitives2.get_last_name().is_some()); - assert_eq!(primitives2.get_last_name().unwrap(), "test/client/z1_wr2"); - // mapping strategy check - // assert_eq!(primitives2.get_last_key().unwrap(), KeyExpr::IdWithSuffix(31, "/z1_wr2".to_string())); - - primitives0.clear_data(); - primitives1.clear_data(); - primitives2.clear_data(); - full_reentrant_route_data( - &tables.tables, - &face1.upgrade().unwrap(), - &"test/client/**".into(), - ext::QoSType::default(), - PushBody::Put(Put { - timestamp: None, - encoding: Encoding::default(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload: ZBuf::empty(), - }), - 0, - ); - - // functionnal check - assert!(primitives0.get_last_name().is_some()); - assert_eq!(primitives0.get_last_name().unwrap(), "test/client/**"); - // mapping strategy check - // assert_eq!(primitives1.get_last_key().unwrap(), KeyExpr::IdWithSuffix(11, "/**".to_string())); - - // functionnal check - assert!(primitives2.get_last_name().is_some()); - assert_eq!(primitives2.get_last_name().unwrap(), "test/client/**"); - // mapping strategy check - // assert_eq!(primitives2.get_last_key().unwrap(), KeyExpr::IdWithSuffix(31, "/**".to_string())); - - primitives0.clear_data(); - primitives1.clear_data(); - primitives2.clear_data(); - full_reentrant_route_data( - &tables.tables, - &face0.upgrade().unwrap(), - &12.into(), - ext::QoSType::default(), - PushBody::Put(Put { - timestamp: None, - encoding: Encoding::default(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload: ZBuf::empty(), - }), - 0, - ); - - // functionnal check - assert!(primitives1.get_last_name().is_some()); - assert_eq!(primitives1.get_last_name().unwrap(), "test/client/z1_pub1"); - // mapping strategy check - // assert_eq!(primitives1.get_last_key().unwrap(), KeyExpr::IdWithSuffix(21, "/z1_pub1".to_string())); - - // functionnal check - assert!(primitives2.get_last_name().is_some()); - assert_eq!(primitives2.get_last_name().unwrap(), "test/client/z1_pub1"); - // mapping strategy check - // assert_eq!(primitives2.get_last_key().unwrap(), KeyExpr::IdWithSuffix(31, "/z1_pub1".to_string())); - - primitives0.clear_data(); - primitives1.clear_data(); - primitives2.clear_data(); - full_reentrant_route_data( - &tables.tables, - &face1.upgrade().unwrap(), - &22.into(), - ext::QoSType::default(), - PushBody::Put(Put { - timestamp: None, - encoding: Encoding::default(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload: ZBuf::empty(), - }), - 0, - ); - - // functionnal check - assert!(primitives0.get_last_name().is_some()); - assert_eq!(primitives0.get_last_name().unwrap(), "test/client/z2_pub1"); - // mapping strategy check - // assert_eq!(primitives1.get_last_key().unwrap(), KeyExpr::IdWithSuffix(11, "/z2_pub1".to_string())); - - // functionnal check - assert!(primitives2.get_last_name().is_some()); - assert_eq!(primitives2.get_last_name().unwrap(), "test/client/z2_pub1"); - // mapping strategy check - // assert_eq!(primitives2.get_last_key().unwrap(), KeyExpr::IdWithSuffix(31, "/z2_pub1".to_string())); -} +// // +// // Copyright (c) 2023 ZettaScale Technology +// // +// // This program and the accompanying materials are made available under the +// // terms of the Eclipse Public License 2.0 which is available at +// // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// // which is available at https://www.apache.org/licenses/LICENSE-2.0. +// // +// // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// // +// // Contributors: +// // ZettaScale Zenoh Team, +// // +// use crate::net::routing::router::{self, *}; +// use std::convert::{TryFrom, TryInto}; +// use std::sync::{Arc, Mutex, RwLock}; +// use std::time::Duration; +// use uhlc::HLC; +// use zenoh_buffers::ZBuf; +// use zenoh_config::defaults::queries_default_timeout; +// use zenoh_core::zlock; +// use zenoh_protocol::core::Encoding; +// use zenoh_protocol::core::{ +// key_expr::keyexpr, ExprId, Reliability, WhatAmI, WireExpr, ZenohId, EMPTY_EXPR_ID, +// }; +// use zenoh_protocol::network::declare::subscriber::ext::SubscriberInfo; +// use zenoh_protocol::network::declare::Mode; +// use zenoh_protocol::network::{ext, Declare, DeclareBody, DeclareKeyExpr}; +// use zenoh_protocol::zenoh::{PushBody, Put}; +// use zenoh_transport::{DummyPrimitives, Primitives}; + +// #[test] +// fn base_test() { +// let tables = TablesLock { +// tables: RwLock::new(Tables::new( +// ZenohId::try_from([1]).unwrap(), +// WhatAmI::Client, +// Some(Arc::new(HLC::default())), +// false, +// true, +// Duration::from_millis(queries_default_timeout), +// )), +// ctrl_lock: Mutex::new(()), +// queries_lock: RwLock::new(()), +// }; + +// let primitives = Arc::new(DummyPrimitives::new()); +// let face = zwrite!(tables.tables).open_face( +// ZenohId::try_from([1]).unwrap(), +// WhatAmI::Client, +// primitives, +// ); +// register_expr( +// &tables, +// &mut face.upgrade().unwrap(), +// 1, +// &"one/two/three".into(), +// ); +// register_expr( +// &tables, +// &mut face.upgrade().unwrap(), +// 2, +// &"one/deux/trois".into(), +// ); + +// let sub_info = SubscriberInfo { +// reliability: Reliability::Reliable, +// mode: Mode::Push, +// }; +// declare_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face.upgrade().unwrap(), +// &WireExpr::from(1).with_suffix("four/five"), +// &sub_info, +// ); + +// Tables::print(&zread!(tables.tables)); +// } + +// #[test] +// fn match_test() { +// let key_exprs = [ +// "**", +// "a", +// "a/b", +// "*", +// "a/*", +// "a/b$*", +// "abc", +// "xx", +// "ab$*", +// "abcd", +// "ab$*d", +// "ab", +// "ab/*", +// "a/*/c/*/e", +// "a/b/c/d/e", +// "a/$*b/c/$*d/e", +// "a/xb/c/xd/e", +// "a/c/e", +// "a/b/c/d/x/e", +// "ab$*cd", +// "abxxcxxd", +// "abxxcxxcd", +// "abxxcxxcdx", +// "a/b/c", +// "ab/**", +// "**/xyz", +// "a/b/xyz/d/e/f/xyz", +// "**/xyz$*xyz", +// "a/b/xyz/d/e/f/xyz", +// "a/**/c/**/e", +// "a/b/b/b/c/d/d/d/e", +// "a/**/c/*/e/*", +// "a/b/b/b/c/d/d/c/d/e/f", +// "a/**/c/*/e/*", +// "x/abc", +// "x/*", +// "x/abc$*", +// "x/$*abc", +// "x/a$*", +// "x/a$*de", +// "x/abc$*de", +// "x/a$*d$*e", +// "x/a$*e", +// "x/a$*c$*e", +// "x/ade", +// "x/c$*", +// "x/$*d", +// "x/$*e", +// ] +// .map(|s| keyexpr::new(s).unwrap()); + +// let tables = TablesLock { +// tables: RwLock::new(Tables::new( +// ZenohId::try_from([1]).unwrap(), +// WhatAmI::Client, +// Some(Arc::new(HLC::default())), +// false, +// true, +// Duration::from_millis(queries_default_timeout), +// )), +// ctrl_lock: Mutex::new(()), +// queries_lock: RwLock::new(()), +// }; +// let primitives = Arc::new(DummyPrimitives::new()); +// let face = zwrite!(tables.tables).open_face( +// ZenohId::try_from([1]).unwrap(), +// WhatAmI::Client, +// primitives, +// ); +// for (i, key_expr) in key_exprs.iter().enumerate() { +// register_expr( +// &tables, +// &mut face.upgrade().unwrap(), +// i.try_into().unwrap(), +// &(*key_expr).into(), +// ); +// } + +// for key_expr1 in key_exprs.iter() { +// let res_matches = Resource::get_matches(&zread!(tables.tables), key_expr1); +// dbg!(res_matches.len()); +// for key_expr2 in key_exprs.iter() { +// if res_matches +// .iter() +// .map(|m| m.upgrade().unwrap().expr()) +// .any(|x| x.as_str() == key_expr2.as_str()) +// { +// assert!(dbg!(dbg!(key_expr1).intersects(dbg!(key_expr2)))); +// } else { +// assert!(!dbg!(dbg!(key_expr1).intersects(dbg!(key_expr2)))); +// } +// } +// } +// } + +// #[test] +// fn clean_test() { +// let tables = TablesLock { +// tables: RwLock::new(Tables::new( +// ZenohId::try_from([1]).unwrap(), +// WhatAmI::Client, +// Some(Arc::new(HLC::default())), +// false, +// true, +// Duration::from_millis(queries_default_timeout), +// )), +// ctrl_lock: Mutex::new(()), +// queries_lock: RwLock::new(()), +// }; + +// let primitives = Arc::new(DummyPrimitives::new()); +// let face0 = zwrite!(tables.tables).open_face( +// ZenohId::try_from([1]).unwrap(), +// WhatAmI::Client, +// primitives, +// ); +// assert!(face0.upgrade().is_some()); + +// // -------------- +// register_expr(&tables, &mut face0.upgrade().unwrap(), 1, &"todrop1".into()); +// let optres1 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop1") +// .map(|res| Arc::downgrade(&res)); +// assert!(optres1.is_some()); +// let res1 = optres1.unwrap(); +// assert!(res1.upgrade().is_some()); + +// register_expr( +// &tables, +// &mut face0.upgrade().unwrap(), +// 2, +// &"todrop1/todrop11".into(), +// ); +// let optres2 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop1/todrop11") +// .map(|res| Arc::downgrade(&res)); +// assert!(optres2.is_some()); +// let res2 = optres2.unwrap(); +// assert!(res2.upgrade().is_some()); + +// register_expr(&tables, &mut face0.upgrade().unwrap(), 3, &"**".into()); +// let optres3 = Resource::get_resource(zread!(tables.tables)._get_root(), "**") +// .map(|res| Arc::downgrade(&res)); +// assert!(optres3.is_some()); +// let res3 = optres3.unwrap(); +// assert!(res3.upgrade().is_some()); + +// unregister_expr(&tables, &mut face0.upgrade().unwrap(), 1); +// assert!(res1.upgrade().is_some()); +// assert!(res2.upgrade().is_some()); +// assert!(res3.upgrade().is_some()); + +// unregister_expr(&tables, &mut face0.upgrade().unwrap(), 2); +// assert!(res1.upgrade().is_none()); +// assert!(res2.upgrade().is_none()); +// assert!(res3.upgrade().is_some()); + +// unregister_expr(&tables, &mut face0.upgrade().unwrap(), 3); +// assert!(res1.upgrade().is_none()); +// assert!(res2.upgrade().is_none()); +// assert!(res3.upgrade().is_none()); + +// // -------------- +// register_expr(&tables, &mut face0.upgrade().unwrap(), 1, &"todrop1".into()); +// let optres1 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop1") +// .map(|res| Arc::downgrade(&res)); +// assert!(optres1.is_some()); +// let res1 = optres1.unwrap(); +// assert!(res1.upgrade().is_some()); + +// let sub_info = SubscriberInfo { +// reliability: Reliability::Reliable, +// mode: Mode::Push, +// }; + +// declare_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face0.upgrade().unwrap(), +// &"todrop1/todrop11".into(), +// &sub_info, +// ); +// let optres2 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop1/todrop11") +// .map(|res| Arc::downgrade(&res)); +// assert!(optres2.is_some()); +// let res2 = optres2.unwrap(); +// assert!(res2.upgrade().is_some()); + +// declare_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face0.upgrade().unwrap(), +// &WireExpr::from(1).with_suffix("/todrop12"), +// &sub_info, +// ); +// let optres3 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop1/todrop12") +// .map(|res| Arc::downgrade(&res)); +// assert!(optres3.is_some()); +// let res3 = optres3.unwrap(); +// assert!(res3.upgrade().is_some()); + +// forget_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face0.upgrade().unwrap(), +// &WireExpr::from(1).with_suffix("/todrop12"), +// ); +// assert!(res1.upgrade().is_some()); +// assert!(res2.upgrade().is_some()); +// assert!(res3.upgrade().is_none()); + +// forget_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face0.upgrade().unwrap(), +// &"todrop1/todrop11".into(), +// ); +// assert!(res1.upgrade().is_some()); +// assert!(res2.upgrade().is_none()); +// assert!(res3.upgrade().is_none()); + +// unregister_expr(&tables, &mut face0.upgrade().unwrap(), 1); +// assert!(res1.upgrade().is_none()); +// assert!(res2.upgrade().is_none()); +// assert!(res3.upgrade().is_none()); + +// // -------------- +// register_expr(&tables, &mut face0.upgrade().unwrap(), 2, &"todrop3".into()); +// declare_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face0.upgrade().unwrap(), +// &"todrop3".into(), +// &sub_info, +// ); +// let optres1 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop3") +// .map(|res| Arc::downgrade(&res)); +// assert!(optres1.is_some()); +// let res1 = optres1.unwrap(); +// assert!(res1.upgrade().is_some()); + +// forget_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face0.upgrade().unwrap(), +// &"todrop3".into(), +// ); +// assert!(res1.upgrade().is_some()); + +// unregister_expr(&tables, &mut face0.upgrade().unwrap(), 2); +// assert!(res1.upgrade().is_none()); + +// // -------------- +// register_expr(&tables, &mut face0.upgrade().unwrap(), 3, &"todrop4".into()); +// register_expr(&tables, &mut face0.upgrade().unwrap(), 4, &"todrop5".into()); +// declare_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face0.upgrade().unwrap(), +// &"todrop5".into(), +// &sub_info, +// ); +// declare_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face0.upgrade().unwrap(), +// &"todrop6".into(), +// &sub_info, +// ); + +// let optres1 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop4") +// .map(|res| Arc::downgrade(&res)); +// assert!(optres1.is_some()); +// let res1 = optres1.unwrap(); +// let optres2 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop5") +// .map(|res| Arc::downgrade(&res)); +// assert!(optres2.is_some()); +// let res2 = optres2.unwrap(); +// let optres3 = Resource::get_resource(zread!(tables.tables)._get_root(), "todrop6") +// .map(|res| Arc::downgrade(&res)); +// assert!(optres3.is_some()); +// let res3 = optres3.unwrap(); + +// assert!(res1.upgrade().is_some()); +// assert!(res2.upgrade().is_some()); +// assert!(res3.upgrade().is_some()); + +// tables::close_face(&tables, &face0); +// assert!(face0.upgrade().is_none()); +// assert!(res1.upgrade().is_none()); +// assert!(res2.upgrade().is_none()); +// assert!(res3.upgrade().is_none()); +// } + +// pub struct ClientPrimitives { +// data: std::sync::Mutex>>, +// mapping: std::sync::Mutex>, +// } + +// impl ClientPrimitives { +// pub fn new() -> ClientPrimitives { +// ClientPrimitives { +// data: std::sync::Mutex::new(None), +// mapping: std::sync::Mutex::new(std::collections::HashMap::new()), +// } +// } + +// pub fn clear_data(&self) { +// *self.data.lock().unwrap() = None; +// } +// } + +// impl Default for ClientPrimitives { +// fn default() -> Self { +// Self::new() +// } +// } + +// impl ClientPrimitives { +// fn get_name(&self, key_expr: &WireExpr) -> String { +// let mapping = self.mapping.lock().unwrap(); +// let (scope, suffix) = key_expr.as_id_and_suffix(); +// if scope == EMPTY_EXPR_ID { +// suffix.to_string() +// } else if suffix.is_empty() { +// mapping.get(&scope).unwrap().clone() +// } else { +// format!("{}{}", mapping.get(&scope).unwrap(), suffix) +// } +// } + +// fn get_last_name(&self) -> Option { +// self.data +// .lock() +// .unwrap() +// .as_ref() +// .map(|data| self.get_name(data)) +// } + +// #[allow(dead_code)] +// fn get_last_key(&self) -> Option { +// self.data.lock().unwrap().as_ref().cloned() +// } +// } + +// impl Primitives for ClientPrimitives { +// fn send_declare(&self, msg: zenoh_protocol::network::Declare) { +// match msg.body { +// DeclareBody::DeclareKeyExpr(d) => { +// let name = self.get_name(&d.wire_expr); +// zlock!(self.mapping).insert(d.id, name); +// } +// DeclareBody::UndeclareKeyExpr(u) => { +// zlock!(self.mapping).remove(&u.id); +// } +// _ => (), +// } +// } + +// fn send_push(&self, msg: zenoh_protocol::network::Push) { +// *zlock!(self.data) = Some(msg.wire_expr.to_owned()); +// } + +// fn send_request(&self, _msg: zenoh_protocol::network::Request) {} + +// fn send_response(&self, _msg: zenoh_protocol::network::Response) {} + +// fn send_response_final(&self, _msg: zenoh_protocol::network::ResponseFinal) {} + +// fn send_close(&self) {} +// } + +// #[test] +// fn client_test() { +// let tables = TablesLock { +// tables: RwLock::new(Tables::new( +// ZenohId::try_from([1]).unwrap(), +// WhatAmI::Client, +// Some(Arc::new(HLC::default())), +// false, +// true, +// Duration::from_millis(queries_default_timeout), +// )), +// ctrl_lock: Mutex::new(()), +// queries_lock: RwLock::new(()), +// }; + +// let sub_info = SubscriberInfo { +// reliability: Reliability::Reliable, +// mode: Mode::Push, +// }; + +// let primitives0 = Arc::new(ClientPrimitives::new()); + +// let face0 = zwrite!(tables.tables).open_face( +// ZenohId::try_from([1]).unwrap(), +// WhatAmI::Client, +// primitives0.clone(), +// ); +// register_expr( +// &tables, +// &mut face0.upgrade().unwrap(), +// 11, +// &"test/client".into(), +// ); +// primitives0.send_declare(Declare { +// ext_qos: ext::QoSType::declare_default(), +// ext_tstamp: None, +// ext_nodeid: ext::NodeIdType::default(), +// body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { +// id: 11, +// wire_expr: "test/client".into(), +// }), +// }); +// declare_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face0.upgrade().unwrap(), +// &WireExpr::from(11).with_suffix("/**"), +// &sub_info, +// ); +// register_expr( +// &tables, +// &mut face0.upgrade().unwrap(), +// 12, +// &WireExpr::from(11).with_suffix("/z1_pub1"), +// ); +// primitives0.send_declare(Declare { +// ext_qos: ext::QoSType::declare_default(), +// ext_tstamp: None, +// ext_nodeid: ext::NodeIdType::default(), +// body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { +// id: 12, +// wire_expr: WireExpr::from(11).with_suffix("/z1_pub1"), +// }), +// }); + +// let primitives1 = Arc::new(ClientPrimitives::new()); +// let face1 = zwrite!(tables.tables).open_face( +// ZenohId::try_from([1]).unwrap(), +// WhatAmI::Client, +// primitives1.clone(), +// ); +// register_expr( +// &tables, +// &mut face1.upgrade().unwrap(), +// 21, +// &"test/client".into(), +// ); +// primitives1.send_declare(Declare { +// ext_qos: ext::QoSType::declare_default(), +// ext_tstamp: None, +// ext_nodeid: ext::NodeIdType::default(), +// body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { +// id: 21, +// wire_expr: "test/client".into(), +// }), +// }); +// declare_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face1.upgrade().unwrap(), +// &WireExpr::from(21).with_suffix("/**"), +// &sub_info, +// ); +// register_expr( +// &tables, +// &mut face1.upgrade().unwrap(), +// 22, +// &WireExpr::from(21).with_suffix("/z2_pub1"), +// ); +// primitives1.send_declare(Declare { +// ext_qos: ext::QoSType::declare_default(), +// ext_tstamp: None, +// ext_nodeid: ext::NodeIdType::default(), +// body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { +// id: 22, +// wire_expr: WireExpr::from(21).with_suffix("/z2_pub1"), +// }), +// }); + +// let primitives2 = Arc::new(ClientPrimitives::new()); +// let face2 = zwrite!(tables.tables).open_face( +// ZenohId::try_from([1]).unwrap(), +// WhatAmI::Client, +// primitives2.clone(), +// ); +// register_expr( +// &tables, +// &mut face2.upgrade().unwrap(), +// 31, +// &"test/client".into(), +// ); +// primitives2.send_declare(Declare { +// ext_qos: ext::QoSType::declare_default(), +// ext_tstamp: None, +// ext_nodeid: ext::NodeIdType::default(), +// body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { +// id: 31, +// wire_expr: "test/client".into(), +// }), +// }); +// declare_client_subscription( +// &tables, +// zread!(tables.tables), +// &mut face2.upgrade().unwrap(), +// &WireExpr::from(31).with_suffix("/**"), +// &sub_info, +// ); + +// primitives0.clear_data(); +// primitives1.clear_data(); +// primitives2.clear_data(); + +// full_reentrant_route_data( +// &tables.tables, +// &face0.upgrade().unwrap(), +// &"test/client/z1_wr1".into(), +// ext::QoSType::default(), +// PushBody::Put(Put { +// timestamp: None, +// encoding: Encoding::default(), +// ext_sinfo: None, +// #[cfg(feature = "shared-memory")] +// ext_shm: None, +// ext_unknown: vec![], +// payload: ZBuf::empty(), +// }), +// 0, +// ); + +// // functionnal check +// assert!(primitives1.get_last_name().is_some()); +// assert_eq!(primitives1.get_last_name().unwrap(), "test/client/z1_wr1"); +// // mapping strategy check +// // assert_eq!(primitives1.get_last_key().unwrap(), KeyExpr::IdWithSuffix(21, "/z1_wr1".to_string())); + +// // functionnal check +// assert!(primitives2.get_last_name().is_some()); +// assert_eq!(primitives2.get_last_name().unwrap(), "test/client/z1_wr1"); +// // mapping strategy check +// // assert_eq!(primitives2.get_last_key().unwrap(), KeyExpr::IdWithSuffix(31, "/z1_wr1".to_string())); + +// primitives0.clear_data(); +// primitives1.clear_data(); +// primitives2.clear_data(); +// full_reentrant_route_data( +// &tables.tables, +// &face0.upgrade().unwrap(), +// &WireExpr::from(11).with_suffix("/z1_wr2"), +// ext::QoSType::default(), +// PushBody::Put(Put { +// timestamp: None, +// encoding: Encoding::default(), +// ext_sinfo: None, +// #[cfg(feature = "shared-memory")] +// ext_shm: None, +// ext_unknown: vec![], +// payload: ZBuf::empty(), +// }), +// 0, +// ); + +// // functionnal check +// assert!(primitives1.get_last_name().is_some()); +// assert_eq!(primitives1.get_last_name().unwrap(), "test/client/z1_wr2"); +// // mapping strategy check +// // assert_eq!(primitives1.get_last_key().unwrap(), KeyExpr::IdWithSuffix(21, "/z1_wr2".to_string())); + +// // functionnal check +// assert!(primitives2.get_last_name().is_some()); +// assert_eq!(primitives2.get_last_name().unwrap(), "test/client/z1_wr2"); +// // mapping strategy check +// // assert_eq!(primitives2.get_last_key().unwrap(), KeyExpr::IdWithSuffix(31, "/z1_wr2".to_string())); + +// primitives0.clear_data(); +// primitives1.clear_data(); +// primitives2.clear_data(); +// full_reentrant_route_data( +// &tables.tables, +// &face1.upgrade().unwrap(), +// &"test/client/**".into(), +// ext::QoSType::default(), +// PushBody::Put(Put { +// timestamp: None, +// encoding: Encoding::default(), +// ext_sinfo: None, +// #[cfg(feature = "shared-memory")] +// ext_shm: None, +// ext_unknown: vec![], +// payload: ZBuf::empty(), +// }), +// 0, +// ); + +// // functionnal check +// assert!(primitives0.get_last_name().is_some()); +// assert_eq!(primitives0.get_last_name().unwrap(), "test/client/**"); +// // mapping strategy check +// // assert_eq!(primitives1.get_last_key().unwrap(), KeyExpr::IdWithSuffix(11, "/**".to_string())); + +// // functionnal check +// assert!(primitives2.get_last_name().is_some()); +// assert_eq!(primitives2.get_last_name().unwrap(), "test/client/**"); +// // mapping strategy check +// // assert_eq!(primitives2.get_last_key().unwrap(), KeyExpr::IdWithSuffix(31, "/**".to_string())); + +// primitives0.clear_data(); +// primitives1.clear_data(); +// primitives2.clear_data(); +// full_reentrant_route_data( +// &tables.tables, +// &face0.upgrade().unwrap(), +// &12.into(), +// ext::QoSType::default(), +// PushBody::Put(Put { +// timestamp: None, +// encoding: Encoding::default(), +// ext_sinfo: None, +// #[cfg(feature = "shared-memory")] +// ext_shm: None, +// ext_unknown: vec![], +// payload: ZBuf::empty(), +// }), +// 0, +// ); + +// // functionnal check +// assert!(primitives1.get_last_name().is_some()); +// assert_eq!(primitives1.get_last_name().unwrap(), "test/client/z1_pub1"); +// // mapping strategy check +// // assert_eq!(primitives1.get_last_key().unwrap(), KeyExpr::IdWithSuffix(21, "/z1_pub1".to_string())); + +// // functionnal check +// assert!(primitives2.get_last_name().is_some()); +// assert_eq!(primitives2.get_last_name().unwrap(), "test/client/z1_pub1"); +// // mapping strategy check +// // assert_eq!(primitives2.get_last_key().unwrap(), KeyExpr::IdWithSuffix(31, "/z1_pub1".to_string())); + +// primitives0.clear_data(); +// primitives1.clear_data(); +// primitives2.clear_data(); +// full_reentrant_route_data( +// &tables.tables, +// &face1.upgrade().unwrap(), +// &22.into(), +// ext::QoSType::default(), +// PushBody::Put(Put { +// timestamp: None, +// encoding: Encoding::default(), +// ext_sinfo: None, +// #[cfg(feature = "shared-memory")] +// ext_shm: None, +// ext_unknown: vec![], +// payload: ZBuf::empty(), +// }), +// 0, +// ); + +// // functionnal check +// assert!(primitives0.get_last_name().is_some()); +// assert_eq!(primitives0.get_last_name().unwrap(), "test/client/z2_pub1"); +// // mapping strategy check +// // assert_eq!(primitives1.get_last_key().unwrap(), KeyExpr::IdWithSuffix(11, "/z2_pub1".to_string())); + +// // functionnal check +// assert!(primitives2.get_last_name().is_some()); +// assert_eq!(primitives2.get_last_name().unwrap(), "test/client/z2_pub1"); +// // mapping strategy check +// // assert_eq!(primitives2.get_last_key().unwrap(), KeyExpr::IdWithSuffix(31, "/z2_pub1".to_string())); +// } diff --git a/zenoh/src/session.rs b/zenoh/src/session.rs index 744f21965f..29a87c24d3 100644 --- a/zenoh/src/session.rs +++ b/zenoh/src/session.rs @@ -20,7 +20,7 @@ use crate::info::*; use crate::key_expr::KeyExprInner; #[zenoh_macros::unstable] use crate::liveliness::{Liveliness, LivelinessTokenState}; -use crate::net::routing::face::Face; +use crate::net::routing::dispatcher::face::Face; use crate::net::runtime::Runtime; use crate::net::transport::Primitives; use crate::prelude::Locality;