From 1d364b3f733b2214d9f6dcaf3697ae8e2c269f96 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Sat, 22 Oct 2022 01:29:37 +0200 Subject: [PATCH 01/16] Add first end-to-end reliability implementation in zenoh-ext --- Cargo.lock | 1 + zenoh-ext/Cargo.toml | 13 + zenoh-ext/examples/z_rel_cache.rs | 115 ++++++++ zenoh-ext/examples/z_rel_pub.rs | 113 ++++++++ zenoh-ext/examples/z_rel_sub.rs | 114 ++++++++ zenoh-ext/src/lib.rs | 8 +- zenoh-ext/src/reliability_cache.rs | 247 ++++++++++++++++ zenoh-ext/src/reliable_publisher.rs | 232 +++++++++++++++ zenoh-ext/src/reliable_subscriber.rs | 404 +++++++++++++++++++++++++++ zenoh-ext/src/session_ext.rs | 87 +++++- 10 files changed, 1332 insertions(+), 2 deletions(-) create mode 100644 zenoh-ext/examples/z_rel_cache.rs create mode 100644 zenoh-ext/examples/z_rel_pub.rs create mode 100644 zenoh-ext/examples/z_rel_sub.rs create mode 100644 zenoh-ext/src/reliability_cache.rs create mode 100644 zenoh-ext/src/reliable_publisher.rs create mode 100644 zenoh-ext/src/reliable_subscriber.rs diff --git a/Cargo.lock b/Cargo.lock index 3e7f11c523..caaeaae48f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3663,6 +3663,7 @@ dependencies = [ "serde", "zenoh", "zenoh-core", + "zenoh-protocol", "zenoh-sync", "zenoh-util", ] diff --git a/zenoh-ext/Cargo.toml b/zenoh-ext/Cargo.toml index 68a0f4974f..7232fc5cd8 100644 --- a/zenoh-ext/Cargo.toml +++ b/zenoh-ext/Cargo.toml @@ -43,6 +43,7 @@ log = "0.4.17" serde = "1.0.145" zenoh = { version = "0.6.0-beta.1", path = "../zenoh", default-features = false, features = ["unstable"] } zenoh-core = { version = "0.6.0-beta.1", path = "../commons/zenoh-core/" } +zenoh-protocol = { version = "0.6.0-beta.1", path = "../commons/zenoh-protocol" } zenoh-sync = { version = "0.6.0-beta.1", path = "../commons/zenoh-sync" } zenoh-util = { version = "0.6.0-beta.1", path = "../commons/zenoh-util" } @@ -65,3 +66,15 @@ path = "examples/z_member.rs" [[example]] name = "z_view_size" path = "examples/z_view_size.rs" + +[[example]] +name = "z_rel_sub" +path = "examples/z_rel_sub.rs" + +[[example]] +name = "z_rel_pub" +path = "examples/z_rel_pub.rs" + +[[example]] +name = "z_rel_cache" +path = "examples/z_rel_cache.rs" diff --git a/zenoh-ext/examples/z_rel_cache.rs b/zenoh-ext/examples/z_rel_cache.rs new file mode 100644 index 0000000000..1935bebe1f --- /dev/null +++ b/zenoh-ext/examples/z_rel_cache.rs @@ -0,0 +1,115 @@ +// +// Copyright (c) 2022 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 async_std::task::sleep; +use clap::{App, Arg}; +use futures::prelude::*; +use std::time::Duration; +use zenoh::config::Config; +use zenoh::prelude::r#async::*; +use zenoh_ext::*; + +#[async_std::main] +async fn main() { + // Initiate logging + env_logger::init(); + + let (config, key_expr, history, prefix) = parse_args(); + + println!("Opening session..."); + let session = zenoh::open(config).res().await.unwrap().into_arc(); + + println!("Declaring ReliabilityCache on {}", key_expr); + let _cache = session + .declare_reliability_cache(key_expr) + .history(history) + .queryable_prefix(prefix) + .res() + .await + .unwrap(); + + println!("Enter 'q' to quit..."); + let mut stdin = async_std::io::stdin(); + let mut input = [0_u8]; + loop { + let _ = stdin.read_exact(&mut input).await; + match input[0] { + b'q' => break, + 0 => sleep(Duration::from_secs(1)).await, + _ => (), + } + } +} + +fn parse_args() -> (Config, String, usize, String) { + let args = App::new("zenoh-ext reliability cache example") + .arg( + Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") + .possible_values(&["peer", "client"]), + ) + .arg(Arg::from_usage( + "-e, --connect=[ENDPOINT]... 'Endpoints to connect to.'", + )) + .arg(Arg::from_usage( + "-l, --listen=[ENDPOINT]... 'Endpoints to listen on.'", + )) + .arg( + Arg::from_usage("-k, --key=[KEYEXPR] 'The key expression to subscribe onto'") + .default_value("demo/reliable/example/**"), + ) + .arg(Arg::from_usage( + "-c, --config=[FILE] 'A configuration file.'", + )) + .arg( + Arg::from_usage("-h, --history=[SIZE] 'The number of publications to keep in cache'") + .default_value("1024"), + ) + .arg( + Arg::from_usage("-x, --prefix=[STRING] 'The id of publishers to cache'") + .default_value("*"), + ) + .arg(Arg::from_usage( + "--no-multicast-scouting 'Disable the multicast-based scouting mechanism.'", + )) + .get_matches(); + + let mut config = if let Some(conf_file) = args.value_of("config") { + Config::from_file(conf_file).unwrap() + } else { + Config::default() + }; + if let Some(Ok(mode)) = args.value_of("mode").map(|mode| mode.parse()) { + config.set_mode(Some(mode)).unwrap(); + } + if let Some(values) = args.values_of("connect") { + config + .connect + .endpoints + .extend(values.map(|v| v.parse().unwrap())) + } + if let Some(values) = args.values_of("listen") { + config + .listen + .endpoints + .extend(values.map(|v| v.parse().unwrap())) + } + if args.is_present("no-multicast-scouting") { + config.scouting.multicast.set_enabled(Some(false)).unwrap(); + } + + let key_expr = args.value_of("key").unwrap().to_string(); + let history: usize = args.value_of("history").unwrap().parse().unwrap(); + let prefix = args.value_of("prefix").unwrap().to_string(); + + (config, key_expr, history, prefix) +} diff --git a/zenoh-ext/examples/z_rel_pub.rs b/zenoh-ext/examples/z_rel_pub.rs new file mode 100644 index 0000000000..6ab2fd44f2 --- /dev/null +++ b/zenoh-ext/examples/z_rel_pub.rs @@ -0,0 +1,113 @@ +// +// Copyright (c) 2022 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 async_std::task::sleep; +use clap::{App, Arg}; +use std::time::Duration; +use zenoh::config::Config; +use zenoh::prelude::r#async::*; +use zenoh_ext::*; + +#[async_std::main] +async fn main() { + // Initiate logging + env_logger::init(); + + let (config, key_expr, value, cache, history) = parse_args(); + + println!("Opening session..."); + let session = zenoh::open(config).res().await.unwrap(); + + println!("Declaring ReliablePublisher on {}", &key_expr); + let publ = session + .declare_reliable_publisher(&key_expr) + .with_cache(cache) + .history(history) + .res() + .await + .unwrap(); + + for idx in 0..u32::MAX { + sleep(Duration::from_secs(1)).await; + let buf = format!("[{:4}] {}", idx, value); + println!("Pub Data ('{}': '{}')", &key_expr, buf); + publ.put(buf).res().await.unwrap(); + } +} + +fn parse_args() -> (Config, String, String, bool, usize) { + let args = App::new("zenoh-ext reliable pub example") + .arg( + Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") + .possible_values(&["peer", "client"]), + ) + .arg(Arg::from_usage( + "-e, --connect=[ENDPOINT]... 'Endpoints to connect to.'", + )) + .arg(Arg::from_usage( + "-l, --listen=[ENDPOINT]... 'Endpoints to listen on.'", + )) + .arg( + Arg::from_usage("-k, --key=[KEYEXPR] 'The key expression to publish.'") + .default_value("demo/reliable/example/zenoh-rs-pub"), + ) + .arg( + Arg::from_usage("-v, --value=[VALUE] 'The value to publish.'") + .default_value("Pub from Rust!"), + ) + .arg(Arg::from_usage( + "-n, --no-cache 'Disable local reliability cache'", + )) + .arg( + Arg::from_usage("-h, --history=[SIZE] 'The number of publications to keep in cache'") + .default_value("1024"), + ) + .arg(Arg::from_usage( + "-c, --config=[FILE] 'A configuration file.'", + )) + .arg(Arg::from_usage( + "--no-multicast-scouting 'Disable the multicast-based scouting mechanism.'", + )) + .get_matches(); + + let mut config = if let Some(conf_file) = args.value_of("config") { + Config::from_file(conf_file).unwrap() + } else { + Config::default() + }; + if let Some(Ok(mode)) = args.value_of("mode").map(|mode| mode.parse()) { + config.set_mode(Some(mode)).unwrap(); + } + if let Some(values) = args.values_of("connect") { + config + .connect + .endpoints + .extend(values.map(|v| v.parse().unwrap())) + } + if let Some(values) = args.values_of("listen") { + config + .listen + .endpoints + .extend(values.map(|v| v.parse().unwrap())) + } + if args.is_present("no-multicast-scouting") { + config.scouting.multicast.set_enabled(Some(false)).unwrap(); + } + + let key_expr = args.value_of("key").unwrap().to_string(); + let value = args.value_of("value").unwrap().to_string(); + let cache = !args.is_present("no-cache"); + let history: usize = args.value_of("history").unwrap().parse().unwrap(); + + (config, key_expr, value, cache, history) +} diff --git a/zenoh-ext/examples/z_rel_sub.rs b/zenoh-ext/examples/z_rel_sub.rs new file mode 100644 index 0000000000..1f59980843 --- /dev/null +++ b/zenoh-ext/examples/z_rel_sub.rs @@ -0,0 +1,114 @@ +// +// Copyright (c) 2022 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 async_std::task::sleep; +use clap::{App, Arg}; +use futures::prelude::*; +use futures::select; +use std::convert::TryFrom; +use std::time::Duration; +use zenoh::config::Config; +use zenoh::prelude::r#async::*; +use zenoh_ext::*; + +#[async_std::main] +async fn main() { + // Initiate logging + env_logger::init(); + + let (config, key_expr) = parse_args(); + + println!("Opening session..."); + let session = zenoh::open(config).res().await.unwrap().into_arc(); + + println!("Declaring ReliableSubscriber on {}", key_expr); + let subscriber = session + .declare_reliable_subscriber(key_expr) + .res() + .await + .unwrap(); + + println!("Enter 'q' to quit..."); + let mut stdin = async_std::io::stdin(); + let mut input = [0_u8]; + loop { + select!( + sample = subscriber.recv_async() => { + let sample = sample.unwrap(); + println!(">> [Subscriber] Received {} ('{}': '{}')", + sample.kind, sample.key_expr.as_str(), String::try_from(&sample.value).unwrap()); + }, + + _ = stdin.read_exact(&mut input).fuse() => { + match input[0] { + b'q' => break, + 0 => sleep(Duration::from_secs(1)).await, + _ => (), + } + } + ); + } +} + +fn parse_args() -> (Config, String) { + let args = App::new("zenoh-ext reliable sub example") + .arg( + Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") + .possible_values(&["peer", "client"]), + ) + .arg(Arg::from_usage( + "-e, --connect=[ENDPOINT]... 'Endpoints to connect to.'", + )) + .arg(Arg::from_usage( + "-l, --listen=[ENDPOINT]... 'Endpoints to listen on.'", + )) + .arg( + Arg::from_usage("-k, --key=[KEYEXPR] 'The key expression to subscribe onto'") + .default_value("demo/reliable/example/**"), + ) + .arg(Arg::from_usage( + "-c, --config=[FILE] 'A configuration file.'", + )) + .arg(Arg::from_usage( + "--no-multicast-scouting 'Disable the multicast-based scouting mechanism.'", + )) + .get_matches(); + + let mut config = if let Some(conf_file) = args.value_of("config") { + Config::from_file(conf_file).unwrap() + } else { + Config::default() + }; + if let Some(Ok(mode)) = args.value_of("mode").map(|mode| mode.parse()) { + config.set_mode(Some(mode)).unwrap(); + } + if let Some(values) = args.values_of("connect") { + config + .connect + .endpoints + .extend(values.map(|v| v.parse().unwrap())) + } + if let Some(values) = args.values_of("listen") { + config + .listen + .endpoints + .extend(values.map(|v| v.parse().unwrap())) + } + if args.is_present("no-multicast-scouting") { + config.scouting.multicast.set_enabled(Some(false)).unwrap(); + } + + let key_expr = args.value_of("key").unwrap().to_string(); + + (config, key_expr) +} diff --git a/zenoh-ext/src/lib.rs b/zenoh-ext/src/lib.rs index c300a413d4..bae1bab993 100644 --- a/zenoh-ext/src/lib.rs +++ b/zenoh-ext/src/lib.rs @@ -14,9 +14,15 @@ pub mod group; mod publication_cache; mod querying_subscriber; +mod reliability_cache; +mod reliable_publisher; +mod reliable_subscriber; mod session_ext; mod subscriber_ext; pub use publication_cache::{PublicationCache, PublicationCacheBuilder}; pub use querying_subscriber::{QueryingSubscriber, QueryingSubscriberBuilder}; -pub use session_ext::SessionExt; +pub use reliability_cache::{ReliabilityCache, ReliabilityCacheBuilder}; +pub use reliable_publisher::{ReliablePublisher, ReliablePublisherBuilder}; +pub use reliable_subscriber::{ReliableSubscriber, ReliableSubscriberBuilder}; +pub use session_ext::{ArcSessionExt, SessionExt}; pub use subscriber_ext::SubscriberForward; diff --git a/zenoh-ext/src/reliability_cache.rs b/zenoh-ext/src/reliability_cache.rs new file mode 100644 index 0000000000..a9a30be928 --- /dev/null +++ b/zenoh-ext/src/reliability_cache.rs @@ -0,0 +1,247 @@ +// +// Copyright (c) 2022 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 async_std::channel::{bounded, Sender}; +use async_std::task; +use futures::select; +use futures::{FutureExt, StreamExt}; +use std::collections::{HashMap, VecDeque}; +use std::convert::TryInto; +use std::future::Ready; +use zenoh::prelude::r#async::*; +use zenoh::queryable::{Query, Queryable}; +use zenoh::subscriber::FlumeSubscriber; +use zenoh::Session; +use zenoh_core::{bail, AsyncResolve, Resolvable, Result as ZResult, SyncResolve}; +use zenoh_util::core::ResolveFuture; + +/// The builder of ReliabilityCache, allowing to configure it. +pub struct ReliabilityCacheBuilder<'a, 'b, 'c> { + session: &'a Session, + pub_key_expr: ZResult>, + queryable_prefix: Option>>, + subscriber_origin: Locality, + queryable_origin: Locality, + history: usize, + resources_limit: Option, +} + +impl<'a, 'b, 'c> ReliabilityCacheBuilder<'a, 'b, 'c> { + pub(crate) fn new( + session: &'a Session, + pub_key_expr: ZResult>, + ) -> ReliabilityCacheBuilder<'a, 'b, 'c> { + ReliabilityCacheBuilder { + session, + pub_key_expr, + queryable_prefix: None, + subscriber_origin: Locality::default(), + queryable_origin: Locality::default(), + history: 1024, + resources_limit: None, + } + } + + /// Change the prefix used for queryable. + pub fn queryable_prefix(mut self, queryable_prefix: TryIntoKeyExpr) -> Self + where + TryIntoKeyExpr: TryInto>, + >>::Error: Into, + { + self.queryable_prefix = Some(queryable_prefix.try_into().map_err(Into::into)); + self + } + + /// Restrict the matching publications that will be cached by this [`ReliabilityCache`] + /// to the ones that have the given [`Locality`](crate::prelude::Locality). + #[inline] + pub fn subscriber_allowed_origin(mut self, origin: Locality) -> Self { + self.subscriber_origin = origin; + self + } + + /// Restrict the matching queries that will be receive by this [`ReliabilityCache`]'s queryable + /// to the ones that have the given [`Locality`](crate::prelude::Locality). + #[inline] + pub fn queryable_allowed_origin(mut self, origin: Locality) -> Self { + self.queryable_origin = origin; + self + } + + /// Change the history size for each resource. + pub fn history(mut self, history: usize) -> Self { + self.history = history; + self + } + + /// Change the limit number of cached resources. + pub fn resources_limit(mut self, limit: usize) -> Self { + self.resources_limit = Some(limit); + self + } +} + +impl<'a> Resolvable for ReliabilityCacheBuilder<'a, '_, '_> { + type To = ZResult>; +} + +impl SyncResolve for ReliabilityCacheBuilder<'_, '_, '_> { + fn res_sync(self) -> ::To { + ReliabilityCache::new(self) + } +} + +impl<'a> AsyncResolve for ReliabilityCacheBuilder<'a, '_, '_> { + type Future = Ready; + + fn res_async(self) -> Self::Future { + std::future::ready(self.res_sync()) + } +} + +pub struct ReliabilityCache<'a> { + _sub: FlumeSubscriber<'a>, + _queryable: Queryable<'a, flume::Receiver>, + _stoptx: Sender, +} + +impl<'a> ReliabilityCache<'a> { + fn new(conf: ReliabilityCacheBuilder<'a, '_, '_>) -> ZResult> { + let key_expr = conf.pub_key_expr?; + // the queryable_prefix (optional), and the key_expr for ReliabilityCache's queryable ("[]/") + let (queryable_prefix, queryable_key_expr): (Option, KeyExpr) = + match conf.queryable_prefix { + None => (None, key_expr.clone()), + Some(Ok(ke)) => { + let queryable_key_expr = (&ke) / &key_expr; + (Some(ke.into()), queryable_key_expr) + } + Some(Err(e)) => bail!("Invalid key expression for queryable_prefix: {}", e), + }; + log::debug!( + "Create ReliabilityCache on {} with history={} resource_limit={:?}", + &key_expr, + conf.history, + conf.resources_limit + ); + + // declare the local subscriber that will store the local publications + let sub = conf + .session + .declare_subscriber(&key_expr) + .allowed_origin(conf.subscriber_origin) + .res_sync()?; + + // declare the queryable that will answer to queries on cache + let queryable = conf + .session + .declare_queryable(&queryable_key_expr) + .allowed_origin(conf.queryable_origin) + .res_sync()?; + + // take local ownership of stuff to be moved into task + let sub_recv = sub.receiver.clone(); + let quer_recv = queryable.receiver.clone(); + let pub_key_expr = key_expr.into_owned(); + let resources_limit = conf.resources_limit; + let history = conf.history; + + let (stoptx, mut stoprx) = bounded::(1); + task::spawn(async move { + let mut cache: HashMap> = + HashMap::with_capacity(resources_limit.unwrap_or(32)); + let limit = resources_limit.unwrap_or(usize::MAX); + + loop { + select!( + // on publication received by the local subscriber, store it + sample = sub_recv.recv_async() => { + if let Ok(sample) = sample { + let queryable_key_expr: KeyExpr<'_> = if let Some(prefix) = &queryable_prefix { + prefix.join(&sample.key_expr).unwrap().into() + } else { + sample.key_expr.clone() + }; + + if let Some(queue) = cache.get_mut(queryable_key_expr.as_keyexpr()) { + if queue.len() >= history { + queue.pop_front(); + } + queue.push_back(sample); + } else if cache.len() >= limit { + log::error!("ReliabilityCache on {}: resource_limit exceeded - can't cache publication for a new resource", + pub_key_expr); + } else { + let mut queue: VecDeque = VecDeque::new(); + queue.push_back(sample); + cache.insert(queryable_key_expr.into(), queue); + } + } + }, + + // on query, reply with cach content + query = quer_recv.recv_async() => { + if let Ok(query) = query { + if !query.selector().key_expr.as_str().contains('*') { + if let Some(queue) = cache.get(query.selector().key_expr.as_keyexpr()) { + for sample in queue { + if let Err(e) = query.reply(Ok(sample.clone())).res_async().await { + log::warn!("Error replying to query: {}", e); + } + } + } + } else { + for (key_expr, queue) in cache.iter() { + if query.selector().key_expr.intersects(unsafe{ keyexpr::from_str_unchecked(key_expr) }) { + for sample in queue { + if let Err(e) = query.reply(Ok(sample.clone())).res_async().await { + log::warn!("Error replying to query: {}", e); + } + } + } + } + } + } + }, + + // When stoptx is dropped, stop the task + _ = stoprx.next().fuse() => { + return + } + ); + } + }); + + Ok(ReliabilityCache { + _sub: sub, + _queryable: queryable, + _stoptx: stoptx, + }) + } + + /// Close this ReliabilityCache + #[inline] + pub fn close(self) -> impl Resolve> + 'a { + ResolveFuture::new(async move { + let ReliabilityCache { + _queryable, + _sub, + _stoptx, + } = self; + _queryable.undeclare().res_async().await?; + _sub.undeclare().res_async().await?; + drop(_stoptx); + Ok(()) + }) + } +} diff --git a/zenoh-ext/src/reliable_publisher.rs b/zenoh-ext/src/reliable_publisher.rs new file mode 100644 index 0000000000..585644095b --- /dev/null +++ b/zenoh-ext/src/reliable_publisher.rs @@ -0,0 +1,232 @@ +// +// Copyright (c) 2022 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 std::future::Ready; +use std::sync::atomic::{AtomicU64, Ordering}; +use zenoh::buffers::WBuf; +use zenoh::prelude::r#async::*; +use zenoh::publication::{Publication, Publisher}; +use zenoh_core::{AsyncResolve, Resolvable, Result as ZResult, SyncResolve}; +use zenoh_protocol::io::WBufCodec; +use zenoh_util::core::ResolveFuture; + +use crate::{ReliabilityCache, SessionExt}; + +/// The builder of ReliablePublisher, allowing to configure it. +pub struct ReliablePublisherBuilder<'a, 'b> { + session: &'a Session, + pub_key_expr: ZResult>, + with_cache: bool, + history: usize, + resources_limit: Option, +} + +impl<'a, 'b> ReliablePublisherBuilder<'a, 'b> { + pub(crate) fn new( + session: &'a Session, + pub_key_expr: ZResult>, + ) -> ReliablePublisherBuilder<'a, 'b> { + ReliablePublisherBuilder { + session, + pub_key_expr, + with_cache: true, + history: 1024, + resources_limit: None, + } + } + + /// Change the limit number of cached resources. + pub fn with_cache(mut self, with_cache: bool) -> Self { + self.with_cache = with_cache; + self + } + + /// Change the history size for each resource. + pub fn history(mut self, history: usize) -> Self { + self.history = history; + self + } + + /// Change the limit number of cached resources. + pub fn resources_limit(mut self, limit: usize) -> Self { + self.resources_limit = Some(limit); + self + } +} + +impl<'a> Resolvable for ReliablePublisherBuilder<'a, '_> { + type To = ZResult>; +} + +impl SyncResolve for ReliablePublisherBuilder<'_, '_> { + fn res_sync(self) -> ::To { + ReliablePublisher::new(self) + } +} + +impl<'a> AsyncResolve for ReliablePublisherBuilder<'a, '_> { + type Future = Ready; + + fn res_async(self) -> Self::Future { + std::future::ready(self.res_sync()) + } +} + +pub struct ReliablePublisher<'a> { + _id: ZenohId, + _seqnum: AtomicU64, + _publisher: Publisher<'a>, + _cache: Option>, +} + +impl<'a> ReliablePublisher<'a> { + fn new(conf: ReliablePublisherBuilder<'a, '_>) -> ZResult { + let key_expr = conf.pub_key_expr?; + let id = conf.session.info().zid().res_sync(); + + let publisher = conf + .session + .declare_publisher(key_expr.clone().into_owned()) + .res_sync()?; + + let cache = if conf.with_cache { + let prefix = id.into_keyexpr(); + let mut builder = conf + .session + .declare_reliability_cache(key_expr.into_owned()) + .subscriber_allowed_origin(Locality::SessionLocal) + .history(conf.history) + .queryable_prefix(&prefix); + if let Some(resources_limit) = conf.resources_limit { + builder = builder.resources_limit(resources_limit); + } + Some(builder.res_sync()?) + } else { + None + }; + + Ok(ReliablePublisher { + _id: id, + _seqnum: AtomicU64::new(0), + _publisher: publisher, + _cache: cache, + }) + } + + pub fn key_expr(&self) -> &KeyExpr<'a> { + self._publisher.key_expr() + } + + /// Change the `congestion_control` to apply when routing the data. + #[inline] + pub fn congestion_control(mut self, congestion_control: CongestionControl) -> Self { + self._publisher = self._publisher.congestion_control(congestion_control); + self + } + + /// Change the priority of the written data. + #[inline] + pub fn priority(mut self, priority: Priority) -> Self { + self._publisher = self._publisher.priority(priority); + self + } + + /// Send data with [`kind`](SampleKind) (Put or Delete). + /// + /// # Examples + /// ``` + /// # async_std::task::block_on(async { + /// use zenoh::prelude::r#async::*; + /// + /// let session = zenoh::open(config::peer()).res().await.unwrap().into_arc(); + /// let publisher = session.declare_publisher("key/expression").res().await.unwrap(); + /// publisher.write(SampleKind::Put, "value").res().await.unwrap(); + /// # }) + /// ``` + pub fn write(&self, kind: SampleKind, value: IntoValue) -> Publication + where + IntoValue: Into, + { + let v: Value = value.into(); + let mut buf = WBuf::new(128, false); + buf.write_zid(&self._id); + buf.write_zint(self._seqnum.fetch_add(1, Ordering::Relaxed)); + buf.write_zbuf_slices(&v.payload); + self._publisher + .write(kind, Value::new(buf.into()).encoding(v.encoding)) + } + + /// Put data. + /// + /// # Examples + /// ``` + /// # async_std::task::block_on(async { + /// use zenoh::prelude::r#async::*; + /// + /// let session = zenoh::open(config::peer()).res().await.unwrap().into_arc(); + /// let publisher = session.declare_publisher("key/expression").res().await.unwrap(); + /// publisher.put("value").res().await.unwrap(); + /// # }) + /// ``` + #[inline] + pub fn put(&self, value: IntoValue) -> Publication + where + IntoValue: Into, + { + self.write(SampleKind::Put, value) + } + + /// Delete data. + /// + /// # Examples + /// ``` + /// # async_std::task::block_on(async { + /// use zenoh::prelude::r#async::*; + /// + /// let session = zenoh::open(config::peer()).res().await.unwrap().into_arc(); + /// let publisher = session.declare_publisher("key/expression").res().await.unwrap(); + /// publisher.delete().res().await.unwrap(); + /// # }) + /// ``` + pub fn delete(&self) -> Publication { + self.write(SampleKind::Delete, Value::empty()) + } + + /// Undeclares the [`Publisher`], informing the network that it needn't optimize publications for its key expression anymore. + /// + /// # Examples + /// ``` + /// # async_std::task::block_on(async { + /// use zenoh::prelude::r#async::*; + /// + /// let session = zenoh::open(config::peer()).res().await.unwrap(); + /// let publisher = session.declare_publisher("key/expression").res().await.unwrap(); + /// publisher.undeclare().res().await.unwrap(); + /// # }) + /// ``` + pub fn undeclare(self) -> impl Resolve> + 'a { + ResolveFuture::new(async move { + let ReliablePublisher { + _id, + _seqnum, + _publisher, + _cache, + } = self; + if let Some(cache) = _cache { + cache.close().res_async().await?; + } + _publisher.undeclare().res_async().await?; + Ok(()) + }) + } +} diff --git a/zenoh-ext/src/reliable_subscriber.rs b/zenoh-ext/src/reliable_subscriber.rs new file mode 100644 index 0000000000..301fe5d6de --- /dev/null +++ b/zenoh-ext/src/reliable_subscriber.rs @@ -0,0 +1,404 @@ +// +// Copyright (c) 2022 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 std::collections::HashMap; +use std::convert::TryInto; +use std::future::Ready; +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use zenoh::buffers::reader::{HasReader, Reader}; +use zenoh::buffers::ZBuf; +use zenoh::handlers::{locked, DefaultHandler}; +use zenoh::prelude::r#async::*; +use zenoh::query::{QueryConsolidation, QueryTarget, Reply, ReplyKeyExpr}; +use zenoh::subscriber::{Reliability, Subscriber}; +use zenoh::Result as ZResult; +use zenoh_core::{zlock, AsyncResolve, Resolvable, SyncResolve}; +use zenoh_protocol::io::ZBufCodec; + +/// The builder of ReliableSubscriber, allowing to configure it. +pub struct ReliableSubscriberBuilder<'b, Handler> { + session: Arc, + key_expr: ZResult>, + reliability: Reliability, + origin: Locality, + query_selector: Option>>, + query_target: QueryTarget, + query_consolidation: QueryConsolidation, + query_timeout: Duration, + handler: Handler, +} + +impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { + pub(crate) fn new( + session: Arc, + key_expr: ZResult>, + ) -> ReliableSubscriberBuilder<'b, DefaultHandler> { + // By default query all matching publication caches and storages + let query_target = QueryTarget::All; + + // By default no query consolidation, to receive more than 1 sample per-resource + // (if history of publications is available) + let query_consolidation = QueryConsolidation::from(zenoh::query::ConsolidationMode::None); + + ReliableSubscriberBuilder { + session, + key_expr, + reliability: Reliability::default(), + origin: Locality::default(), + query_selector: None, + query_target, + query_consolidation, + query_timeout: Duration::from_secs(10), + handler: DefaultHandler, + } + } + + /// Add callback to ReliableSubscriber. + #[inline] + pub fn callback(self, callback: Callback) -> ReliableSubscriberBuilder<'b, Callback> + where + Callback: Fn(Sample) + Send + Sync + 'static, + { + let ReliableSubscriberBuilder { + session, + key_expr, + reliability, + origin, + query_selector, + query_target, + query_consolidation, + query_timeout, + handler: _, + } = self; + ReliableSubscriberBuilder { + session, + key_expr, + reliability, + origin, + query_selector, + query_target, + query_consolidation, + query_timeout, + handler: callback, + } + } + + /// Add callback to `ReliableSubscriber`. + /// + /// Using this guarantees that your callback will never be called concurrently. + /// If your callback is also accepted by the [`callback`](ReliableSubscriberBuilder::callback) method, we suggest you use it instead of `callback_mut` + #[inline] + pub fn callback_mut( + self, + callback: CallbackMut, + ) -> ReliableSubscriberBuilder<'b, impl Fn(Sample) + Send + Sync + 'static> + where + CallbackMut: FnMut(Sample) + Send + Sync + 'static, + { + self.callback(locked(callback)) + } + + /// Make the built ReliableSubscriber a [`ReliableSubscriber`](ReliableSubscriber). + #[inline] + pub fn with(self, handler: Handler) -> ReliableSubscriberBuilder<'b, Handler> + where + Handler: zenoh::prelude::IntoCallbackReceiverPair<'static, Sample>, + { + let ReliableSubscriberBuilder { + session, + key_expr, + reliability, + origin, + query_selector, + query_target, + query_consolidation, + query_timeout, + handler: _, + } = self; + ReliableSubscriberBuilder { + session, + key_expr, + reliability, + origin, + query_selector, + query_target, + query_consolidation, + query_timeout, + handler, + } + } +} +impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { + /// Change the subscription reliability. + #[inline] + pub fn reliability(mut self, reliability: Reliability) -> Self { + self.reliability = reliability; + self + } + + /// Change the subscription reliability to Reliable. + #[inline] + pub fn reliable(mut self) -> Self { + self.reliability = Reliability::Reliable; + self + } + + /// Change the subscription reliability to BestEffort. + #[inline] + pub fn best_effort(mut self) -> Self { + self.reliability = Reliability::BestEffort; + self + } + + /// Restrict the matching publications that will be receive by this [`Subscriber`] + /// to the ones that have the given [`Locality`](crate::prelude::Locality). + #[zenoh_core::unstable] + #[inline] + pub fn allowed_origin(mut self, origin: Locality) -> Self { + self.origin = origin; + self + } + + /// Change the selector to be used for queries. + #[inline] + pub fn query_selector(mut self, query_selector: IntoSelector) -> Self + where + IntoSelector: TryInto>, + >>::Error: Into, + { + self.query_selector = Some(query_selector.try_into().map_err(Into::into)); + self + } + + /// Change the target to be used for queries. + #[inline] + pub fn query_target(mut self, query_target: QueryTarget) -> Self { + self.query_target = query_target; + self + } + + /// Change the consolidation mode to be used for queries. + #[inline] + pub fn query_consolidation>( + mut self, + query_consolidation: QC, + ) -> Self { + self.query_consolidation = query_consolidation.into(); + self + } + + /// Change the timeout to be used for queries. + #[inline] + pub fn query_timeout(mut self, query_timeout: Duration) -> Self { + self.query_timeout = query_timeout; + self + } + + fn with_static_keys(self) -> ReliableSubscriberBuilder<'static, Handler> { + ReliableSubscriberBuilder { + session: self.session, + key_expr: self.key_expr.map(|s| s.into_owned()), + reliability: self.reliability, + origin: self.origin, + query_selector: self.query_selector.map(|s| s.map(|s| s.into_owned())), + query_target: self.query_target, + query_consolidation: self.query_consolidation, + query_timeout: self.query_timeout, + handler: self.handler, + } + } +} + +impl<'a, Handler> Resolvable for ReliableSubscriberBuilder<'a, Handler> +where + Handler: IntoCallbackReceiverPair<'static, Sample>, + Handler::Receiver: Send, +{ + type To = ZResult>; +} + +impl SyncResolve for ReliableSubscriberBuilder<'_, Handler> +where + Handler: IntoCallbackReceiverPair<'static, Sample> + Send, + Handler::Receiver: Send, +{ + fn res_sync(self) -> ::To { + ReliableSubscriber::new(self.with_static_keys()) + } +} + +impl AsyncResolve for ReliableSubscriberBuilder<'_, Handler> +where + Handler: IntoCallbackReceiverPair<'static, Sample> + Send, + Handler::Receiver: Send, +{ + type Future = Ready; + + fn res_async(self) -> Self::Future { + std::future::ready(self.res_sync()) + } +} +struct InnerState { + last_seq_num: Option, + pending_queries: u64, + pending_samples: HashMap, +} + +pub struct ReliableSubscriber<'a, Receiver> { + _subscriber: Subscriber<'a, ()>, + receiver: Receiver, +} +impl std::ops::Deref for ReliableSubscriber<'_, Receiver> { + type Target = Receiver; + fn deref(&self) -> &Self::Target { + &self.receiver + } +} +impl std::ops::DerefMut for ReliableSubscriber<'_, Receiver> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.receiver + } +} + +fn handle_sample( + states: &mut HashMap, + sample: Sample, + callback: &Arc, +) -> ZenohId { + let mut buf = sample.value.payload.reader(); + let id = buf.read_zid().unwrap(); //TODO + let seq_num = buf.read_zint().unwrap(); //TODO + let mut payload = ZBuf::default(); + buf.read_into_zbuf(&mut payload, buf.remaining()); + let value = Value::new(payload).encoding(sample.encoding.clone()); + let s = Sample::new(sample.key_expr, value); + let state = states.entry(id).or_insert(InnerState { + last_seq_num: None, + pending_queries: 0, + pending_samples: HashMap::new(), + }); + if state.last_seq_num.is_some() && seq_num != state.last_seq_num.unwrap() + 1 { + if seq_num > state.last_seq_num.unwrap() { + state.pending_samples.insert(seq_num, s); + } + } else { + callback(s); + let mut last_seq_num = seq_num; + state.last_seq_num = Some(last_seq_num); + while let Some(s) = state.pending_samples.remove(&(last_seq_num + 1)) { + callback(s); + last_seq_num += 1; + state.last_seq_num = Some(last_seq_num); + } + } + id +} + +impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { + fn new(conf: ReliableSubscriberBuilder<'a, Handler>) -> ZResult + where + Handler: IntoCallbackReceiverPair<'static, Sample, Receiver = Receiver> + Send, + { + let statesref = Arc::new(Mutex::new(HashMap::new())); + let (callback, receiver) = conf.handler.into_cb_receiver_pair(); + let key_expr = conf.key_expr?; + let query_timeout = conf.query_timeout; + + let sub_callback = { + let session = conf.session.clone(); + let callback = callback.clone(); + let key_expr = key_expr.clone().into_owned(); + + move |s: Sample| { + let mut states = zlock!(statesref); + let id = handle_sample(&mut states, s, &callback); + if let Some(state) = states.get_mut(&id) { + if state.pending_queries == 0 && !state.pending_samples.is_empty() { + state.pending_queries += 1; + drop(states); + let key_expr = (&id.into_keyexpr()) / &key_expr; + let handler = RepliesHandler { + id, + statesref: statesref.clone(), + callback: callback.clone(), + }; + let _ = session + .get(key_expr) + .callback({ + move |r: Reply| { + if let Ok(s) = r.sample { + let states = &mut zlock!(handler.statesref); + handle_sample(states, s, &handler.callback); + } + } + }) + .consolidation(ConsolidationMode::None) + .accept_replies(ReplyKeyExpr::Any) + .timeout(query_timeout) + .res_sync(); + } + } + } + }; + + let subscriber = conf + .session + .declare_subscriber(&key_expr) + .callback(sub_callback) + .reliability(conf.reliability) + .allowed_origin(conf.origin) + .res_sync()?; + + let reliable_subscriber = ReliableSubscriber { + _subscriber: subscriber, + receiver, + }; + + Ok(reliable_subscriber) + } + + /// Close this ReliableSubscriber + #[inline] + pub fn close(self) -> impl Resolve> + 'a { + self._subscriber.undeclare() + } +} + +#[derive(Clone)] +struct RepliesHandler { + id: ZenohId, + statesref: Arc>>, + callback: Arc, +} + +impl Drop for RepliesHandler { + fn drop(&mut self) { + let mut states = zlock!(self.statesref); + if let Some(state) = states.get_mut(&self.id) { + state.pending_queries -= 1; + if !state.pending_samples.is_empty() { + log::error!("Sample missed: unable to retrieve some missing samples."); + let mut pending_samples = state + .pending_samples + .drain() + .collect::>(); + pending_samples.sort_by_key(|(k, _s)| *k); + for (seq_num, sample) in pending_samples { + state.last_seq_num = Some(seq_num); + (self.callback)(sample); + } + } + } + } +} diff --git a/zenoh-ext/src/session_ext.rs b/zenoh-ext/src/session_ext.rs index 3b766dc798..697c404557 100644 --- a/zenoh-ext/src/session_ext.rs +++ b/zenoh-ext/src/session_ext.rs @@ -11,7 +11,10 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::{PublicationCacheBuilder, QueryingSubscriberBuilder}; +use super::{ + PublicationCacheBuilder, QueryingSubscriberBuilder, ReliabilityCacheBuilder, + ReliablePublisherBuilder, ReliableSubscriberBuilder, +}; use std::convert::TryInto; use std::fmt; use std::ops::Deref; @@ -93,6 +96,22 @@ pub trait SessionExt { where TryIntoKeyExpr: TryInto>, >>::Error: Into; + + fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( + &'a self, + pub_key_expr: TryIntoKeyExpr, + ) -> ReliabilityCacheBuilder<'a, 'b, 'c> + where + TryIntoKeyExpr: TryInto>, + >>::Error: Into; + + fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( + &'a self, + pub_key_expr: TryIntoKeyExpr, + ) -> ReliablePublisherBuilder<'a, 'b> + where + TryIntoKeyExpr: TryInto>, + >>::Error: Into; } impl SessionExt for Session { @@ -120,6 +139,37 @@ impl SessionExt for Session { { PublicationCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } + + fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( + &'a self, + pub_key_expr: TryIntoKeyExpr, + ) -> ReliabilityCacheBuilder<'a, 'b, 'c> + where + TryIntoKeyExpr: TryInto>, + >>::Error: Into, + { + ReliabilityCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) + } + + fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( + &'a self, + pub_key_expr: TryIntoKeyExpr, + ) -> ReliablePublisherBuilder<'a, 'b> + where + TryIntoKeyExpr: TryInto>, + >>::Error: Into, + { + ReliablePublisherBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) + } +} +pub trait ArcSessionExt { + fn declare_reliable_subscriber<'b, TryIntoKeyExpr>( + &self, + sub_key_expr: TryIntoKeyExpr, + ) -> ReliableSubscriberBuilder<'b, DefaultHandler> + where + TryIntoKeyExpr: TryInto>, + >>::Error: Into; } impl SessionExt for Arc { @@ -147,4 +197,39 @@ impl SessionExt for Arc { { PublicationCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } + + fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( + &'a self, + pub_key_expr: TryIntoKeyExpr, + ) -> ReliabilityCacheBuilder<'a, 'b, 'c> + where + TryIntoKeyExpr: TryInto>, + >>::Error: Into, + { + ReliabilityCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) + } + + fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( + &'a self, + pub_key_expr: TryIntoKeyExpr, + ) -> ReliablePublisherBuilder<'a, 'b> + where + TryIntoKeyExpr: TryInto>, + >>::Error: Into, + { + ReliablePublisherBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) + } +} + +impl ArcSessionExt for Arc { + fn declare_reliable_subscriber<'b, TryIntoKeyExpr>( + &self, + sub_key_expr: TryIntoKeyExpr, + ) -> ReliableSubscriberBuilder<'b, DefaultHandler> + where + TryIntoKeyExpr: TryInto>, + >>::Error: Into, + { + ReliableSubscriberBuilder::new(self.clone(), sub_key_expr.try_into().map_err(Into::into)) + } } From 01e69acd4fe79c5c1ad0d8219192ad9af4d45966 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Mon, 24 Oct 2022 10:21:00 +0200 Subject: [PATCH 02/16] ReliabilityCache can interpret a seq num range in query parameters --- zenoh-ext/src/reliability_cache.rs | 38 +++++++++++++++++++++++++--- zenoh-ext/src/reliable_subscriber.rs | 15 +++++++++-- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/zenoh-ext/src/reliability_cache.rs b/zenoh-ext/src/reliability_cache.rs index a9a30be928..e0e619fe0e 100644 --- a/zenoh-ext/src/reliability_cache.rs +++ b/zenoh-ext/src/reliability_cache.rs @@ -18,11 +18,13 @@ use futures::{FutureExt, StreamExt}; use std::collections::{HashMap, VecDeque}; use std::convert::TryInto; use std::future::Ready; +use zenoh::buffers::reader::HasReader; use zenoh::prelude::r#async::*; use zenoh::queryable::{Query, Queryable}; use zenoh::subscriber::FlumeSubscriber; use zenoh::Session; use zenoh_core::{bail, AsyncResolve, Resolvable, Result as ZResult, SyncResolve}; +use zenoh_protocol::io::ZBufCodec; use zenoh_util::core::ResolveFuture; /// The builder of ReliabilityCache, allowing to configure it. @@ -109,6 +111,29 @@ impl<'a> AsyncResolve for ReliabilityCacheBuilder<'a, '_, '_> { } } +fn decode_range(range: &str) -> (Option, Option) { + let mut split = range.split(".."); + let start = split.next().and_then(|s| s.parse::().ok()); + let end = split.next().map(|s| s.parse::().ok()).unwrap_or(start); + (start, end) +} + +fn sample_in_range(sample: &Sample, start: Option, end: Option) -> bool { + if start.is_none() && end.is_none() { + true + } else { + let mut buf = sample.value.payload.reader(); + let _id = buf.read_zid().unwrap(); //TODO + let seq_num = buf.read_zint().unwrap(); //TODO + match (start, end) { + (Some(start), Some(end)) => seq_num >= start && seq_num <= end, + (Some(start), None) => seq_num >= start, + (None, Some(end)) => seq_num <= end, + (None, None) => true, + } + } +} + pub struct ReliabilityCache<'a> { _sub: FlumeSubscriber<'a>, _queryable: Queryable<'a, flume::Receiver>, @@ -192,11 +217,14 @@ impl<'a> ReliabilityCache<'a> { // on query, reply with cach content query = quer_recv.recv_async() => { if let Ok(query) = query { + let (start, end) = query.selector().parameters_cowmap().ok().and_then(|map| map.get("_sn").map(|range|decode_range(range))).unwrap_or((None, None)); if !query.selector().key_expr.as_str().contains('*') { if let Some(queue) = cache.get(query.selector().key_expr.as_keyexpr()) { for sample in queue { - if let Err(e) = query.reply(Ok(sample.clone())).res_async().await { - log::warn!("Error replying to query: {}", e); + if sample_in_range(sample, start, end) { + if let Err(e) = query.reply(Ok(sample.clone())).res_async().await { + log::warn!("Error replying to query: {}", e); + } } } } @@ -204,8 +232,10 @@ impl<'a> ReliabilityCache<'a> { for (key_expr, queue) in cache.iter() { if query.selector().key_expr.intersects(unsafe{ keyexpr::from_str_unchecked(key_expr) }) { for sample in queue { - if let Err(e) = query.reply(Ok(sample.clone())).res_async().await { - log::warn!("Error replying to query: {}", e); + if sample_in_range(sample, start, end) { + if let Err(e) = query.reply(Ok(sample.clone())).res_async().await { + log::warn!("Error replying to query: {}", e); + } } } } diff --git a/zenoh-ext/src/reliable_subscriber.rs b/zenoh-ext/src/reliable_subscriber.rs index 301fe5d6de..94516240d5 100644 --- a/zenoh-ext/src/reliable_subscriber.rs +++ b/zenoh-ext/src/reliable_subscriber.rs @@ -305,6 +305,15 @@ fn handle_sample( id } +fn seq_num_range(start: Option, end: Option) -> String { + match (start, end) { + (Some(start), Some(end)) => format!("_sn={}..{}", start, end), + (Some(start), None) => format!("_sn={}..", start), + (None, Some(end)) => format!("_sn=..{}", end), + (None, None) => "_sn=..".to_string(), + } +} + impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { fn new(conf: ReliableSubscriberBuilder<'a, Handler>) -> ZResult where @@ -326,15 +335,17 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { if let Some(state) = states.get_mut(&id) { if state.pending_queries == 0 && !state.pending_samples.is_empty() { state.pending_queries += 1; - drop(states); let key_expr = (&id.into_keyexpr()) / &key_expr; + let seq_num_range = + seq_num_range(Some(state.last_seq_num.unwrap() + 1), None); + drop(states); let handler = RepliesHandler { id, statesref: statesref.clone(), callback: callback.clone(), }; let _ = session - .get(key_expr) + .get(Selector::from(key_expr).with_parameters(&seq_num_range)) .callback({ move |r: Reply| { if let Ok(s) = r.sample { From bfa393a980d0a23621273a5503c70cf1ca781ef1 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Mon, 24 Oct 2022 16:12:03 +0200 Subject: [PATCH 03/16] Cleanup --- zenoh-ext/src/reliable_subscriber.rs | 49 +++------------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/zenoh-ext/src/reliable_subscriber.rs b/zenoh-ext/src/reliable_subscriber.rs index 94516240d5..a2eca90987 100644 --- a/zenoh-ext/src/reliable_subscriber.rs +++ b/zenoh-ext/src/reliable_subscriber.rs @@ -12,7 +12,6 @@ // ZettaScale Zenoh Team, // use std::collections::HashMap; -use std::convert::TryInto; use std::future::Ready; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -20,7 +19,7 @@ use zenoh::buffers::reader::{HasReader, Reader}; use zenoh::buffers::ZBuf; use zenoh::handlers::{locked, DefaultHandler}; use zenoh::prelude::r#async::*; -use zenoh::query::{QueryConsolidation, QueryTarget, Reply, ReplyKeyExpr}; +use zenoh::query::{QueryTarget, Reply, ReplyKeyExpr}; use zenoh::subscriber::{Reliability, Subscriber}; use zenoh::Result as ZResult; use zenoh_core::{zlock, AsyncResolve, Resolvable, SyncResolve}; @@ -32,9 +31,7 @@ pub struct ReliableSubscriberBuilder<'b, Handler> { key_expr: ZResult>, reliability: Reliability, origin: Locality, - query_selector: Option>>, query_target: QueryTarget, - query_consolidation: QueryConsolidation, query_timeout: Duration, handler: Handler, } @@ -44,21 +41,12 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { session: Arc, key_expr: ZResult>, ) -> ReliableSubscriberBuilder<'b, DefaultHandler> { - // By default query all matching publication caches and storages - let query_target = QueryTarget::All; - - // By default no query consolidation, to receive more than 1 sample per-resource - // (if history of publications is available) - let query_consolidation = QueryConsolidation::from(zenoh::query::ConsolidationMode::None); - ReliableSubscriberBuilder { session, key_expr, reliability: Reliability::default(), origin: Locality::default(), - query_selector: None, - query_target, - query_consolidation, + query_target: QueryTarget::BestMatching, query_timeout: Duration::from_secs(10), handler: DefaultHandler, } @@ -75,9 +63,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { key_expr, reliability, origin, - query_selector, query_target, - query_consolidation, query_timeout, handler: _, } = self; @@ -86,9 +72,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { key_expr, reliability, origin, - query_selector, query_target, - query_consolidation, query_timeout, handler: callback, } @@ -120,9 +104,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { key_expr, reliability, origin, - query_selector, query_target, - query_consolidation, query_timeout, handler: _, } = self; @@ -131,9 +113,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { key_expr, reliability, origin, - query_selector, query_target, - query_consolidation, query_timeout, handler, } @@ -170,17 +150,6 @@ impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { self } - /// Change the selector to be used for queries. - #[inline] - pub fn query_selector(mut self, query_selector: IntoSelector) -> Self - where - IntoSelector: TryInto>, - >>::Error: Into, - { - self.query_selector = Some(query_selector.try_into().map_err(Into::into)); - self - } - /// Change the target to be used for queries. #[inline] pub fn query_target(mut self, query_target: QueryTarget) -> Self { @@ -188,16 +157,6 @@ impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { self } - /// Change the consolidation mode to be used for queries. - #[inline] - pub fn query_consolidation>( - mut self, - query_consolidation: QC, - ) -> Self { - self.query_consolidation = query_consolidation.into(); - self - } - /// Change the timeout to be used for queries. #[inline] pub fn query_timeout(mut self, query_timeout: Duration) -> Self { @@ -211,9 +170,7 @@ impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { key_expr: self.key_expr.map(|s| s.into_owned()), reliability: self.reliability, origin: self.origin, - query_selector: self.query_selector.map(|s| s.map(|s| s.into_owned())), query_target: self.query_target, - query_consolidation: self.query_consolidation, query_timeout: self.query_timeout, handler: self.handler, } @@ -322,6 +279,7 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { let statesref = Arc::new(Mutex::new(HashMap::new())); let (callback, receiver) = conf.handler.into_cb_receiver_pair(); let key_expr = conf.key_expr?; + let query_target = conf.query_target; let query_timeout = conf.query_timeout; let sub_callback = { @@ -356,6 +314,7 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { }) .consolidation(ConsolidationMode::None) .accept_replies(ReplyKeyExpr::Any) + .target(query_target) .timeout(query_timeout) .res_sync(); } From ed3850d5f0387d2f8a2caf7c5c8596908299e1f9 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Mon, 24 Oct 2022 18:51:21 +0200 Subject: [PATCH 04/16] Add periodic queries option to ReliableSubscriber --- Cargo.lock | 2 + zenoh-ext/Cargo.toml | 2 + zenoh-ext/examples/z_rel_sub.rs | 13 +++- zenoh-ext/src/reliable_subscriber.rs | 93 ++++++++++++++++++++++++++-- 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index caaeaae48f..2e7f15b6c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3654,6 +3654,7 @@ name = "zenoh-ext" version = "0.6.0-beta.1" dependencies = [ "async-std", + "async-trait", "bincode", "clap", "env_logger", @@ -3662,6 +3663,7 @@ dependencies = [ "log", "serde", "zenoh", + "zenoh-collections", "zenoh-core", "zenoh-protocol", "zenoh-sync", diff --git a/zenoh-ext/Cargo.toml b/zenoh-ext/Cargo.toml index 7232fc5cd8..8676fbd1ee 100644 --- a/zenoh-ext/Cargo.toml +++ b/zenoh-ext/Cargo.toml @@ -35,6 +35,7 @@ async-std = { version = "=1.12.0", default-features = false, features = [ "attributes", "unstable", ] } +async-trait = "0.1.57" bincode = "1.3.3" env_logger = "0.9.1" flume = "0.10.14" @@ -42,6 +43,7 @@ futures = "0.3.24" log = "0.4.17" serde = "1.0.145" zenoh = { version = "0.6.0-beta.1", path = "../zenoh", default-features = false, features = ["unstable"] } +zenoh-collections = { version = "0.6.0-beta.1", path = "../commons/zenoh-collections/" } zenoh-core = { version = "0.6.0-beta.1", path = "../commons/zenoh-core/" } zenoh-protocol = { version = "0.6.0-beta.1", path = "../commons/zenoh-protocol" } zenoh-sync = { version = "0.6.0-beta.1", path = "../commons/zenoh-sync" } diff --git a/zenoh-ext/examples/z_rel_sub.rs b/zenoh-ext/examples/z_rel_sub.rs index 1f59980843..eaca53bbd7 100644 --- a/zenoh-ext/examples/z_rel_sub.rs +++ b/zenoh-ext/examples/z_rel_sub.rs @@ -26,7 +26,7 @@ async fn main() { // Initiate logging env_logger::init(); - let (config, key_expr) = parse_args(); + let (config, key_expr, period) = parse_args(); println!("Opening session..."); let session = zenoh::open(config).res().await.unwrap().into_arc(); @@ -34,6 +34,7 @@ async fn main() { println!("Declaring ReliableSubscriber on {}", key_expr); let subscriber = session .declare_reliable_subscriber(key_expr) + .periodic_queries(period) .res() .await .unwrap(); @@ -60,7 +61,7 @@ async fn main() { } } -fn parse_args() -> (Config, String) { +fn parse_args() -> (Config, String, Option) { let args = App::new("zenoh-ext reliable sub example") .arg( Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") @@ -76,6 +77,9 @@ fn parse_args() -> (Config, String) { Arg::from_usage("-k, --key=[KEYEXPR] 'The key expression to subscribe onto'") .default_value("demo/reliable/example/**"), ) + .arg(Arg::from_usage( + "-p, --period=[PERIOD]... 'Query for missing samples periodically with given period in seconds.'", + )) .arg(Arg::from_usage( "-c, --config=[FILE] 'A configuration file.'", )) @@ -109,6 +113,9 @@ fn parse_args() -> (Config, String) { } let key_expr = args.value_of("key").unwrap().to_string(); + let period = args + .value_of("period") + .map(|p| Duration::from_secs(p.parse::().unwrap())); - (config, key_expr) + (config, key_expr, period) } diff --git a/zenoh-ext/src/reliable_subscriber.rs b/zenoh-ext/src/reliable_subscriber.rs index a2eca90987..74b6d6f837 100644 --- a/zenoh-ext/src/reliable_subscriber.rs +++ b/zenoh-ext/src/reliable_subscriber.rs @@ -11,6 +11,8 @@ // Contributors: // ZettaScale Zenoh Team, // +use async_trait::async_trait; +use std::collections::hash_map::Entry; use std::collections::HashMap; use std::future::Ready; use std::sync::{Arc, Mutex}; @@ -22,6 +24,8 @@ use zenoh::prelude::r#async::*; use zenoh::query::{QueryTarget, Reply, ReplyKeyExpr}; use zenoh::subscriber::{Reliability, Subscriber}; use zenoh::Result as ZResult; +use zenoh_collections::timer::Timer; +use zenoh_collections::{Timed, TimedEvent}; use zenoh_core::{zlock, AsyncResolve, Resolvable, SyncResolve}; use zenoh_protocol::io::ZBufCodec; @@ -33,6 +37,7 @@ pub struct ReliableSubscriberBuilder<'b, Handler> { origin: Locality, query_target: QueryTarget, query_timeout: Duration, + period: Option, handler: Handler, } @@ -48,6 +53,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { origin: Locality::default(), query_target: QueryTarget::BestMatching, query_timeout: Duration::from_secs(10), + period: None, handler: DefaultHandler, } } @@ -65,6 +71,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { origin, query_target, query_timeout, + period, handler: _, } = self; ReliableSubscriberBuilder { @@ -74,6 +81,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { origin, query_target, query_timeout, + period, handler: callback, } } @@ -106,6 +114,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { origin, query_target, query_timeout, + period, handler: _, } = self; ReliableSubscriberBuilder { @@ -115,6 +124,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { origin, query_target, query_timeout, + period, handler, } } @@ -164,6 +174,13 @@ impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { self } + /// Enable periodic queries and specify queries period. + #[inline] + pub fn periodic_queries(mut self, period: Option) -> Self { + self.period = period; + self + } + fn with_static_keys(self) -> ReliableSubscriberBuilder<'static, Handler> { ReliableSubscriberBuilder { session: self.session, @@ -172,6 +189,7 @@ impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { origin: self.origin, query_target: self.query_target, query_timeout: self.query_timeout, + period: self.period, handler: self.handler, } } @@ -232,7 +250,7 @@ fn handle_sample( states: &mut HashMap, sample: Sample, callback: &Arc, -) -> ZenohId { +) -> (ZenohId, bool) { let mut buf = sample.value.payload.reader(); let id = buf.read_zid().unwrap(); //TODO let seq_num = buf.read_zint().unwrap(); //TODO @@ -240,7 +258,9 @@ fn handle_sample( buf.read_into_zbuf(&mut payload, buf.remaining()); let value = Value::new(payload).encoding(sample.encoding.clone()); let s = Sample::new(sample.key_expr, value); - let state = states.entry(id).or_insert(InnerState { + let entry = states.entry(id); + let new = matches!(&entry, Entry::Occupied(_)); + let state = entry.or_insert(InnerState { last_seq_num: None, pending_queries: 0, pending_samples: HashMap::new(), @@ -259,7 +279,7 @@ fn handle_sample( state.last_seq_num = Some(last_seq_num); } } - id + (id, new) } fn seq_num_range(start: Option, end: Option) -> String { @@ -271,6 +291,50 @@ fn seq_num_range(start: Option, end: Option) -> String { } } +struct PeriodicQuery { + id: ZenohId, + statesref: Arc>>, + key_expr: KeyExpr<'static>, + session: Arc, + query_target: QueryTarget, + query_timeout: Duration, + callback: Arc, +} + +#[async_trait] +impl Timed for PeriodicQuery { + async fn run(&mut self) { + let mut states = zlock!(self.statesref); + if let Some(state) = states.get_mut(&self.id) { + state.pending_queries += 1; + let key_expr = (&self.id.into_keyexpr()) / &self.key_expr; + let seq_num_range = seq_num_range(Some(state.last_seq_num.unwrap() + 1), None); + drop(states); + let handler = RepliesHandler { + id: self.id, + statesref: self.statesref.clone(), + callback: self.callback.clone(), + }; + let _ = self + .session + .get(Selector::from(key_expr).with_parameters(&seq_num_range)) + .callback({ + move |r: Reply| { + if let Ok(s) = r.sample { + let states = &mut zlock!(handler.statesref); + handle_sample(states, s, &handler.callback); + } + } + }) + .consolidation(ConsolidationMode::None) + .accept_replies(ReplyKeyExpr::Any) + .target(self.query_target) + .timeout(self.query_timeout) + .res_sync(); + } + } +} + impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { fn new(conf: ReliableSubscriberBuilder<'a, Handler>) -> ZResult where @@ -286,10 +350,31 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { let session = conf.session.clone(); let callback = callback.clone(); let key_expr = key_expr.clone().into_owned(); + let timing = conf + .period + .map(|period| (Arc::new(Timer::new(false)), period)); move |s: Sample| { let mut states = zlock!(statesref); - let id = handle_sample(&mut states, s, &callback); + let (id, new) = handle_sample(&mut states, s, &callback); + + if new { + if let Some((timer, period)) = timing.as_ref() { + timer.add(TimedEvent::periodic( + *period, + PeriodicQuery { + id, + statesref: statesref.clone(), + key_expr: key_expr.clone(), + session: session.clone(), + query_target, + query_timeout, + callback: callback.clone(), + }, + )) + } + } + if let Some(state) = states.get_mut(&id) { if state.pending_queries == 0 && !state.pending_samples.is_empty() { state.pending_queries += 1; From 84ea3a0406021f09fbd429035f954901c3d37e5b Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Tue, 25 Oct 2022 17:41:25 +0200 Subject: [PATCH 05/16] ReliableSubscriber can optionally retrieve historical data --- zenoh-ext/examples/z_rel_sub.rs | 11 +- zenoh-ext/src/reliable_subscriber.rs | 150 +++++++++++++++++++++------ 2 files changed, 127 insertions(+), 34 deletions(-) diff --git a/zenoh-ext/examples/z_rel_sub.rs b/zenoh-ext/examples/z_rel_sub.rs index eaca53bbd7..497a3f71d7 100644 --- a/zenoh-ext/examples/z_rel_sub.rs +++ b/zenoh-ext/examples/z_rel_sub.rs @@ -26,7 +26,7 @@ async fn main() { // Initiate logging env_logger::init(); - let (config, key_expr, period) = parse_args(); + let (config, key_expr, history, period) = parse_args(); println!("Opening session..."); let session = zenoh::open(config).res().await.unwrap().into_arc(); @@ -34,6 +34,7 @@ async fn main() { println!("Declaring ReliableSubscriber on {}", key_expr); let subscriber = session .declare_reliable_subscriber(key_expr) + .history(history) .periodic_queries(period) .res() .await @@ -61,7 +62,7 @@ async fn main() { } } -fn parse_args() -> (Config, String, Option) { +fn parse_args() -> (Config, String, bool, Option) { let args = App::new("zenoh-ext reliable sub example") .arg( Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") @@ -77,6 +78,9 @@ fn parse_args() -> (Config, String, Option) { Arg::from_usage("-k, --key=[KEYEXPR] 'The key expression to subscribe onto'") .default_value("demo/reliable/example/**"), ) + .arg(Arg::from_usage( + "-h, --history 'Query for historical samples at startup.'", + )) .arg(Arg::from_usage( "-p, --period=[PERIOD]... 'Query for missing samples periodically with given period in seconds.'", )) @@ -116,6 +120,7 @@ fn parse_args() -> (Config, String, Option) { let period = args .value_of("period") .map(|p| Duration::from_secs(p.parse::().unwrap())); + let history = args.is_present("history"); - (config, key_expr, period) + (config, key_expr, history, period) } diff --git a/zenoh-ext/src/reliable_subscriber.rs b/zenoh-ext/src/reliable_subscriber.rs index 74b6d6f837..15dc93fe3d 100644 --- a/zenoh-ext/src/reliable_subscriber.rs +++ b/zenoh-ext/src/reliable_subscriber.rs @@ -14,6 +14,7 @@ use async_trait::async_trait; use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::convert::TryFrom; use std::future::Ready; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -38,6 +39,7 @@ pub struct ReliableSubscriberBuilder<'b, Handler> { query_target: QueryTarget, query_timeout: Duration, period: Option, + history: bool, handler: Handler, } @@ -54,6 +56,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { query_target: QueryTarget::BestMatching, query_timeout: Duration::from_secs(10), period: None, + history: false, handler: DefaultHandler, } } @@ -72,6 +75,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { query_target, query_timeout, period, + history, handler: _, } = self; ReliableSubscriberBuilder { @@ -82,6 +86,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { query_target, query_timeout, period, + history, handler: callback, } } @@ -115,6 +120,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { query_target, query_timeout, period, + history, handler: _, } = self; ReliableSubscriberBuilder { @@ -125,6 +131,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { query_target, query_timeout, period, + history, handler, } } @@ -181,6 +188,13 @@ impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { self } + /// Enable/Disable query for historical data. + #[inline] + pub fn history(mut self, history: bool) -> Self { + self.history = history; + self + } + fn with_static_keys(self) -> ReliableSubscriberBuilder<'static, Handler> { ReliableSubscriberBuilder { session: self.session, @@ -190,6 +204,7 @@ impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { query_target: self.query_target, query_timeout: self.query_timeout, period: self.period, + history: self.history, handler: self.handler, } } @@ -248,6 +263,7 @@ impl std::ops::DerefMut for ReliableSubscriber<'_, Receiver> { fn handle_sample( states: &mut HashMap, + wait: bool, sample: Sample, callback: &Arc, ) -> (ZenohId, bool) { @@ -265,7 +281,9 @@ fn handle_sample( pending_queries: 0, pending_samples: HashMap::new(), }); - if state.last_seq_num.is_some() && seq_num != state.last_seq_num.unwrap() + 1 { + if wait { + state.pending_samples.insert(seq_num, s); + } else if state.last_seq_num.is_some() && seq_num != state.last_seq_num.unwrap() + 1 { if seq_num > state.last_seq_num.unwrap() { state.pending_samples.insert(seq_num, s); } @@ -291,9 +309,10 @@ fn seq_num_range(start: Option, end: Option) -> String { } } +#[derive(Clone)] struct PeriodicQuery { id: ZenohId, - statesref: Arc>>, + statesref: Arc, bool)>>, key_expr: KeyExpr<'static>, session: Arc, query_target: QueryTarget, @@ -301,15 +320,23 @@ struct PeriodicQuery { callback: Arc, } +impl PeriodicQuery { + fn with_id(mut self, id: ZenohId) -> Self { + self.id = id; + self + } +} + #[async_trait] impl Timed for PeriodicQuery { async fn run(&mut self) { - let mut states = zlock!(self.statesref); + let mut lock = zlock!(self.statesref); + let (states, _wait) = &mut *lock; if let Some(state) = states.get_mut(&self.id) { state.pending_queries += 1; let key_expr = (&self.id.into_keyexpr()) / &self.key_expr; let seq_num_range = seq_num_range(Some(state.last_seq_num.unwrap() + 1), None); - drop(states); + drop(lock); let handler = RepliesHandler { id: self.id, statesref: self.statesref.clone(), @@ -321,8 +348,8 @@ impl Timed for PeriodicQuery { .callback({ move |r: Reply| { if let Ok(s) = r.sample { - let states = &mut zlock!(handler.statesref); - handle_sample(states, s, &handler.callback); + let (ref mut states, wait) = &mut *zlock!(handler.statesref); + handle_sample(states, *wait, s, &handler.callback); } } }) @@ -340,38 +367,43 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { where Handler: IntoCallbackReceiverPair<'static, Sample, Receiver = Receiver> + Send, { - let statesref = Arc::new(Mutex::new(HashMap::new())); + let statesref = Arc::new(Mutex::new((HashMap::new(), conf.history))); let (callback, receiver) = conf.handler.into_cb_receiver_pair(); let key_expr = conf.key_expr?; let query_target = conf.query_target; let query_timeout = conf.query_timeout; + let session = conf.session.clone(); + let periodic_query = conf.period.map(|period| { + ( + Arc::new(Timer::new(false)), + period, + PeriodicQuery { + id: ZenohId::try_from([1]).unwrap(), + statesref: statesref.clone(), + key_expr: key_expr.clone().into_owned(), + session, + query_target, + query_timeout, + callback: callback.clone(), + }, + ) + }); let sub_callback = { + let statesref = statesref.clone(); let session = conf.session.clone(); let callback = callback.clone(); let key_expr = key_expr.clone().into_owned(); - let timing = conf - .period - .map(|period| (Arc::new(Timer::new(false)), period)); + let periodic_query = periodic_query.clone(); move |s: Sample| { - let mut states = zlock!(statesref); - let (id, new) = handle_sample(&mut states, s, &callback); + let mut lock = zlock!(statesref); + let (states, wait) = &mut *lock; + let (id, new) = handle_sample(states, *wait, s, &callback); if new { - if let Some((timer, period)) = timing.as_ref() { - timer.add(TimedEvent::periodic( - *period, - PeriodicQuery { - id, - statesref: statesref.clone(), - key_expr: key_expr.clone(), - session: session.clone(), - query_target, - query_timeout, - callback: callback.clone(), - }, - )) + if let Some((timer, period, query)) = periodic_query.as_ref() { + timer.add(TimedEvent::periodic(*period, query.clone().with_id(id))) } } @@ -381,7 +413,7 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { let key_expr = (&id.into_keyexpr()) / &key_expr; let seq_num_range = seq_num_range(Some(state.last_seq_num.unwrap() + 1), None); - drop(states); + drop(lock); let handler = RepliesHandler { id, statesref: statesref.clone(), @@ -392,8 +424,9 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { .callback({ move |r: Reply| { if let Ok(s) = r.sample { - let states = &mut zlock!(handler.statesref); - handle_sample(states, s, &handler.callback); + let (ref mut states, wait) = + &mut *zlock!(handler.statesref); + handle_sample(states, *wait, s, &handler.callback); } } }) @@ -415,6 +448,33 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { .allowed_origin(conf.origin) .res_sync()?; + if conf.history { + let handler = InitialRepliesHandler { + statesref, + periodic_query, + callback, + }; + let _ = conf + .session + .get( + Selector::from(KeyExpr::try_from("*").unwrap() / &key_expr) + .with_parameters("0.."), + ) + .callback({ + move |r: Reply| { + if let Ok(s) = r.sample { + let (ref mut states, wait) = &mut *zlock!(handler.statesref); + handle_sample(states, *wait, s, &handler.callback); + } + } + }) + .consolidation(ConsolidationMode::None) + .accept_replies(ReplyKeyExpr::Any) + .target(query_target) + .timeout(query_timeout) + .res_sync(); + } + let reliable_subscriber = ReliableSubscriber { _subscriber: subscriber, receiver, @@ -430,19 +490,47 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { } } +#[derive(Clone)] +struct InitialRepliesHandler { + statesref: Arc, bool)>>, + periodic_query: Option<(Arc, Duration, PeriodicQuery)>, + callback: Arc, +} + +impl Drop for InitialRepliesHandler { + fn drop(&mut self) { + let (states, wait) = &mut *zlock!(self.statesref); + for (id, state) in states.iter_mut() { + let mut pending_samples = state + .pending_samples + .drain() + .collect::>(); + pending_samples.sort_by_key(|(k, _s)| *k); + for (seq_num, sample) in pending_samples { + state.last_seq_num = Some(seq_num); + (self.callback)(sample); + } + if let Some((timer, period, query)) = self.periodic_query.as_ref() { + timer.add(TimedEvent::periodic(*period, query.clone().with_id(*id))) + } + } + *wait = false; + } +} + #[derive(Clone)] struct RepliesHandler { id: ZenohId, - statesref: Arc>>, + statesref: Arc, bool)>>, callback: Arc, } impl Drop for RepliesHandler { fn drop(&mut self) { - let mut states = zlock!(self.statesref); + let (states, wait) = &mut *zlock!(self.statesref); if let Some(state) = states.get_mut(&self.id) { state.pending_queries -= 1; - if !state.pending_samples.is_empty() { + if !state.pending_samples.is_empty() && !*wait { log::error!("Sample missed: unable to retrieve some missing samples."); let mut pending_samples = state .pending_samples From 38555f67d92826235411c85802822d1e29ff36fe Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Thu, 27 Oct 2022 12:38:41 +0200 Subject: [PATCH 06/16] Mark end-to-end reliability related stuff as unstable --- zenoh-ext/Cargo.toml | 3 ++ zenoh-ext/src/lib.rs | 3 ++ zenoh-ext/src/reliability_cache.rs | 42 ++++++++++++------- zenoh-ext/src/reliable_publisher.rs | 29 +++++++++---- zenoh-ext/src/reliable_subscriber.rs | 63 ++++++++++++++++++++-------- zenoh-ext/src/session_ext.rs | 15 +++++-- 6 files changed, 109 insertions(+), 46 deletions(-) diff --git a/zenoh-ext/Cargo.toml b/zenoh-ext/Cargo.toml index 8676fbd1ee..4d2f80077c 100644 --- a/zenoh-ext/Cargo.toml +++ b/zenoh-ext/Cargo.toml @@ -72,11 +72,14 @@ path = "examples/z_view_size.rs" [[example]] name = "z_rel_sub" path = "examples/z_rel_sub.rs" +required-features = ["unstable"] [[example]] name = "z_rel_pub" path = "examples/z_rel_pub.rs" +required-features = ["unstable"] [[example]] name = "z_rel_cache" path = "examples/z_rel_cache.rs" +required-features = ["unstable"] diff --git a/zenoh-ext/src/lib.rs b/zenoh-ext/src/lib.rs index bae1bab993..1d131aa173 100644 --- a/zenoh-ext/src/lib.rs +++ b/zenoh-ext/src/lib.rs @@ -21,8 +21,11 @@ mod session_ext; mod subscriber_ext; pub use publication_cache::{PublicationCache, PublicationCacheBuilder}; pub use querying_subscriber::{QueryingSubscriber, QueryingSubscriberBuilder}; +#[zenoh_core::unstable] pub use reliability_cache::{ReliabilityCache, ReliabilityCacheBuilder}; +#[zenoh_core::unstable] pub use reliable_publisher::{ReliablePublisher, ReliablePublisherBuilder}; +#[zenoh_core::unstable] pub use reliable_subscriber::{ReliableSubscriber, ReliableSubscriberBuilder}; pub use session_ext::{ArcSessionExt, SessionExt}; pub use subscriber_ext::SubscriberForward; diff --git a/zenoh-ext/src/reliability_cache.rs b/zenoh-ext/src/reliability_cache.rs index e0e619fe0e..ff79f16e4e 100644 --- a/zenoh-ext/src/reliability_cache.rs +++ b/zenoh-ext/src/reliability_cache.rs @@ -11,23 +11,27 @@ // Contributors: // ZettaScale Zenoh Team, // -use async_std::channel::{bounded, Sender}; -use async_std::task; -use futures::select; -use futures::{FutureExt, StreamExt}; -use std::collections::{HashMap, VecDeque}; -use std::convert::TryInto; -use std::future::Ready; -use zenoh::buffers::reader::HasReader; -use zenoh::prelude::r#async::*; -use zenoh::queryable::{Query, Queryable}; -use zenoh::subscriber::FlumeSubscriber; -use zenoh::Session; -use zenoh_core::{bail, AsyncResolve, Resolvable, Result as ZResult, SyncResolve}; -use zenoh_protocol::io::ZBufCodec; -use zenoh_util::core::ResolveFuture; +#[zenoh_core::unstable] +use { + async_std::channel::{bounded, Sender}, + async_std::task, + futures::select, + futures::{FutureExt, StreamExt}, + std::collections::{HashMap, VecDeque}, + std::convert::TryInto, + std::future::Ready, + zenoh::buffers::reader::HasReader, + zenoh::prelude::r#async::*, + zenoh::queryable::{Query, Queryable}, + zenoh::subscriber::FlumeSubscriber, + zenoh::Session, + zenoh_core::{bail, AsyncResolve, Resolvable, Result as ZResult, SyncResolve}, + zenoh_protocol::io::ZBufCodec, + zenoh_util::core::ResolveFuture, +}; /// The builder of ReliabilityCache, allowing to configure it. +#[zenoh_core::unstable] pub struct ReliabilityCacheBuilder<'a, 'b, 'c> { session: &'a Session, pub_key_expr: ZResult>, @@ -38,6 +42,7 @@ pub struct ReliabilityCacheBuilder<'a, 'b, 'c> { resources_limit: Option, } +#[zenoh_core::unstable] impl<'a, 'b, 'c> ReliabilityCacheBuilder<'a, 'b, 'c> { pub(crate) fn new( session: &'a Session, @@ -93,16 +98,19 @@ impl<'a, 'b, 'c> ReliabilityCacheBuilder<'a, 'b, 'c> { } } +#[zenoh_core::unstable] impl<'a> Resolvable for ReliabilityCacheBuilder<'a, '_, '_> { type To = ZResult>; } +#[zenoh_core::unstable] impl SyncResolve for ReliabilityCacheBuilder<'_, '_, '_> { fn res_sync(self) -> ::To { ReliabilityCache::new(self) } } +#[zenoh_core::unstable] impl<'a> AsyncResolve for ReliabilityCacheBuilder<'a, '_, '_> { type Future = Ready; @@ -111,6 +119,7 @@ impl<'a> AsyncResolve for ReliabilityCacheBuilder<'a, '_, '_> { } } +#[zenoh_core::unstable] fn decode_range(range: &str) -> (Option, Option) { let mut split = range.split(".."); let start = split.next().and_then(|s| s.parse::().ok()); @@ -118,6 +127,7 @@ fn decode_range(range: &str) -> (Option, Option) { (start, end) } +#[zenoh_core::unstable] fn sample_in_range(sample: &Sample, start: Option, end: Option) -> bool { if start.is_none() && end.is_none() { true @@ -134,12 +144,14 @@ fn sample_in_range(sample: &Sample, start: Option, end: Option) -> b } } +#[zenoh_core::unstable] pub struct ReliabilityCache<'a> { _sub: FlumeSubscriber<'a>, _queryable: Queryable<'a, flume::Receiver>, _stoptx: Sender, } +#[zenoh_core::unstable] impl<'a> ReliabilityCache<'a> { fn new(conf: ReliabilityCacheBuilder<'a, '_, '_>) -> ZResult> { let key_expr = conf.pub_key_expr?; diff --git a/zenoh-ext/src/reliable_publisher.rs b/zenoh-ext/src/reliable_publisher.rs index 585644095b..72956521ae 100644 --- a/zenoh-ext/src/reliable_publisher.rs +++ b/zenoh-ext/src/reliable_publisher.rs @@ -11,18 +11,23 @@ // Contributors: // ZettaScale Zenoh Team, // -use std::future::Ready; -use std::sync::atomic::{AtomicU64, Ordering}; -use zenoh::buffers::WBuf; -use zenoh::prelude::r#async::*; -use zenoh::publication::{Publication, Publisher}; -use zenoh_core::{AsyncResolve, Resolvable, Result as ZResult, SyncResolve}; -use zenoh_protocol::io::WBufCodec; -use zenoh_util::core::ResolveFuture; - +#[zenoh_core::unstable] +use { + std::future::Ready, + std::sync::atomic::{AtomicU64, Ordering}, + zenoh::buffers::WBuf, + zenoh::prelude::r#async::*, + zenoh::publication::{Publication, Publisher}, + zenoh_core::{AsyncResolve, Resolvable, Result as ZResult, SyncResolve}, + zenoh_protocol::io::WBufCodec, + zenoh_util::core::ResolveFuture, +}; + +#[zenoh_core::unstable] use crate::{ReliabilityCache, SessionExt}; /// The builder of ReliablePublisher, allowing to configure it. +#[zenoh_core::unstable] pub struct ReliablePublisherBuilder<'a, 'b> { session: &'a Session, pub_key_expr: ZResult>, @@ -31,6 +36,7 @@ pub struct ReliablePublisherBuilder<'a, 'b> { resources_limit: Option, } +#[zenoh_core::unstable] impl<'a, 'b> ReliablePublisherBuilder<'a, 'b> { pub(crate) fn new( session: &'a Session, @@ -64,16 +70,19 @@ impl<'a, 'b> ReliablePublisherBuilder<'a, 'b> { } } +#[zenoh_core::unstable] impl<'a> Resolvable for ReliablePublisherBuilder<'a, '_> { type To = ZResult>; } +#[zenoh_core::unstable] impl SyncResolve for ReliablePublisherBuilder<'_, '_> { fn res_sync(self) -> ::To { ReliablePublisher::new(self) } } +#[zenoh_core::unstable] impl<'a> AsyncResolve for ReliablePublisherBuilder<'a, '_> { type Future = Ready; @@ -82,6 +91,7 @@ impl<'a> AsyncResolve for ReliablePublisherBuilder<'a, '_> { } } +#[zenoh_core::unstable] pub struct ReliablePublisher<'a> { _id: ZenohId, _seqnum: AtomicU64, @@ -89,6 +99,7 @@ pub struct ReliablePublisher<'a> { _cache: Option>, } +#[zenoh_core::unstable] impl<'a> ReliablePublisher<'a> { fn new(conf: ReliablePublisherBuilder<'a, '_>) -> ZResult { let key_expr = conf.pub_key_expr?; diff --git a/zenoh-ext/src/reliable_subscriber.rs b/zenoh-ext/src/reliable_subscriber.rs index 15dc93fe3d..083307467c 100644 --- a/zenoh-ext/src/reliable_subscriber.rs +++ b/zenoh-ext/src/reliable_subscriber.rs @@ -11,26 +11,30 @@ // Contributors: // ZettaScale Zenoh Team, // -use async_trait::async_trait; -use std::collections::hash_map::Entry; -use std::collections::HashMap; -use std::convert::TryFrom; -use std::future::Ready; -use std::sync::{Arc, Mutex}; -use std::time::Duration; -use zenoh::buffers::reader::{HasReader, Reader}; -use zenoh::buffers::ZBuf; -use zenoh::handlers::{locked, DefaultHandler}; -use zenoh::prelude::r#async::*; -use zenoh::query::{QueryTarget, Reply, ReplyKeyExpr}; -use zenoh::subscriber::{Reliability, Subscriber}; -use zenoh::Result as ZResult; -use zenoh_collections::timer::Timer; -use zenoh_collections::{Timed, TimedEvent}; -use zenoh_core::{zlock, AsyncResolve, Resolvable, SyncResolve}; -use zenoh_protocol::io::ZBufCodec; +#[zenoh_core::unstable] +use { + async_trait::async_trait, + std::collections::hash_map::Entry, + std::collections::HashMap, + std::convert::TryFrom, + std::future::Ready, + std::sync::{Arc, Mutex}, + std::time::Duration, + zenoh::buffers::reader::{HasReader, Reader}, + zenoh::buffers::ZBuf, + zenoh::handlers::{locked, DefaultHandler}, + zenoh::prelude::r#async::*, + zenoh::query::{QueryTarget, Reply, ReplyKeyExpr}, + zenoh::subscriber::{Reliability, Subscriber}, + zenoh::Result as ZResult, + zenoh_collections::timer::Timer, + zenoh_collections::{Timed, TimedEvent}, + zenoh_core::{zlock, AsyncResolve, Resolvable, SyncResolve}, + zenoh_protocol::io::ZBufCodec, +}; /// The builder of ReliableSubscriber, allowing to configure it. +#[zenoh_core::unstable] pub struct ReliableSubscriberBuilder<'b, Handler> { session: Arc, key_expr: ZResult>, @@ -43,6 +47,7 @@ pub struct ReliableSubscriberBuilder<'b, Handler> { handler: Handler, } +#[zenoh_core::unstable] impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { pub(crate) fn new( session: Arc, @@ -136,6 +141,8 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { } } } + +#[zenoh_core::unstable] impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { /// Change the subscription reliability. #[inline] @@ -210,6 +217,7 @@ impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { } } +#[zenoh_core::unstable] impl<'a, Handler> Resolvable for ReliableSubscriberBuilder<'a, Handler> where Handler: IntoCallbackReceiverPair<'static, Sample>, @@ -218,6 +226,7 @@ where type To = ZResult>; } +#[zenoh_core::unstable] impl SyncResolve for ReliableSubscriberBuilder<'_, Handler> where Handler: IntoCallbackReceiverPair<'static, Sample> + Send, @@ -228,6 +237,7 @@ where } } +#[zenoh_core::unstable] impl AsyncResolve for ReliableSubscriberBuilder<'_, Handler> where Handler: IntoCallbackReceiverPair<'static, Sample> + Send, @@ -239,28 +249,36 @@ where std::future::ready(self.res_sync()) } } + +#[zenoh_core::unstable] struct InnerState { last_seq_num: Option, pending_queries: u64, pending_samples: HashMap, } +#[zenoh_core::unstable] pub struct ReliableSubscriber<'a, Receiver> { _subscriber: Subscriber<'a, ()>, receiver: Receiver, } + +#[zenoh_core::unstable] impl std::ops::Deref for ReliableSubscriber<'_, Receiver> { type Target = Receiver; fn deref(&self) -> &Self::Target { &self.receiver } } + +#[zenoh_core::unstable] impl std::ops::DerefMut for ReliableSubscriber<'_, Receiver> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.receiver } } +#[zenoh_core::unstable] fn handle_sample( states: &mut HashMap, wait: bool, @@ -300,6 +318,7 @@ fn handle_sample( (id, new) } +#[zenoh_core::unstable] fn seq_num_range(start: Option, end: Option) -> String { match (start, end) { (Some(start), Some(end)) => format!("_sn={}..{}", start, end), @@ -309,6 +328,7 @@ fn seq_num_range(start: Option, end: Option) -> String { } } +#[zenoh_core::unstable] #[derive(Clone)] struct PeriodicQuery { id: ZenohId, @@ -320,6 +340,7 @@ struct PeriodicQuery { callback: Arc, } +#[zenoh_core::unstable] impl PeriodicQuery { fn with_id(mut self, id: ZenohId) -> Self { self.id = id; @@ -327,6 +348,7 @@ impl PeriodicQuery { } } +#[zenoh_core::unstable] #[async_trait] impl Timed for PeriodicQuery { async fn run(&mut self) { @@ -362,6 +384,7 @@ impl Timed for PeriodicQuery { } } +#[zenoh_core::unstable] impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { fn new(conf: ReliableSubscriberBuilder<'a, Handler>) -> ZResult where @@ -490,6 +513,7 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { } } +#[zenoh_core::unstable] #[derive(Clone)] struct InitialRepliesHandler { statesref: Arc, bool)>>, @@ -497,6 +521,7 @@ struct InitialRepliesHandler { callback: Arc, } +#[zenoh_core::unstable] impl Drop for InitialRepliesHandler { fn drop(&mut self) { let (states, wait) = &mut *zlock!(self.statesref); @@ -518,6 +543,7 @@ impl Drop for InitialRepliesHandler { } } +#[zenoh_core::unstable] #[derive(Clone)] struct RepliesHandler { id: ZenohId, @@ -525,6 +551,7 @@ struct RepliesHandler { callback: Arc, } +#[zenoh_core::unstable] impl Drop for RepliesHandler { fn drop(&mut self) { let (states, wait) = &mut *zlock!(self.statesref); diff --git a/zenoh-ext/src/session_ext.rs b/zenoh-ext/src/session_ext.rs index 697c404557..a975cb56ed 100644 --- a/zenoh-ext/src/session_ext.rs +++ b/zenoh-ext/src/session_ext.rs @@ -11,10 +11,9 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::{ - PublicationCacheBuilder, QueryingSubscriberBuilder, ReliabilityCacheBuilder, - ReliablePublisherBuilder, ReliableSubscriberBuilder, -}; +use super::{PublicationCacheBuilder, QueryingSubscriberBuilder}; +#[zenoh_core::unstable] +use super::{ReliabilityCacheBuilder, ReliablePublisherBuilder, ReliableSubscriberBuilder}; use std::convert::TryInto; use std::fmt; use std::ops::Deref; @@ -97,6 +96,7 @@ pub trait SessionExt { TryIntoKeyExpr: TryInto>, >>::Error: Into; + #[zenoh_core::unstable] fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, @@ -105,6 +105,7 @@ pub trait SessionExt { TryIntoKeyExpr: TryInto>, >>::Error: Into; + #[zenoh_core::unstable] fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, @@ -140,6 +141,7 @@ impl SessionExt for Session { PublicationCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } + #[zenoh_core::unstable] fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, @@ -151,6 +153,7 @@ impl SessionExt for Session { ReliabilityCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } + #[zenoh_core::unstable] fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, @@ -163,6 +166,7 @@ impl SessionExt for Session { } } pub trait ArcSessionExt { + #[zenoh_core::unstable] fn declare_reliable_subscriber<'b, TryIntoKeyExpr>( &self, sub_key_expr: TryIntoKeyExpr, @@ -198,6 +202,7 @@ impl SessionExt for Arc { PublicationCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } + #[zenoh_core::unstable] fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, @@ -209,6 +214,7 @@ impl SessionExt for Arc { ReliabilityCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } + #[zenoh_core::unstable] fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, @@ -222,6 +228,7 @@ impl SessionExt for Arc { } impl ArcSessionExt for Arc { + #[zenoh_core::unstable] fn declare_reliable_subscriber<'b, TryIntoKeyExpr>( &self, sub_key_expr: TryIntoKeyExpr, From f3d0f289be57c6cca8754baef8514c0670efcd55 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Mon, 7 Nov 2022 15:23:00 +0100 Subject: [PATCH 07/16] Reintroduce SourceInfo without first_router_id/sn --- .../zenoh-protocol/benches/data_creation.rs | 6 +- commons/zenoh-protocol/src/proto/msg.rs | 14 +++- .../zenoh-protocol/src/proto/msg_reader.rs | 13 ++-- .../zenoh-protocol/src/proto/msg_writer.rs | 6 ++ commons/zenoh-protocol/tests/msg_codec.rs | 2 + zenoh/src/sample.rs | 74 +++++++++++++++++++ 6 files changed, 104 insertions(+), 11 deletions(-) diff --git a/commons/zenoh-protocol/benches/data_creation.rs b/commons/zenoh-protocol/benches/data_creation.rs index 1e86fae97a..0e4df44555 100644 --- a/commons/zenoh-protocol/benches/data_creation.rs +++ b/commons/zenoh-protocol/benches/data_creation.rs @@ -17,7 +17,7 @@ extern crate criterion; use criterion::Criterion; use std::convert::TryFrom; use std::sync::Arc; -use zenoh_protocol_core::SampleKind; +use zenoh_protocol_core::{SampleKind, ZenohId}; use zenoh_protocol::core::Encoding; use zenoh_protocol::core::{Channel, CongestionControl, WireExpr}; @@ -50,6 +50,8 @@ fn criterion_benchmark(c: &mut Criterion) { Default::default(), uhlc::ID::try_from([2u8; uhlc::ID::MAX_SIZE]).unwrap(), )), + source_id: Some(ZenohId::try_from([1_u8; ZenohId::MAX_SIZE]).unwrap()), + source_sn: Some(12345), }); let msg = ZenohMessage::make_data( @@ -99,6 +101,8 @@ fn criterion_benchmark(c: &mut Criterion) { Default::default(), uhlc::ID::try_from([1_u8; uhlc::ID::MAX_SIZE]).unwrap(), )), + source_id: Some(ZenohId::try_from([1_u8; ZenohId::MAX_SIZE]).unwrap()), + source_sn: Some(12345), }); let payload = ZBuf::from(vec![0; 1024]); let channel = Channel::default(); diff --git a/commons/zenoh-protocol/src/proto/msg.rs b/commons/zenoh-protocol/src/proto/msg.rs index 4218a3672b..5b1cb771d2 100644 --- a/commons/zenoh-protocol/src/proto/msg.rs +++ b/commons/zenoh-protocol/src/proto/msg.rs @@ -527,10 +527,6 @@ impl Header for Priority { /// +---------------+ /// ~ source_sn ~ if options & (1 << 8) /// +---------------+ -/// ~first_router_id~ if options & (1 << 9) -/// +---------------+ -/// ~first_router_sn~ if options & (1 << 10) -/// +---------------+ /// /// - if options & (1 << 5) then the payload is sliced /// @@ -542,6 +538,8 @@ pub struct DataInfo { pub kind: SampleKind, pub encoding: Option, pub timestamp: Option, + pub source_id: Option, + pub source_sn: Option, } impl DataInfo { @@ -566,6 +564,12 @@ impl Options for DataInfo { if self.timestamp.is_some() { options |= zmsg::data::info::TIMESTAMP; } + if self.source_id.is_some() { + options |= zmsg::data::info::SRCID; + } + if self.source_sn.is_some() { + options |= zmsg::data::info::SRCSN; + } options } @@ -587,6 +591,8 @@ impl Options for DataInfo { || self.kind != SampleKind::Put || self.encoding.is_some() || self.timestamp.is_some() + || self.source_id.is_some() + || self.source_sn.is_some() } } diff --git a/commons/zenoh-protocol/src/proto/msg_reader.rs b/commons/zenoh-protocol/src/proto/msg_reader.rs index 272fe28284..be0d5a978d 100644 --- a/commons/zenoh-protocol/src/proto/msg_reader.rs +++ b/commons/zenoh-protocol/src/proto/msg_reader.rs @@ -567,6 +567,7 @@ impl MessageReader for ZBufReader<'_> { #[inline(always)] fn read_data_info(&mut self) -> Option { let mut info = DataInfo::new(); + let options = self.read_zint()?; #[cfg(feature = "shared-memory")] { @@ -589,13 +590,13 @@ impl MessageReader for ZBufReader<'_> { if imsg::has_option(options, zmsg::data::info::TIMESTAMP) { info.timestamp = Some(self.read_timestamp()?); } - const RESERVED_FIELDS: u64 = zmsg::data::info::SRCID - | zmsg::data::info::SRCSN - | zmsg::data::info::RTRID - | zmsg::data::info::RTRSN; - if options & RESERVED_FIELDS != 0 { - log::error!("A message with set reserved bits has been received. This is likely to be because your network has different versions of zenoh linked together.") + if imsg::has_option(options, zmsg::data::info::SRCID) { + info.source_id = Some(self.read_zid()?); + } + if imsg::has_option(options, zmsg::data::info::SRCSN) { + info.source_sn = Some(self.read_zint()?); } + Some(info) } diff --git a/commons/zenoh-protocol/src/proto/msg_writer.rs b/commons/zenoh-protocol/src/proto/msg_writer.rs index 8c8c1f06f6..084f5f5654 100644 --- a/commons/zenoh-protocol/src/proto/msg_writer.rs +++ b/commons/zenoh-protocol/src/proto/msg_writer.rs @@ -417,6 +417,12 @@ impl MessageWriter for WBuf { if let Some(ts) = info.timestamp.as_ref() { zcheck!(self.write_timestamp(ts)); } + if let Some(zid) = info.source_id.as_ref() { + zcheck!(self.write_zid(zid)); + } + if let Some(sn) = info.source_sn { + zcheck!(self.write_zint(sn)); + } true } diff --git a/commons/zenoh-protocol/tests/msg_codec.rs b/commons/zenoh-protocol/tests/msg_codec.rs index 96c1be22e0..68699368bc 100644 --- a/commons/zenoh-protocol/tests/msg_codec.rs +++ b/commons/zenoh-protocol/tests/msg_codec.rs @@ -206,6 +206,8 @@ fn gen_data_info() -> DataInfo { timestamp: option_gen!(gen_timestamp()), #[cfg(feature = "shared-memory")] sliced: false, + source_id: option_gen!(gen_zid()), + source_sn: option_gen!(gen!(ZInt)), } } diff --git a/zenoh/src/sample.rs b/zenoh/src/sample.rs index 208b9b697c..e845f689ec 100644 --- a/zenoh/src/sample.rs +++ b/zenoh/src/sample.rs @@ -15,7 +15,9 @@ //! Sample primitives use std::convert::TryInto; +use zenoh_config::ZenohId; use zenoh_protocol::proto::DataInfo; +use zenoh_protocol_core::ZInt; use crate::buffers::ZBuf; use crate::prelude::{KeyExpr, SampleKind, Value}; @@ -42,6 +44,48 @@ impl Default for Locality { } } +/// Informations on the source of a zenoh [`Sample`]. +#[zenoh_core::unstable] +#[derive(Debug, Clone)] +pub struct SourceInfo { + /// The [`ZenohId`] of the zenoh instance that published the concerned [`Sample`]. + pub source_id: Option, + /// The sequence number of the [`Sample`] from the source. + pub source_sn: Option, +} + +#[test] +fn source_info_stack_size() { + assert_eq!(std::mem::size_of::(), 16 * 2); +} + +impl SourceInfo { + pub(crate) fn empty() -> Self { + SourceInfo { + source_id: None, + source_sn: None, + } + } +} + +impl From for SourceInfo { + fn from(data_info: DataInfo) -> Self { + SourceInfo { + source_id: data_info.source_id, + source_sn: data_info.source_sn, + } + } +} + +impl From> for SourceInfo { + fn from(data_info: Option) -> Self { + match data_info { + Some(data_info) => data_info.into(), + None => SourceInfo::empty(), + } + } +} + /// A zenoh sample. #[non_exhaustive] #[derive(Clone, Debug)] @@ -54,6 +98,16 @@ pub struct Sample { pub kind: SampleKind, /// The [`Timestamp`] of this Sample. pub timestamp: Option, + + #[cfg(feature = "unstable")] + ///
+ /// 🔬 + /// This API has been marked as unstable: it works as advertised, but we may change it in a future release. + /// To use it, you must enable zenoh's unstable feature flag. + ///
+ /// + /// Infos on the source of this Sample. + pub source_info: SourceInfo, } impl Sample { @@ -69,6 +123,8 @@ impl Sample { value: value.into(), kind: SampleKind::default(), timestamp: None, + #[cfg(feature = "unstable")] + source_info: SourceInfo::empty(), } } /// Creates a new Sample. @@ -87,6 +143,8 @@ impl Sample { value: value.into(), kind: SampleKind::default(), timestamp: None, + #[cfg(feature = "unstable")] + source_info: SourceInfo::empty(), }) } @@ -107,6 +165,8 @@ impl Sample { value, kind: data_info.kind, timestamp: data_info.timestamp, + #[cfg(feature = "unstable")] + source_info: data_info.into(), } } else { Sample { @@ -114,6 +174,8 @@ impl Sample { value, kind: SampleKind::default(), timestamp: None, + #[cfg(feature = "unstable")] + source_info: SourceInfo::empty(), } } } @@ -126,6 +188,10 @@ impl Sample { timestamp: self.timestamp, #[cfg(feature = "shared-memory")] sliced: false, + #[cfg(feature = "unstable")] + source_id: self.source_info.source_id, + #[cfg(feature = "unstable")] + source_sn: self.source_info.source_sn, }; (self.key_expr, self.value.payload, info) } @@ -143,6 +209,14 @@ impl Sample { self } + /// Sets the source info of this Sample. + #[zenoh_core::unstable] + #[inline] + pub fn with_source_info(mut self, source_info: SourceInfo) -> Self { + self.source_info = source_info; + self + } + #[inline] /// Ensure that an associated Timestamp is present in this Sample. /// If not, a new one is created with the current system time and 0x00 as id. From b2840acccba346345f20fa2a2fd9075dc575ef34 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Mon, 7 Nov 2022 15:38:58 +0100 Subject: [PATCH 08/16] Add with_source_info function to Publication builder --- zenoh/src/publication.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/zenoh/src/publication.rs b/zenoh/src/publication.rs index a2ee983976..10f6c8b72d 100644 --- a/zenoh/src/publication.rs +++ b/zenoh/src/publication.rs @@ -16,6 +16,7 @@ use crate::net::transport::Primitives; use crate::prelude::*; +use crate::sample::SourceInfo; use crate::subscriber::Reliability; use crate::Encoding; use crate::SessionRef; @@ -232,6 +233,7 @@ impl<'a> Publisher<'a> { publisher: self, value, kind, + source_info: SourceInfo::empty(), } } @@ -371,6 +373,17 @@ pub struct Publication<'a> { publisher: &'a Publisher<'a>, value: Value, kind: SampleKind, + source_info: SourceInfo, +} + +impl Publication<'_> { + /// Attach the given [`SourceInfo`] to the published data. + #[zenoh_core::unstable] + #[inline] + pub fn with_source_info(mut self, source_info: SourceInfo) -> Self { + self.source_info = source_info; + self + } } impl Resolvable for Publication<'_> { @@ -383,6 +396,7 @@ impl SyncResolve for Publication<'_> { publisher, value, kind, + source_info, } = self; log::trace!("write({:?}, [...])", publisher.key_expr); let primitives = zread!(publisher.session.state) @@ -399,6 +413,8 @@ impl SyncResolve for Publication<'_> { None }; info.timestamp = publisher.session.runtime.new_timestamp(); + info.source_id = source_info.source_id; + info.source_sn = source_info.source_sn; let data_info = if info.has_options() { Some(info) } else { None }; primitives.send_data( From 64283a7bced1fee6a7dfdbdb74a950009f7b4acf Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Mon, 7 Nov 2022 16:04:00 +0100 Subject: [PATCH 09/16] Fix clippy warnings --- zenoh-ext/examples/z_rel_cache.rs | 2 +- zenoh-ext/examples/z_rel_pub.rs | 2 +- zenoh-ext/examples/z_rel_sub.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zenoh-ext/examples/z_rel_cache.rs b/zenoh-ext/examples/z_rel_cache.rs index 1935bebe1f..708c8c50b3 100644 --- a/zenoh-ext/examples/z_rel_cache.rs +++ b/zenoh-ext/examples/z_rel_cache.rs @@ -55,7 +55,7 @@ fn parse_args() -> (Config, String, usize, String) { let args = App::new("zenoh-ext reliability cache example") .arg( Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") - .possible_values(&["peer", "client"]), + .possible_values(["peer", "client"]), ) .arg(Arg::from_usage( "-e, --connect=[ENDPOINT]... 'Endpoints to connect to.'", diff --git a/zenoh-ext/examples/z_rel_pub.rs b/zenoh-ext/examples/z_rel_pub.rs index 6ab2fd44f2..fba32f598b 100644 --- a/zenoh-ext/examples/z_rel_pub.rs +++ b/zenoh-ext/examples/z_rel_pub.rs @@ -49,7 +49,7 @@ fn parse_args() -> (Config, String, String, bool, usize) { let args = App::new("zenoh-ext reliable pub example") .arg( Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") - .possible_values(&["peer", "client"]), + .possible_values(["peer", "client"]), ) .arg(Arg::from_usage( "-e, --connect=[ENDPOINT]... 'Endpoints to connect to.'", diff --git a/zenoh-ext/examples/z_rel_sub.rs b/zenoh-ext/examples/z_rel_sub.rs index 497a3f71d7..a6deb13f73 100644 --- a/zenoh-ext/examples/z_rel_sub.rs +++ b/zenoh-ext/examples/z_rel_sub.rs @@ -66,7 +66,7 @@ fn parse_args() -> (Config, String, bool, Option) { let args = App::new("zenoh-ext reliable sub example") .arg( Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") - .possible_values(&["peer", "client"]), + .possible_values(["peer", "client"]), ) .arg(Arg::from_usage( "-e, --connect=[ENDPOINT]... 'Endpoints to connect to.'", From e74ddde68f8135e81d6e1b1bcb32606fc2227ab8 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Mon, 7 Nov 2022 16:42:13 +0100 Subject: [PATCH 10/16] ReliablePublisher, ReliableSubscriber and ReliabilityCache use SourceInfo --- zenoh-ext/src/reliability_cache.rs | 50 ++++----- zenoh-ext/src/reliable_publisher.rs | 16 +-- zenoh-ext/src/reliable_subscriber.rs | 162 ++++++++++++++------------- 3 files changed, 119 insertions(+), 109 deletions(-) diff --git a/zenoh-ext/src/reliability_cache.rs b/zenoh-ext/src/reliability_cache.rs index ff79f16e4e..5fdbd00bce 100644 --- a/zenoh-ext/src/reliability_cache.rs +++ b/zenoh-ext/src/reliability_cache.rs @@ -20,13 +20,12 @@ use { std::collections::{HashMap, VecDeque}, std::convert::TryInto, std::future::Ready, - zenoh::buffers::reader::HasReader, zenoh::prelude::r#async::*, zenoh::queryable::{Query, Queryable}, + zenoh::sample::SourceInfo, zenoh::subscriber::FlumeSubscriber, zenoh::Session, zenoh_core::{bail, AsyncResolve, Resolvable, Result as ZResult, SyncResolve}, - zenoh_protocol::io::ZBufCodec, zenoh_util::core::ResolveFuture, }; @@ -131,16 +130,15 @@ fn decode_range(range: &str) -> (Option, Option) { fn sample_in_range(sample: &Sample, start: Option, end: Option) -> bool { if start.is_none() && end.is_none() { true - } else { - let mut buf = sample.value.payload.reader(); - let _id = buf.read_zid().unwrap(); //TODO - let seq_num = buf.read_zint().unwrap(); //TODO + } else if let Some(source_sn) = sample.source_info.source_sn { match (start, end) { - (Some(start), Some(end)) => seq_num >= start && seq_num <= end, - (Some(start), None) => seq_num >= start, - (None, Some(end)) => seq_num <= end, + (Some(start), Some(end)) => source_sn >= start && source_sn <= end, + (Some(start), None) => source_sn >= start, + (None, Some(end)) => source_sn <= end, (None, None) => true, } + } else { + false } } @@ -204,24 +202,26 @@ impl<'a> ReliabilityCache<'a> { // on publication received by the local subscriber, store it sample = sub_recv.recv_async() => { if let Ok(sample) = sample { - let queryable_key_expr: KeyExpr<'_> = if let Some(prefix) = &queryable_prefix { - prefix.join(&sample.key_expr).unwrap().into() - } else { - sample.key_expr.clone() - }; + if let SourceInfo { source_id: Some(_), source_sn: Some(_)} = sample.source_info { + let queryable_key_expr: KeyExpr<'_> = if let Some(prefix) = &queryable_prefix { + prefix.join(&sample.key_expr).unwrap().into() + } else { + sample.key_expr.clone() + }; - if let Some(queue) = cache.get_mut(queryable_key_expr.as_keyexpr()) { - if queue.len() >= history { - queue.pop_front(); + if let Some(queue) = cache.get_mut(queryable_key_expr.as_keyexpr()) { + if queue.len() >= history { + queue.pop_front(); + } + queue.push_back(sample); + } else if cache.len() >= limit { + log::error!("ReliabilityCache on {}: resource_limit exceeded - can't cache publication for a new resource", + pub_key_expr); + } else { + let mut queue: VecDeque = VecDeque::new(); + queue.push_back(sample); + cache.insert(queryable_key_expr.into(), queue); } - queue.push_back(sample); - } else if cache.len() >= limit { - log::error!("ReliabilityCache on {}: resource_limit exceeded - can't cache publication for a new resource", - pub_key_expr); - } else { - let mut queue: VecDeque = VecDeque::new(); - queue.push_back(sample); - cache.insert(queryable_key_expr.into(), queue); } } }, diff --git a/zenoh-ext/src/reliable_publisher.rs b/zenoh-ext/src/reliable_publisher.rs index 72956521ae..9042bbf727 100644 --- a/zenoh-ext/src/reliable_publisher.rs +++ b/zenoh-ext/src/reliable_publisher.rs @@ -15,14 +15,15 @@ use { std::future::Ready, std::sync::atomic::{AtomicU64, Ordering}, - zenoh::buffers::WBuf, zenoh::prelude::r#async::*, zenoh::publication::{Publication, Publisher}, zenoh_core::{AsyncResolve, Resolvable, Result as ZResult, SyncResolve}, - zenoh_protocol::io::WBufCodec, zenoh_util::core::ResolveFuture, }; +#[zenoh_core::unstable] +use zenoh::sample::SourceInfo; + #[zenoh_core::unstable] use crate::{ReliabilityCache, SessionExt}; @@ -168,13 +169,12 @@ impl<'a> ReliablePublisher<'a> { where IntoValue: Into, { - let v: Value = value.into(); - let mut buf = WBuf::new(128, false); - buf.write_zid(&self._id); - buf.write_zint(self._seqnum.fetch_add(1, Ordering::Relaxed)); - buf.write_zbuf_slices(&v.payload); self._publisher - .write(kind, Value::new(buf.into()).encoding(v.encoding)) + .write(kind, value) + .with_source_info(SourceInfo { + source_id: Some(self._id), + source_sn: Some(self._seqnum.fetch_add(1, Ordering::Relaxed)), + }) } /// Put data. diff --git a/zenoh-ext/src/reliable_subscriber.rs b/zenoh-ext/src/reliable_subscriber.rs index 083307467c..8431342773 100644 --- a/zenoh-ext/src/reliable_subscriber.rs +++ b/zenoh-ext/src/reliable_subscriber.rs @@ -1,3 +1,5 @@ +use zenoh::sample::SourceInfo; + // // Copyright (c) 2022 ZettaScale Technology // @@ -20,8 +22,6 @@ use { std::future::Ready, std::sync::{Arc, Mutex}, std::time::Duration, - zenoh::buffers::reader::{HasReader, Reader}, - zenoh::buffers::ZBuf, zenoh::handlers::{locked, DefaultHandler}, zenoh::prelude::r#async::*, zenoh::query::{QueryTarget, Reply, ReplyKeyExpr}, @@ -30,7 +30,6 @@ use { zenoh_collections::timer::Timer, zenoh_collections::{Timed, TimedEvent}, zenoh_core::{zlock, AsyncResolve, Resolvable, SyncResolve}, - zenoh_protocol::io::ZBufCodec, }; /// The builder of ReliableSubscriber, allowing to configure it. @@ -284,38 +283,40 @@ fn handle_sample( wait: bool, sample: Sample, callback: &Arc, -) -> (ZenohId, bool) { - let mut buf = sample.value.payload.reader(); - let id = buf.read_zid().unwrap(); //TODO - let seq_num = buf.read_zint().unwrap(); //TODO - let mut payload = ZBuf::default(); - buf.read_into_zbuf(&mut payload, buf.remaining()); - let value = Value::new(payload).encoding(sample.encoding.clone()); - let s = Sample::new(sample.key_expr, value); - let entry = states.entry(id); - let new = matches!(&entry, Entry::Occupied(_)); - let state = entry.or_insert(InnerState { - last_seq_num: None, - pending_queries: 0, - pending_samples: HashMap::new(), - }); - if wait { - state.pending_samples.insert(seq_num, s); - } else if state.last_seq_num.is_some() && seq_num != state.last_seq_num.unwrap() + 1 { - if seq_num > state.last_seq_num.unwrap() { - state.pending_samples.insert(seq_num, s); - } - } else { - callback(s); - let mut last_seq_num = seq_num; - state.last_seq_num = Some(last_seq_num); - while let Some(s) = state.pending_samples.remove(&(last_seq_num + 1)) { - callback(s); - last_seq_num += 1; +) -> bool { + if let SourceInfo { + source_id: Some(source_id), + source_sn: Some(source_sn), + } = sample.source_info + { + let entry = states.entry(source_id); + let new = matches!(&entry, Entry::Occupied(_)); + let state = entry.or_insert(InnerState { + last_seq_num: None, + pending_queries: 0, + pending_samples: HashMap::new(), + }); + if wait { + state.pending_samples.insert(source_sn, sample); + } else if state.last_seq_num.is_some() && source_sn != state.last_seq_num.unwrap() + 1 { + if source_sn > state.last_seq_num.unwrap() { + state.pending_samples.insert(source_sn, sample); + } + } else { + callback(sample); + let mut last_seq_num = source_sn; state.last_seq_num = Some(last_seq_num); + while let Some(s) = state.pending_samples.remove(&(last_seq_num + 1)) { + callback(s); + last_seq_num += 1; + state.last_seq_num = Some(last_seq_num); + } } + new + } else { + callback(sample); + true } - (id, new) } #[zenoh_core::unstable] @@ -331,7 +332,7 @@ fn seq_num_range(start: Option, end: Option) -> String { #[zenoh_core::unstable] #[derive(Clone)] struct PeriodicQuery { - id: ZenohId, + source_id: ZenohId, statesref: Arc, bool)>>, key_expr: KeyExpr<'static>, session: Arc, @@ -342,8 +343,8 @@ struct PeriodicQuery { #[zenoh_core::unstable] impl PeriodicQuery { - fn with_id(mut self, id: ZenohId) -> Self { - self.id = id; + fn with_source_id(mut self, source_id: ZenohId) -> Self { + self.source_id = source_id; self } } @@ -354,13 +355,13 @@ impl Timed for PeriodicQuery { async fn run(&mut self) { let mut lock = zlock!(self.statesref); let (states, _wait) = &mut *lock; - if let Some(state) = states.get_mut(&self.id) { + if let Some(state) = states.get_mut(&self.source_id) { state.pending_queries += 1; - let key_expr = (&self.id.into_keyexpr()) / &self.key_expr; + let key_expr = (&self.source_id.into_keyexpr()) / &self.key_expr; let seq_num_range = seq_num_range(Some(state.last_seq_num.unwrap() + 1), None); drop(lock); let handler = RepliesHandler { - id: self.id, + source_id: self.source_id, statesref: self.statesref.clone(), callback: self.callback.clone(), }; @@ -401,7 +402,7 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { Arc::new(Timer::new(false)), period, PeriodicQuery { - id: ZenohId::try_from([1]).unwrap(), + source_id: ZenohId::try_from([1]).unwrap(), statesref: statesref.clone(), key_expr: key_expr.clone().into_owned(), session, @@ -422,42 +423,48 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { move |s: Sample| { let mut lock = zlock!(statesref); let (states, wait) = &mut *lock; - let (id, new) = handle_sample(states, *wait, s, &callback); - - if new { - if let Some((timer, period, query)) = periodic_query.as_ref() { - timer.add(TimedEvent::periodic(*period, query.clone().with_id(id))) + let source_id = s.source_info.source_id; + let new = handle_sample(states, *wait, s, &callback); + + if let Some(source_id) = source_id { + if new { + if let Some((timer, period, query)) = periodic_query.as_ref() { + timer.add(TimedEvent::periodic( + *period, + query.clone().with_source_id(source_id), + )) + } } - } - if let Some(state) = states.get_mut(&id) { - if state.pending_queries == 0 && !state.pending_samples.is_empty() { - state.pending_queries += 1; - let key_expr = (&id.into_keyexpr()) / &key_expr; - let seq_num_range = - seq_num_range(Some(state.last_seq_num.unwrap() + 1), None); - drop(lock); - let handler = RepliesHandler { - id, - statesref: statesref.clone(), - callback: callback.clone(), - }; - let _ = session - .get(Selector::from(key_expr).with_parameters(&seq_num_range)) - .callback({ - move |r: Reply| { - if let Ok(s) = r.sample { - let (ref mut states, wait) = - &mut *zlock!(handler.statesref); - handle_sample(states, *wait, s, &handler.callback); + if let Some(state) = states.get_mut(&source_id) { + if state.pending_queries == 0 && !state.pending_samples.is_empty() { + state.pending_queries += 1; + let key_expr = (&source_id.into_keyexpr()) / &key_expr; + let seq_num_range = + seq_num_range(Some(state.last_seq_num.unwrap() + 1), None); + drop(lock); + let handler = RepliesHandler { + source_id, + statesref: statesref.clone(), + callback: callback.clone(), + }; + let _ = session + .get(Selector::from(key_expr).with_parameters(&seq_num_range)) + .callback({ + move |r: Reply| { + if let Ok(s) = r.sample { + let (ref mut states, wait) = + &mut *zlock!(handler.statesref); + handle_sample(states, *wait, s, &handler.callback); + } } - } - }) - .consolidation(ConsolidationMode::None) - .accept_replies(ReplyKeyExpr::Any) - .target(query_target) - .timeout(query_timeout) - .res_sync(); + }) + .consolidation(ConsolidationMode::None) + .accept_replies(ReplyKeyExpr::Any) + .target(query_target) + .timeout(query_timeout) + .res_sync(); + } } } } @@ -525,7 +532,7 @@ struct InitialRepliesHandler { impl Drop for InitialRepliesHandler { fn drop(&mut self) { let (states, wait) = &mut *zlock!(self.statesref); - for (id, state) in states.iter_mut() { + for (source_id, state) in states.iter_mut() { let mut pending_samples = state .pending_samples .drain() @@ -536,7 +543,10 @@ impl Drop for InitialRepliesHandler { (self.callback)(sample); } if let Some((timer, period, query)) = self.periodic_query.as_ref() { - timer.add(TimedEvent::periodic(*period, query.clone().with_id(*id))) + timer.add(TimedEvent::periodic( + *period, + query.clone().with_source_id(*source_id), + )) } } *wait = false; @@ -546,7 +556,7 @@ impl Drop for InitialRepliesHandler { #[zenoh_core::unstable] #[derive(Clone)] struct RepliesHandler { - id: ZenohId, + source_id: ZenohId, statesref: Arc, bool)>>, callback: Arc, } @@ -555,7 +565,7 @@ struct RepliesHandler { impl Drop for RepliesHandler { fn drop(&mut self) { let (states, wait) = &mut *zlock!(self.statesref); - if let Some(state) = states.get_mut(&self.id) { + if let Some(state) = states.get_mut(&self.source_id) { state.pending_queries -= 1; if !state.pending_samples.is_empty() && !*wait { log::error!("Sample missed: unable to retrieve some missing samples."); From 75433d8a55627b28953d57c0773fb273dddb444c Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Mon, 7 Nov 2022 16:47:54 +0100 Subject: [PATCH 11/16] End-to-end reliability examples use demo/example key as the other examples --- zenoh-ext/examples/z_rel_cache.rs | 2 +- zenoh-ext/examples/z_rel_pub.rs | 2 +- zenoh-ext/examples/z_rel_sub.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zenoh-ext/examples/z_rel_cache.rs b/zenoh-ext/examples/z_rel_cache.rs index 708c8c50b3..c219760af4 100644 --- a/zenoh-ext/examples/z_rel_cache.rs +++ b/zenoh-ext/examples/z_rel_cache.rs @@ -65,7 +65,7 @@ fn parse_args() -> (Config, String, usize, String) { )) .arg( Arg::from_usage("-k, --key=[KEYEXPR] 'The key expression to subscribe onto'") - .default_value("demo/reliable/example/**"), + .default_value("demo/example/**"), ) .arg(Arg::from_usage( "-c, --config=[FILE] 'A configuration file.'", diff --git a/zenoh-ext/examples/z_rel_pub.rs b/zenoh-ext/examples/z_rel_pub.rs index fba32f598b..fbc821d860 100644 --- a/zenoh-ext/examples/z_rel_pub.rs +++ b/zenoh-ext/examples/z_rel_pub.rs @@ -59,7 +59,7 @@ fn parse_args() -> (Config, String, String, bool, usize) { )) .arg( Arg::from_usage("-k, --key=[KEYEXPR] 'The key expression to publish.'") - .default_value("demo/reliable/example/zenoh-rs-pub"), + .default_value("demo/example/zenoh-rs-pub"), ) .arg( Arg::from_usage("-v, --value=[VALUE] 'The value to publish.'") diff --git a/zenoh-ext/examples/z_rel_sub.rs b/zenoh-ext/examples/z_rel_sub.rs index a6deb13f73..95744806a4 100644 --- a/zenoh-ext/examples/z_rel_sub.rs +++ b/zenoh-ext/examples/z_rel_sub.rs @@ -76,7 +76,7 @@ fn parse_args() -> (Config, String, bool, Option) { )) .arg( Arg::from_usage("-k, --key=[KEYEXPR] 'The key expression to subscribe onto'") - .default_value("demo/reliable/example/**"), + .default_value("demo/example/**"), ) .arg(Arg::from_usage( "-h, --history 'Query for historical samples at startup.'", From 330eb237b2269e72ff928cbfa6a1dc75ecf4839b Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Tue, 6 Dec 2022 10:35:27 +0100 Subject: [PATCH 12/16] Fix clippy warning --- zenoh-ext/src/reliable_subscriber.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zenoh-ext/src/reliable_subscriber.rs b/zenoh-ext/src/reliable_subscriber.rs index 8431342773..7f1654e6b7 100644 --- a/zenoh-ext/src/reliable_subscriber.rs +++ b/zenoh-ext/src/reliable_subscriber.rs @@ -1,5 +1,3 @@ -use zenoh::sample::SourceInfo; - // // Copyright (c) 2022 ZettaScale Technology // @@ -25,6 +23,7 @@ use { zenoh::handlers::{locked, DefaultHandler}, zenoh::prelude::r#async::*, zenoh::query::{QueryTarget, Reply, ReplyKeyExpr}, + zenoh::sample::SourceInfo, zenoh::subscriber::{Reliability, Subscriber}, zenoh::Result as ZResult, zenoh_collections::timer::Timer, From 72235b21bc66302a14a4e4076e19abffbfb25bf6 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Tue, 6 Dec 2022 10:52:53 +0100 Subject: [PATCH 13/16] Rename (E2E)Reliability to NBFTReliability --- zenoh-ext/Cargo.toml | 12 ++-- .../{z_rel_cache.rs => z_nbftr_cache.rs} | 4 +- .../examples/{z_rel_pub.rs => z_nbftr_pub.rs} | 4 +- .../examples/{z_rel_sub.rs => z_nbftr_sub.rs} | 4 +- zenoh-ext/src/lib.rs | 16 ++--- ...lity_cache.rs => nbftreliability_cache.rs} | 42 ++++++------ ...publisher.rs => nbftreliable_publisher.rs} | 34 +++++----- ...bscriber.rs => nbftreliable_subscriber.rs} | 65 ++++++++++--------- zenoh-ext/src/session_ext.rs | 35 +++++----- 9 files changed, 112 insertions(+), 104 deletions(-) rename zenoh-ext/examples/{z_rel_cache.rs => z_nbftr_cache.rs} (95%) rename zenoh-ext/examples/{z_rel_pub.rs => z_nbftr_pub.rs} (95%) rename zenoh-ext/examples/{z_rel_sub.rs => z_nbftr_sub.rs} (96%) rename zenoh-ext/src/{reliability_cache.rs => nbftreliability_cache.rs} (89%) rename zenoh-ext/src/{reliable_publisher.rs => nbftreliable_publisher.rs} (88%) rename zenoh-ext/src/{reliable_subscriber.rs => nbftreliable_subscriber.rs} (89%) diff --git a/zenoh-ext/Cargo.toml b/zenoh-ext/Cargo.toml index 4d2f80077c..3607e56569 100644 --- a/zenoh-ext/Cargo.toml +++ b/zenoh-ext/Cargo.toml @@ -70,16 +70,16 @@ name = "z_view_size" path = "examples/z_view_size.rs" [[example]] -name = "z_rel_sub" -path = "examples/z_rel_sub.rs" +name = "z_nbftr_sub" +path = "examples/z_nbftr_sub.rs" required-features = ["unstable"] [[example]] -name = "z_rel_pub" -path = "examples/z_rel_pub.rs" +name = "z_nbftr_pub" +path = "examples/z_nbftr_pub.rs" required-features = ["unstable"] [[example]] -name = "z_rel_cache" -path = "examples/z_rel_cache.rs" +name = "z_nbftr_cache" +path = "examples/z_nbftr_cache.rs" required-features = ["unstable"] diff --git a/zenoh-ext/examples/z_rel_cache.rs b/zenoh-ext/examples/z_nbftr_cache.rs similarity index 95% rename from zenoh-ext/examples/z_rel_cache.rs rename to zenoh-ext/examples/z_nbftr_cache.rs index c219760af4..a9b19f580d 100644 --- a/zenoh-ext/examples/z_rel_cache.rs +++ b/zenoh-ext/examples/z_nbftr_cache.rs @@ -29,7 +29,7 @@ async fn main() { println!("Opening session..."); let session = zenoh::open(config).res().await.unwrap().into_arc(); - println!("Declaring ReliabilityCache on {}", key_expr); + println!("Declaring NBFTReliabilityCache on {}", key_expr); let _cache = session .declare_reliability_cache(key_expr) .history(history) @@ -52,7 +52,7 @@ async fn main() { } fn parse_args() -> (Config, String, usize, String) { - let args = App::new("zenoh-ext reliability cache example") + let args = App::new("zenoh-ext non blocking fault tolerant reliability cache example") .arg( Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") .possible_values(["peer", "client"]), diff --git a/zenoh-ext/examples/z_rel_pub.rs b/zenoh-ext/examples/z_nbftr_pub.rs similarity index 95% rename from zenoh-ext/examples/z_rel_pub.rs rename to zenoh-ext/examples/z_nbftr_pub.rs index fbc821d860..4d1c6f85d9 100644 --- a/zenoh-ext/examples/z_rel_pub.rs +++ b/zenoh-ext/examples/z_nbftr_pub.rs @@ -28,7 +28,7 @@ async fn main() { println!("Opening session..."); let session = zenoh::open(config).res().await.unwrap(); - println!("Declaring ReliablePublisher on {}", &key_expr); + println!("Declaring NBFTReliablePublisher on {}", &key_expr); let publ = session .declare_reliable_publisher(&key_expr) .with_cache(cache) @@ -46,7 +46,7 @@ async fn main() { } fn parse_args() -> (Config, String, String, bool, usize) { - let args = App::new("zenoh-ext reliable pub example") + let args = App::new("zenoh-ext non blocking fault tolerant reliable publisher example") .arg( Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") .possible_values(["peer", "client"]), diff --git a/zenoh-ext/examples/z_rel_sub.rs b/zenoh-ext/examples/z_nbftr_sub.rs similarity index 96% rename from zenoh-ext/examples/z_rel_sub.rs rename to zenoh-ext/examples/z_nbftr_sub.rs index 95744806a4..4717136d63 100644 --- a/zenoh-ext/examples/z_rel_sub.rs +++ b/zenoh-ext/examples/z_nbftr_sub.rs @@ -31,7 +31,7 @@ async fn main() { println!("Opening session..."); let session = zenoh::open(config).res().await.unwrap().into_arc(); - println!("Declaring ReliableSubscriber on {}", key_expr); + println!("Declaring NBFTReliableSubscriber on {}", key_expr); let subscriber = session .declare_reliable_subscriber(key_expr) .history(history) @@ -63,7 +63,7 @@ async fn main() { } fn parse_args() -> (Config, String, bool, Option) { - let args = App::new("zenoh-ext reliable sub example") + let args = App::new("zenoh-ext non blocking fault tolerant reliable subscriber example") .arg( Arg::from_usage("-m, --mode=[MODE] 'The zenoh session mode (peer by default).") .possible_values(["peer", "client"]), diff --git a/zenoh-ext/src/lib.rs b/zenoh-ext/src/lib.rs index 1d131aa173..c1e42af250 100644 --- a/zenoh-ext/src/lib.rs +++ b/zenoh-ext/src/lib.rs @@ -12,20 +12,20 @@ // ZettaScale Zenoh Team, // pub mod group; +mod nbftreliability_cache; +mod nbftreliable_publisher; +mod nbftreliable_subscriber; mod publication_cache; mod querying_subscriber; -mod reliability_cache; -mod reliable_publisher; -mod reliable_subscriber; mod session_ext; mod subscriber_ext; -pub use publication_cache::{PublicationCache, PublicationCacheBuilder}; -pub use querying_subscriber::{QueryingSubscriber, QueryingSubscriberBuilder}; #[zenoh_core::unstable] -pub use reliability_cache::{ReliabilityCache, ReliabilityCacheBuilder}; +pub use nbftreliability_cache::{NBFTReliabilityCache, NBFTReliabilityCacheBuilder}; #[zenoh_core::unstable] -pub use reliable_publisher::{ReliablePublisher, ReliablePublisherBuilder}; +pub use nbftreliable_publisher::{NBFTReliablePublisher, NBFTReliablePublisherBuilder}; #[zenoh_core::unstable] -pub use reliable_subscriber::{ReliableSubscriber, ReliableSubscriberBuilder}; +pub use nbftreliable_subscriber::{NBFTReliableSubscriber, NBFTReliableSubscriberBuilder}; +pub use publication_cache::{PublicationCache, PublicationCacheBuilder}; +pub use querying_subscriber::{QueryingSubscriber, QueryingSubscriberBuilder}; pub use session_ext::{ArcSessionExt, SessionExt}; pub use subscriber_ext::SubscriberForward; diff --git a/zenoh-ext/src/reliability_cache.rs b/zenoh-ext/src/nbftreliability_cache.rs similarity index 89% rename from zenoh-ext/src/reliability_cache.rs rename to zenoh-ext/src/nbftreliability_cache.rs index 5fdbd00bce..1762f95fbf 100644 --- a/zenoh-ext/src/reliability_cache.rs +++ b/zenoh-ext/src/nbftreliability_cache.rs @@ -29,9 +29,9 @@ use { zenoh_util::core::ResolveFuture, }; -/// The builder of ReliabilityCache, allowing to configure it. +/// The builder of NBFTReliabilityCache, allowing to configure it. #[zenoh_core::unstable] -pub struct ReliabilityCacheBuilder<'a, 'b, 'c> { +pub struct NBFTReliabilityCacheBuilder<'a, 'b, 'c> { session: &'a Session, pub_key_expr: ZResult>, queryable_prefix: Option>>, @@ -42,12 +42,12 @@ pub struct ReliabilityCacheBuilder<'a, 'b, 'c> { } #[zenoh_core::unstable] -impl<'a, 'b, 'c> ReliabilityCacheBuilder<'a, 'b, 'c> { +impl<'a, 'b, 'c> NBFTReliabilityCacheBuilder<'a, 'b, 'c> { pub(crate) fn new( session: &'a Session, pub_key_expr: ZResult>, - ) -> ReliabilityCacheBuilder<'a, 'b, 'c> { - ReliabilityCacheBuilder { + ) -> NBFTReliabilityCacheBuilder<'a, 'b, 'c> { + NBFTReliabilityCacheBuilder { session, pub_key_expr, queryable_prefix: None, @@ -68,7 +68,7 @@ impl<'a, 'b, 'c> ReliabilityCacheBuilder<'a, 'b, 'c> { self } - /// Restrict the matching publications that will be cached by this [`ReliabilityCache`] + /// Restrict the matching publications that will be cached by this [`NBFTReliabilityCache`] /// to the ones that have the given [`Locality`](crate::prelude::Locality). #[inline] pub fn subscriber_allowed_origin(mut self, origin: Locality) -> Self { @@ -76,7 +76,7 @@ impl<'a, 'b, 'c> ReliabilityCacheBuilder<'a, 'b, 'c> { self } - /// Restrict the matching queries that will be receive by this [`ReliabilityCache`]'s queryable + /// Restrict the matching queries that will be receive by this [`NBFTReliabilityCache`]'s queryable /// to the ones that have the given [`Locality`](crate::prelude::Locality). #[inline] pub fn queryable_allowed_origin(mut self, origin: Locality) -> Self { @@ -98,19 +98,19 @@ impl<'a, 'b, 'c> ReliabilityCacheBuilder<'a, 'b, 'c> { } #[zenoh_core::unstable] -impl<'a> Resolvable for ReliabilityCacheBuilder<'a, '_, '_> { - type To = ZResult>; +impl<'a> Resolvable for NBFTReliabilityCacheBuilder<'a, '_, '_> { + type To = ZResult>; } #[zenoh_core::unstable] -impl SyncResolve for ReliabilityCacheBuilder<'_, '_, '_> { +impl SyncResolve for NBFTReliabilityCacheBuilder<'_, '_, '_> { fn res_sync(self) -> ::To { - ReliabilityCache::new(self) + NBFTReliabilityCache::new(self) } } #[zenoh_core::unstable] -impl<'a> AsyncResolve for ReliabilityCacheBuilder<'a, '_, '_> { +impl<'a> AsyncResolve for NBFTReliabilityCacheBuilder<'a, '_, '_> { type Future = Ready; fn res_async(self) -> Self::Future { @@ -143,17 +143,17 @@ fn sample_in_range(sample: &Sample, start: Option, end: Option) -> b } #[zenoh_core::unstable] -pub struct ReliabilityCache<'a> { +pub struct NBFTReliabilityCache<'a> { _sub: FlumeSubscriber<'a>, _queryable: Queryable<'a, flume::Receiver>, _stoptx: Sender, } #[zenoh_core::unstable] -impl<'a> ReliabilityCache<'a> { - fn new(conf: ReliabilityCacheBuilder<'a, '_, '_>) -> ZResult> { +impl<'a> NBFTReliabilityCache<'a> { + fn new(conf: NBFTReliabilityCacheBuilder<'a, '_, '_>) -> ZResult> { let key_expr = conf.pub_key_expr?; - // the queryable_prefix (optional), and the key_expr for ReliabilityCache's queryable ("[]/") + // the queryable_prefix (optional), and the key_expr for NBFTReliabilityCache's queryable ("[]/") let (queryable_prefix, queryable_key_expr): (Option, KeyExpr) = match conf.queryable_prefix { None => (None, key_expr.clone()), @@ -164,7 +164,7 @@ impl<'a> ReliabilityCache<'a> { Some(Err(e)) => bail!("Invalid key expression for queryable_prefix: {}", e), }; log::debug!( - "Create ReliabilityCache on {} with history={} resource_limit={:?}", + "Create NBFTReliabilityCache on {} with history={} resource_limit={:?}", &key_expr, conf.history, conf.resources_limit @@ -215,7 +215,7 @@ impl<'a> ReliabilityCache<'a> { } queue.push_back(sample); } else if cache.len() >= limit { - log::error!("ReliabilityCache on {}: resource_limit exceeded - can't cache publication for a new resource", + log::error!("NBFTReliabilityCache on {}: resource_limit exceeded - can't cache publication for a new resource", pub_key_expr); } else { let mut queue: VecDeque = VecDeque::new(); @@ -264,18 +264,18 @@ impl<'a> ReliabilityCache<'a> { } }); - Ok(ReliabilityCache { + Ok(NBFTReliabilityCache { _sub: sub, _queryable: queryable, _stoptx: stoptx, }) } - /// Close this ReliabilityCache + /// Close this NBFTReliabilityCache #[inline] pub fn close(self) -> impl Resolve> + 'a { ResolveFuture::new(async move { - let ReliabilityCache { + let NBFTReliabilityCache { _queryable, _sub, _stoptx, diff --git a/zenoh-ext/src/reliable_publisher.rs b/zenoh-ext/src/nbftreliable_publisher.rs similarity index 88% rename from zenoh-ext/src/reliable_publisher.rs rename to zenoh-ext/src/nbftreliable_publisher.rs index 9042bbf727..3518e62fa5 100644 --- a/zenoh-ext/src/reliable_publisher.rs +++ b/zenoh-ext/src/nbftreliable_publisher.rs @@ -25,11 +25,11 @@ use { use zenoh::sample::SourceInfo; #[zenoh_core::unstable] -use crate::{ReliabilityCache, SessionExt}; +use crate::{NBFTReliabilityCache, SessionExt}; -/// The builder of ReliablePublisher, allowing to configure it. +/// The builder of NBFTReliablePublisher, allowing to configure it. #[zenoh_core::unstable] -pub struct ReliablePublisherBuilder<'a, 'b> { +pub struct NBFTReliablePublisherBuilder<'a, 'b> { session: &'a Session, pub_key_expr: ZResult>, with_cache: bool, @@ -38,12 +38,12 @@ pub struct ReliablePublisherBuilder<'a, 'b> { } #[zenoh_core::unstable] -impl<'a, 'b> ReliablePublisherBuilder<'a, 'b> { +impl<'a, 'b> NBFTReliablePublisherBuilder<'a, 'b> { pub(crate) fn new( session: &'a Session, pub_key_expr: ZResult>, - ) -> ReliablePublisherBuilder<'a, 'b> { - ReliablePublisherBuilder { + ) -> NBFTReliablePublisherBuilder<'a, 'b> { + NBFTReliablePublisherBuilder { session, pub_key_expr, with_cache: true, @@ -72,19 +72,19 @@ impl<'a, 'b> ReliablePublisherBuilder<'a, 'b> { } #[zenoh_core::unstable] -impl<'a> Resolvable for ReliablePublisherBuilder<'a, '_> { - type To = ZResult>; +impl<'a> Resolvable for NBFTReliablePublisherBuilder<'a, '_> { + type To = ZResult>; } #[zenoh_core::unstable] -impl SyncResolve for ReliablePublisherBuilder<'_, '_> { +impl SyncResolve for NBFTReliablePublisherBuilder<'_, '_> { fn res_sync(self) -> ::To { - ReliablePublisher::new(self) + NBFTReliablePublisher::new(self) } } #[zenoh_core::unstable] -impl<'a> AsyncResolve for ReliablePublisherBuilder<'a, '_> { +impl<'a> AsyncResolve for NBFTReliablePublisherBuilder<'a, '_> { type Future = Ready; fn res_async(self) -> Self::Future { @@ -93,16 +93,16 @@ impl<'a> AsyncResolve for ReliablePublisherBuilder<'a, '_> { } #[zenoh_core::unstable] -pub struct ReliablePublisher<'a> { +pub struct NBFTReliablePublisher<'a> { _id: ZenohId, _seqnum: AtomicU64, _publisher: Publisher<'a>, - _cache: Option>, + _cache: Option>, } #[zenoh_core::unstable] -impl<'a> ReliablePublisher<'a> { - fn new(conf: ReliablePublisherBuilder<'a, '_>) -> ZResult { +impl<'a> NBFTReliablePublisher<'a> { + fn new(conf: NBFTReliablePublisherBuilder<'a, '_>) -> ZResult { let key_expr = conf.pub_key_expr?; let id = conf.session.info().zid().res_sync(); @@ -127,7 +127,7 @@ impl<'a> ReliablePublisher<'a> { None }; - Ok(ReliablePublisher { + Ok(NBFTReliablePublisher { _id: id, _seqnum: AtomicU64::new(0), _publisher: publisher, @@ -227,7 +227,7 @@ impl<'a> ReliablePublisher<'a> { /// ``` pub fn undeclare(self) -> impl Resolve> + 'a { ResolveFuture::new(async move { - let ReliablePublisher { + let NBFTReliablePublisher { _id, _seqnum, _publisher, diff --git a/zenoh-ext/src/reliable_subscriber.rs b/zenoh-ext/src/nbftreliable_subscriber.rs similarity index 89% rename from zenoh-ext/src/reliable_subscriber.rs rename to zenoh-ext/src/nbftreliable_subscriber.rs index 7f1654e6b7..68ac677937 100644 --- a/zenoh-ext/src/reliable_subscriber.rs +++ b/zenoh-ext/src/nbftreliable_subscriber.rs @@ -31,9 +31,9 @@ use { zenoh_core::{zlock, AsyncResolve, Resolvable, SyncResolve}, }; -/// The builder of ReliableSubscriber, allowing to configure it. +/// The builder of NBFTReliableSubscriber, allowing to configure it. #[zenoh_core::unstable] -pub struct ReliableSubscriberBuilder<'b, Handler> { +pub struct NBFTReliableSubscriberBuilder<'b, Handler> { session: Arc, key_expr: ZResult>, reliability: Reliability, @@ -46,12 +46,12 @@ pub struct ReliableSubscriberBuilder<'b, Handler> { } #[zenoh_core::unstable] -impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { +impl<'b> NBFTReliableSubscriberBuilder<'b, DefaultHandler> { pub(crate) fn new( session: Arc, key_expr: ZResult>, - ) -> ReliableSubscriberBuilder<'b, DefaultHandler> { - ReliableSubscriberBuilder { + ) -> NBFTReliableSubscriberBuilder<'b, DefaultHandler> { + NBFTReliableSubscriberBuilder { session, key_expr, reliability: Reliability::default(), @@ -64,13 +64,16 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { } } - /// Add callback to ReliableSubscriber. + /// Add callback to NBFTReliableSubscriber. #[inline] - pub fn callback(self, callback: Callback) -> ReliableSubscriberBuilder<'b, Callback> + pub fn callback( + self, + callback: Callback, + ) -> NBFTReliableSubscriberBuilder<'b, Callback> where Callback: Fn(Sample) + Send + Sync + 'static, { - let ReliableSubscriberBuilder { + let NBFTReliableSubscriberBuilder { session, key_expr, reliability, @@ -81,7 +84,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { history, handler: _, } = self; - ReliableSubscriberBuilder { + NBFTReliableSubscriberBuilder { session, key_expr, reliability, @@ -94,28 +97,28 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { } } - /// Add callback to `ReliableSubscriber`. + /// Add callback to `NBFTReliableSubscriber`. /// /// Using this guarantees that your callback will never be called concurrently. - /// If your callback is also accepted by the [`callback`](ReliableSubscriberBuilder::callback) method, we suggest you use it instead of `callback_mut` + /// If your callback is also accepted by the [`callback`](NBFTReliableSubscriberBuilder::callback) method, we suggest you use it instead of `callback_mut` #[inline] pub fn callback_mut( self, callback: CallbackMut, - ) -> ReliableSubscriberBuilder<'b, impl Fn(Sample) + Send + Sync + 'static> + ) -> NBFTReliableSubscriberBuilder<'b, impl Fn(Sample) + Send + Sync + 'static> where CallbackMut: FnMut(Sample) + Send + Sync + 'static, { self.callback(locked(callback)) } - /// Make the built ReliableSubscriber a [`ReliableSubscriber`](ReliableSubscriber). + /// Make the built NBFTReliableSubscriber a [`NBFTReliableSubscriber`](NBFTReliableSubscriber). #[inline] - pub fn with(self, handler: Handler) -> ReliableSubscriberBuilder<'b, Handler> + pub fn with(self, handler: Handler) -> NBFTReliableSubscriberBuilder<'b, Handler> where Handler: zenoh::prelude::IntoCallbackReceiverPair<'static, Sample>, { - let ReliableSubscriberBuilder { + let NBFTReliableSubscriberBuilder { session, key_expr, reliability, @@ -126,7 +129,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { history, handler: _, } = self; - ReliableSubscriberBuilder { + NBFTReliableSubscriberBuilder { session, key_expr, reliability, @@ -141,7 +144,7 @@ impl<'b> ReliableSubscriberBuilder<'b, DefaultHandler> { } #[zenoh_core::unstable] -impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { +impl<'b, Handler> NBFTReliableSubscriberBuilder<'b, Handler> { /// Change the subscription reliability. #[inline] pub fn reliability(mut self, reliability: Reliability) -> Self { @@ -200,8 +203,8 @@ impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { self } - fn with_static_keys(self) -> ReliableSubscriberBuilder<'static, Handler> { - ReliableSubscriberBuilder { + fn with_static_keys(self) -> NBFTReliableSubscriberBuilder<'static, Handler> { + NBFTReliableSubscriberBuilder { session: self.session, key_expr: self.key_expr.map(|s| s.into_owned()), reliability: self.reliability, @@ -216,27 +219,27 @@ impl<'b, Handler> ReliableSubscriberBuilder<'b, Handler> { } #[zenoh_core::unstable] -impl<'a, Handler> Resolvable for ReliableSubscriberBuilder<'a, Handler> +impl<'a, Handler> Resolvable for NBFTReliableSubscriberBuilder<'a, Handler> where Handler: IntoCallbackReceiverPair<'static, Sample>, Handler::Receiver: Send, { - type To = ZResult>; + type To = ZResult>; } #[zenoh_core::unstable] -impl SyncResolve for ReliableSubscriberBuilder<'_, Handler> +impl SyncResolve for NBFTReliableSubscriberBuilder<'_, Handler> where Handler: IntoCallbackReceiverPair<'static, Sample> + Send, Handler::Receiver: Send, { fn res_sync(self) -> ::To { - ReliableSubscriber::new(self.with_static_keys()) + NBFTReliableSubscriber::new(self.with_static_keys()) } } #[zenoh_core::unstable] -impl AsyncResolve for ReliableSubscriberBuilder<'_, Handler> +impl AsyncResolve for NBFTReliableSubscriberBuilder<'_, Handler> where Handler: IntoCallbackReceiverPair<'static, Sample> + Send, Handler::Receiver: Send, @@ -256,13 +259,13 @@ struct InnerState { } #[zenoh_core::unstable] -pub struct ReliableSubscriber<'a, Receiver> { +pub struct NBFTReliableSubscriber<'a, Receiver> { _subscriber: Subscriber<'a, ()>, receiver: Receiver, } #[zenoh_core::unstable] -impl std::ops::Deref for ReliableSubscriber<'_, Receiver> { +impl std::ops::Deref for NBFTReliableSubscriber<'_, Receiver> { type Target = Receiver; fn deref(&self) -> &Self::Target { &self.receiver @@ -270,7 +273,7 @@ impl std::ops::Deref for ReliableSubscriber<'_, Receiver> { } #[zenoh_core::unstable] -impl std::ops::DerefMut for ReliableSubscriber<'_, Receiver> { +impl std::ops::DerefMut for NBFTReliableSubscriber<'_, Receiver> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.receiver } @@ -385,8 +388,8 @@ impl Timed for PeriodicQuery { } #[zenoh_core::unstable] -impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { - fn new(conf: ReliableSubscriberBuilder<'a, Handler>) -> ZResult +impl<'a, Receiver> NBFTReliableSubscriber<'a, Receiver> { + fn new(conf: NBFTReliableSubscriberBuilder<'a, Handler>) -> ZResult where Handler: IntoCallbackReceiverPair<'static, Sample, Receiver = Receiver> + Send, { @@ -504,7 +507,7 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { .res_sync(); } - let reliable_subscriber = ReliableSubscriber { + let reliable_subscriber = NBFTReliableSubscriber { _subscriber: subscriber, receiver, }; @@ -512,7 +515,7 @@ impl<'a, Receiver> ReliableSubscriber<'a, Receiver> { Ok(reliable_subscriber) } - /// Close this ReliableSubscriber + /// Close this NBFTReliableSubscriber #[inline] pub fn close(self) -> impl Resolve> + 'a { self._subscriber.undeclare() diff --git a/zenoh-ext/src/session_ext.rs b/zenoh-ext/src/session_ext.rs index a975cb56ed..c00ae9910c 100644 --- a/zenoh-ext/src/session_ext.rs +++ b/zenoh-ext/src/session_ext.rs @@ -11,9 +11,11 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::{PublicationCacheBuilder, QueryingSubscriberBuilder}; #[zenoh_core::unstable] -use super::{ReliabilityCacheBuilder, ReliablePublisherBuilder, ReliableSubscriberBuilder}; +use super::{ + NBFTReliabilityCacheBuilder, NBFTReliablePublisherBuilder, NBFTReliableSubscriberBuilder, +}; +use super::{PublicationCacheBuilder, QueryingSubscriberBuilder}; use std::convert::TryInto; use std::fmt; use std::ops::Deref; @@ -100,7 +102,7 @@ pub trait SessionExt { fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, - ) -> ReliabilityCacheBuilder<'a, 'b, 'c> + ) -> NBFTReliabilityCacheBuilder<'a, 'b, 'c> where TryIntoKeyExpr: TryInto>, >>::Error: Into; @@ -109,7 +111,7 @@ pub trait SessionExt { fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, - ) -> ReliablePublisherBuilder<'a, 'b> + ) -> NBFTReliablePublisherBuilder<'a, 'b> where TryIntoKeyExpr: TryInto>, >>::Error: Into; @@ -145,24 +147,24 @@ impl SessionExt for Session { fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, - ) -> ReliabilityCacheBuilder<'a, 'b, 'c> + ) -> NBFTReliabilityCacheBuilder<'a, 'b, 'c> where TryIntoKeyExpr: TryInto>, >>::Error: Into, { - ReliabilityCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) + NBFTReliabilityCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } #[zenoh_core::unstable] fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, - ) -> ReliablePublisherBuilder<'a, 'b> + ) -> NBFTReliablePublisherBuilder<'a, 'b> where TryIntoKeyExpr: TryInto>, >>::Error: Into, { - ReliablePublisherBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) + NBFTReliablePublisherBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } } pub trait ArcSessionExt { @@ -170,7 +172,7 @@ pub trait ArcSessionExt { fn declare_reliable_subscriber<'b, TryIntoKeyExpr>( &self, sub_key_expr: TryIntoKeyExpr, - ) -> ReliableSubscriberBuilder<'b, DefaultHandler> + ) -> NBFTReliableSubscriberBuilder<'b, DefaultHandler> where TryIntoKeyExpr: TryInto>, >>::Error: Into; @@ -206,24 +208,24 @@ impl SessionExt for Arc { fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, - ) -> ReliabilityCacheBuilder<'a, 'b, 'c> + ) -> NBFTReliabilityCacheBuilder<'a, 'b, 'c> where TryIntoKeyExpr: TryInto>, >>::Error: Into, { - ReliabilityCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) + NBFTReliabilityCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } #[zenoh_core::unstable] fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, - ) -> ReliablePublisherBuilder<'a, 'b> + ) -> NBFTReliablePublisherBuilder<'a, 'b> where TryIntoKeyExpr: TryInto>, >>::Error: Into, { - ReliablePublisherBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) + NBFTReliablePublisherBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } } @@ -232,11 +234,14 @@ impl ArcSessionExt for Arc { fn declare_reliable_subscriber<'b, TryIntoKeyExpr>( &self, sub_key_expr: TryIntoKeyExpr, - ) -> ReliableSubscriberBuilder<'b, DefaultHandler> + ) -> NBFTReliableSubscriberBuilder<'b, DefaultHandler> where TryIntoKeyExpr: TryInto>, >>::Error: Into, { - ReliableSubscriberBuilder::new(self.clone(), sub_key_expr.try_into().map_err(Into::into)) + NBFTReliableSubscriberBuilder::new( + self.clone(), + sub_key_expr.try_into().map_err(Into::into), + ) } } From 15e39acdba391e00bc16b17fd9b6edcbb7382e99 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Wed, 14 Dec 2022 15:36:47 +0100 Subject: [PATCH 14/16] Avoid unsafe using OwnedKeyExpr::borrow --- Cargo.lock | 1 - zenoh-ext/src/nbftreliability_cache.rs | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a642586f18..43095fcf2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3867,7 +3867,6 @@ dependencies = [ "zenoh", "zenoh-collections", "zenoh-core", - "zenoh-protocol", "zenoh-sync", "zenoh-util", ] diff --git a/zenoh-ext/src/nbftreliability_cache.rs b/zenoh-ext/src/nbftreliability_cache.rs index 1762f95fbf..284201d084 100644 --- a/zenoh-ext/src/nbftreliability_cache.rs +++ b/zenoh-ext/src/nbftreliability_cache.rs @@ -17,6 +17,7 @@ use { async_std::task, futures::select, futures::{FutureExt, StreamExt}, + std::borrow::Borrow, std::collections::{HashMap, VecDeque}, std::convert::TryInto, std::future::Ready, @@ -242,7 +243,7 @@ impl<'a> NBFTReliabilityCache<'a> { } } else { for (key_expr, queue) in cache.iter() { - if query.selector().key_expr.intersects(unsafe{ keyexpr::from_str_unchecked(key_expr) }) { + if query.selector().key_expr.intersects(key_expr.borrow()) { for sample in queue { if sample_in_range(sample, start, end) { if let Err(e) = query.reply(Ok(sample.clone())).res_async().await { From 3464907744792f9a183eb11dc5793a1c34b4a9c1 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Wed, 14 Dec 2022 17:05:25 +0100 Subject: [PATCH 15/16] Check that sample keys of query replies indeed intersect the NBFTReliableSubscriber key_expr --- zenoh-ext/src/nbftreliable_subscriber.rs | 31 +++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/zenoh-ext/src/nbftreliable_subscriber.rs b/zenoh-ext/src/nbftreliable_subscriber.rs index 68ac677937..c763dde42a 100644 --- a/zenoh-ext/src/nbftreliable_subscriber.rs +++ b/zenoh-ext/src/nbftreliable_subscriber.rs @@ -359,7 +359,7 @@ impl Timed for PeriodicQuery { let (states, _wait) = &mut *lock; if let Some(state) = states.get_mut(&self.source_id) { state.pending_queries += 1; - let key_expr = (&self.source_id.into_keyexpr()) / &self.key_expr; + let query_expr = (&self.source_id.into_keyexpr()) / &self.key_expr; let seq_num_range = seq_num_range(Some(state.last_seq_num.unwrap() + 1), None); drop(lock); let handler = RepliesHandler { @@ -369,12 +369,15 @@ impl Timed for PeriodicQuery { }; let _ = self .session - .get(Selector::from(key_expr).with_parameters(&seq_num_range)) + .get(Selector::from(query_expr).with_parameters(&seq_num_range)) .callback({ + let key_expr = self.key_expr.clone().into_owned(); move |r: Reply| { if let Ok(s) = r.sample { - let (ref mut states, wait) = &mut *zlock!(handler.statesref); - handle_sample(states, *wait, s, &handler.callback); + if key_expr.intersects(&s.key_expr) { + let (ref mut states, wait) = &mut *zlock!(handler.statesref); + handle_sample(states, *wait, s, &handler.callback); + } } } }) @@ -441,7 +444,7 @@ impl<'a, Receiver> NBFTReliableSubscriber<'a, Receiver> { if let Some(state) = states.get_mut(&source_id) { if state.pending_queries == 0 && !state.pending_samples.is_empty() { state.pending_queries += 1; - let key_expr = (&source_id.into_keyexpr()) / &key_expr; + let query_expr = (&source_id.into_keyexpr()) / &key_expr; let seq_num_range = seq_num_range(Some(state.last_seq_num.unwrap() + 1), None); drop(lock); @@ -451,13 +454,16 @@ impl<'a, Receiver> NBFTReliableSubscriber<'a, Receiver> { callback: callback.clone(), }; let _ = session - .get(Selector::from(key_expr).with_parameters(&seq_num_range)) + .get(Selector::from(query_expr).with_parameters(&seq_num_range)) .callback({ + let key_expr = key_expr.clone().into_owned(); move |r: Reply| { if let Ok(s) = r.sample { - let (ref mut states, wait) = - &mut *zlock!(handler.statesref); - handle_sample(states, *wait, s, &handler.callback); + if key_expr.intersects(&s.key_expr) { + let (ref mut states, wait) = + &mut *zlock!(handler.statesref); + handle_sample(states, *wait, s, &handler.callback); + } } } }) @@ -493,10 +499,13 @@ impl<'a, Receiver> NBFTReliableSubscriber<'a, Receiver> { .with_parameters("0.."), ) .callback({ + let key_expr = key_expr.clone().into_owned(); move |r: Reply| { if let Ok(s) = r.sample { - let (ref mut states, wait) = &mut *zlock!(handler.statesref); - handle_sample(states, *wait, s, &handler.callback); + if key_expr.intersects(&s.key_expr) { + let (ref mut states, wait) = &mut *zlock!(handler.statesref); + handle_sample(states, *wait, s, &handler.callback); + } } } }) From 89957a105f86b6c8a3f1ca4c3a3858bd09cbac22 Mon Sep 17 00:00:00 2001 From: OlivierHecart Date: Wed, 14 Dec 2022 17:14:36 +0100 Subject: [PATCH 16/16] Rename nbft declaration functions --- zenoh-ext/examples/z_nbftr_cache.rs | 2 +- zenoh-ext/examples/z_nbftr_pub.rs | 2 +- zenoh-ext/examples/z_nbftr_sub.rs | 2 +- zenoh-ext/src/nbftreliable_publisher.rs | 2 +- zenoh-ext/src/session_ext.rs | 16 ++++++++-------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/zenoh-ext/examples/z_nbftr_cache.rs b/zenoh-ext/examples/z_nbftr_cache.rs index a9b19f580d..cdc52867ee 100644 --- a/zenoh-ext/examples/z_nbftr_cache.rs +++ b/zenoh-ext/examples/z_nbftr_cache.rs @@ -31,7 +31,7 @@ async fn main() { println!("Declaring NBFTReliabilityCache on {}", key_expr); let _cache = session - .declare_reliability_cache(key_expr) + .declare_nbftreliability_cache(key_expr) .history(history) .queryable_prefix(prefix) .res() diff --git a/zenoh-ext/examples/z_nbftr_pub.rs b/zenoh-ext/examples/z_nbftr_pub.rs index 4d1c6f85d9..360a32fd79 100644 --- a/zenoh-ext/examples/z_nbftr_pub.rs +++ b/zenoh-ext/examples/z_nbftr_pub.rs @@ -30,7 +30,7 @@ async fn main() { println!("Declaring NBFTReliablePublisher on {}", &key_expr); let publ = session - .declare_reliable_publisher(&key_expr) + .declare_nbftreliable_publisher(&key_expr) .with_cache(cache) .history(history) .res() diff --git a/zenoh-ext/examples/z_nbftr_sub.rs b/zenoh-ext/examples/z_nbftr_sub.rs index 4717136d63..ae5bdef88c 100644 --- a/zenoh-ext/examples/z_nbftr_sub.rs +++ b/zenoh-ext/examples/z_nbftr_sub.rs @@ -33,7 +33,7 @@ async fn main() { println!("Declaring NBFTReliableSubscriber on {}", key_expr); let subscriber = session - .declare_reliable_subscriber(key_expr) + .declare_nbftreliable_subscriber(key_expr) .history(history) .periodic_queries(period) .res() diff --git a/zenoh-ext/src/nbftreliable_publisher.rs b/zenoh-ext/src/nbftreliable_publisher.rs index 3518e62fa5..c86637af98 100644 --- a/zenoh-ext/src/nbftreliable_publisher.rs +++ b/zenoh-ext/src/nbftreliable_publisher.rs @@ -115,7 +115,7 @@ impl<'a> NBFTReliablePublisher<'a> { let prefix = id.into_keyexpr(); let mut builder = conf .session - .declare_reliability_cache(key_expr.into_owned()) + .declare_nbftreliability_cache(key_expr.into_owned()) .subscriber_allowed_origin(Locality::SessionLocal) .history(conf.history) .queryable_prefix(&prefix); diff --git a/zenoh-ext/src/session_ext.rs b/zenoh-ext/src/session_ext.rs index c00ae9910c..7ad1f500bb 100644 --- a/zenoh-ext/src/session_ext.rs +++ b/zenoh-ext/src/session_ext.rs @@ -99,7 +99,7 @@ pub trait SessionExt { >>::Error: Into; #[zenoh_core::unstable] - fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( + fn declare_nbftreliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, ) -> NBFTReliabilityCacheBuilder<'a, 'b, 'c> @@ -108,7 +108,7 @@ pub trait SessionExt { >>::Error: Into; #[zenoh_core::unstable] - fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( + fn declare_nbftreliable_publisher<'a, 'b, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, ) -> NBFTReliablePublisherBuilder<'a, 'b> @@ -144,7 +144,7 @@ impl SessionExt for Session { } #[zenoh_core::unstable] - fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( + fn declare_nbftreliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, ) -> NBFTReliabilityCacheBuilder<'a, 'b, 'c> @@ -156,7 +156,7 @@ impl SessionExt for Session { } #[zenoh_core::unstable] - fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( + fn declare_nbftreliable_publisher<'a, 'b, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, ) -> NBFTReliablePublisherBuilder<'a, 'b> @@ -169,7 +169,7 @@ impl SessionExt for Session { } pub trait ArcSessionExt { #[zenoh_core::unstable] - fn declare_reliable_subscriber<'b, TryIntoKeyExpr>( + fn declare_nbftreliable_subscriber<'b, TryIntoKeyExpr>( &self, sub_key_expr: TryIntoKeyExpr, ) -> NBFTReliableSubscriberBuilder<'b, DefaultHandler> @@ -205,7 +205,7 @@ impl SessionExt for Arc { } #[zenoh_core::unstable] - fn declare_reliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( + fn declare_nbftreliability_cache<'a, 'b, 'c, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, ) -> NBFTReliabilityCacheBuilder<'a, 'b, 'c> @@ -217,7 +217,7 @@ impl SessionExt for Arc { } #[zenoh_core::unstable] - fn declare_reliable_publisher<'a, 'b, TryIntoKeyExpr>( + fn declare_nbftreliable_publisher<'a, 'b, TryIntoKeyExpr>( &'a self, pub_key_expr: TryIntoKeyExpr, ) -> NBFTReliablePublisherBuilder<'a, 'b> @@ -231,7 +231,7 @@ impl SessionExt for Arc { impl ArcSessionExt for Arc { #[zenoh_core::unstable] - fn declare_reliable_subscriber<'b, TryIntoKeyExpr>( + fn declare_nbftreliable_subscriber<'b, TryIntoKeyExpr>( &self, sub_key_expr: TryIntoKeyExpr, ) -> NBFTReliableSubscriberBuilder<'b, DefaultHandler>