From cb3fa548f26bba223ff2f7fee54892b55ed1d318 Mon Sep 17 00:00:00 2001
From: OlivierHecart <olivier@zettascale.tech>
Date: Wed, 11 Dec 2024 11:00:44 +0100
Subject: [PATCH] Add Advanced Pub/Sub feature (#1582)

* Expose and use ke macro

* Fix SourceInfo publication

* Add AdvancedPublisher AdvancedSubscriber and AdvancedSubscriber

* Fix doctests

* Fix doc warnings

* Remove debug trace

* Add history test

* Fix periodic queries

* Remove debug trace

* Lower test debug level

* Add retransmission tests

* Liveliness sub callback shoud increase pending queries counter

* Liveliness sub callback shoud spawn periodic queries when enbaled

* Add late_joiner test

* Only treat pending samples when there are no more pending queries

* Apply proper sequencing for history

* Improve AdvancedSubscriber

* Code reorg

* Code reorg

* Fix deduplication

* Subscribe to liveliness tokens with history

* Update builders

* Add examples

* Fix rustdoc

* Move stuff in State

* Code reorg

* Add smaple_miss_callback

* Add sample miss test

* Update z_advanced_sub example

* Explicit use in examples

* Update API

* Fix rustdoc

* Allow sample miss detection when recovery disabled

* Add miss_sample_callback to DataSubscriberBuilderExt

* Add sample_miss_detection to PublisherBuilderExt

* Add test_advanced_sample_miss test

* Deliver sample even when no miss callback

* Replace sample_miss_callback with sample_miss_listener

* Fix clippy warnings

* Fix tests

* Add HistoryConf max_samples option

* Add HistoryConf max_age option

* Use BTreeMap

* Add meta_keyexpr option

* Add late_joiner_detection and meta_keyexpr options on Subcriber side

* Renaming

* Fix compilation issues

* Remove AdvancedCache from public API

* Update Session admin to match AdvancedSub

* Gather constants

* Fix doc build

* Renaming

* Mark PublicationCache and QueryingSubscriber as deprecated and remove related examples

* Remove z_pub_cache and z_query_sub entries from zenoh-ext examples README

* Add z_advanced_pub and z_advanced_sub to zenoh-ext examples Cargo.toml

* Add CacheConfig replies_qos option

* Call cache directly from publisher

* Update doc

* Add missing unstable tags

* Add missing unstable tags

* Add missing unstable tags

* Add unstable tag everywhere

* Add missing AdvancedSubscriber methods

* Fix WeakSession::Session internal function

* Expose missing SampleMissListener and related structs

* Add AdvancedPublisherBuilderExt::advanced function

* Add missing AdvancedPublisherBuilder functions

* Fix doctests

* Expose Miss struct

* impl QoSBuilderTrait for AdvancedPublisherBuilder

* Propagate PublisherBuilder values to AdvancedPublisherBuilder

* Rename AdvancedSubscriber::close()

* Add unstable tags

* Add AdvancedSubscriber::detect_publishers function

* Remove debug println

* Renaming

* Add unstable tags

* Use std Range

* Spawn Timer in a tokio runtime

* Fix panic when last_delivered is None

* Release lock before calling get

* Update key mapping

* Improve doc

* fix: fix callback API (#1647)

* Update doc

* Fix ke_liveliness

* Fix doc

* Fix doc

---------

Co-authored-by: Joseph Perez <joperez@hotmail.fr>
---
 Cargo.lock                                    |   23 +
 Cargo.toml                                    |    1 +
 commons/zenoh-config/src/wrappers.rs          |   10 +
 commons/zenoh-macros/src/lib.rs               |    2 +-
 zenoh-ext/Cargo.toml                          |    4 +-
 zenoh-ext/examples/Cargo.toml                 |    8 +-
 zenoh-ext/examples/examples/README.md         |   22 +-
 .../{z_pub_cache.rs => z_advanced_pub.rs}     |   51 +-
 zenoh-ext/examples/examples/z_advanced_sub.rs |   84 ++
 zenoh-ext/examples/examples/z_query_sub.rs    |   80 -
 zenoh-ext/src/advanced_cache.rs               |  363 +++++
 zenoh-ext/src/advanced_publisher.rs           |  598 ++++++++
 zenoh-ext/src/advanced_subscriber.rs          | 1324 +++++++++++++++++
 zenoh-ext/src/lib.rs                          |   18 +-
 zenoh-ext/src/publication_cache.rs            |   41 +
 zenoh-ext/src/publisher_ext.rs                |   73 +
 zenoh-ext/src/querying_subscriber.rs          |  130 ++
 zenoh-ext/src/session_ext.rs                  |    8 +
 zenoh-ext/src/subscriber_ext.rs               |   80 +-
 zenoh-ext/tests/advanced.rs                   |  715 +++++++++
 zenoh-ext/tests/liveliness.rs                 |    4 +
 zenoh/Cargo.toml                              |    1 +
 zenoh/src/api/admin.rs                        |   93 +-
 zenoh/src/api/builders/publisher.rs           |   27 +
 zenoh/src/api/builders/sample.rs              |   51 +-
 zenoh/src/api/publisher.rs                    |    5 +
 zenoh/src/api/session.rs                      |   24 +-
 zenoh/src/api/subscriber.rs                   |    5 +
 zenoh/src/lib.rs                              |   17 +-
 29 files changed, 3695 insertions(+), 167 deletions(-)
 rename zenoh-ext/examples/examples/{z_pub_cache.rs => z_advanced_pub.rs} (61%)
 create mode 100644 zenoh-ext/examples/examples/z_advanced_sub.rs
 delete mode 100644 zenoh-ext/examples/examples/z_query_sub.rs
 create mode 100644 zenoh-ext/src/advanced_cache.rs
 create mode 100644 zenoh-ext/src/advanced_publisher.rs
 create mode 100644 zenoh-ext/src/advanced_subscriber.rs
 create mode 100644 zenoh-ext/src/publisher_ext.rs
 create mode 100644 zenoh-ext/tests/advanced.rs

diff --git a/Cargo.lock b/Cargo.lock
index e47d08dd0f..a7198e5862 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3065,6 +3065,26 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "ref-cast"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931"
+dependencies = [
+ "ref-cast-impl",
+]
+
+[[package]]
+name = "ref-cast-impl"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
 [[package]]
 name = "regex"
 version = "1.10.6"
@@ -5071,6 +5091,7 @@ dependencies = [
  "petgraph",
  "phf",
  "rand 0.8.5",
+ "ref-cast",
  "rustc_version 0.4.1",
  "serde",
  "serde_json",
@@ -5201,6 +5222,7 @@ dependencies = [
 name = "zenoh-ext"
 version = "1.0.0-dev"
 dependencies = [
+ "async-trait",
  "bincode",
  "flume",
  "futures",
@@ -5209,6 +5231,7 @@ dependencies = [
  "serde",
  "tokio",
  "tracing",
+ "uhlc",
  "zenoh",
  "zenoh-config",
  "zenoh-macros",
diff --git a/Cargo.toml b/Cargo.toml
index de23d2eea4..b69d42f66e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -136,6 +136,7 @@ quote = "1.0.37"
 rand = { version = "0.8.5", default-features = false } # Default features are disabled due to usage in no_std crates
 rand_chacha = "0.3.1"
 rcgen = "0.13.1"
+ref-cast = "1.0.23"
 regex = "1.10.6"
 ron = "0.8.1"
 ringbuffer-spsc = "0.1.9"
diff --git a/commons/zenoh-config/src/wrappers.rs b/commons/zenoh-config/src/wrappers.rs
index 31afe358e7..759507c770 100644
--- a/commons/zenoh-config/src/wrappers.rs
+++ b/commons/zenoh-config/src/wrappers.rs
@@ -152,6 +152,16 @@ pub struct EntityGlobalId(EntityGlobalIdProto);
 pub type EntityId = u32;
 
 impl EntityGlobalId {
+    /// Creates a new EntityGlobalId.
+    #[zenoh_macros::internal]
+    pub fn new(zid: ZenohId, eid: EntityId) -> Self {
+        EntityGlobalIdProto {
+            zid: zid.into(),
+            eid,
+        }
+        .into()
+    }
+
     /// Returns the [`ZenohId`], i.e. the Zenoh session, this ID is associated to.
     pub fn zid(&self) -> ZenohId {
         self.0.zid.into()
diff --git a/commons/zenoh-macros/src/lib.rs b/commons/zenoh-macros/src/lib.rs
index 2047c424cf..cc6f4a2295 100644
--- a/commons/zenoh-macros/src/lib.rs
+++ b/commons/zenoh-macros/src/lib.rs
@@ -515,7 +515,7 @@ pub fn ke(tokens: TokenStream) -> TokenStream {
     let value: LitStr = syn::parse(tokens).unwrap();
     let ke = value.value();
     match zenoh_keyexpr::keyexpr::new(&ke) {
-        Ok(_) => quote!(unsafe {::zenoh::key_expr::keyexpr::from_str_unchecked(#ke)}).into(),
+        Ok(_) => quote!(unsafe { zenoh::key_expr::keyexpr::from_str_unchecked(#ke)}).into(),
         Err(e) => panic!("{}", e),
     }
 }
diff --git a/zenoh-ext/Cargo.toml b/zenoh-ext/Cargo.toml
index 4de255d0d6..4fdf98b8df 100644
--- a/zenoh-ext/Cargo.toml
+++ b/zenoh-ext/Cargo.toml
@@ -38,6 +38,7 @@ tokio = { workspace = true, features = [
   "macros",
   "io-std",
 ] }
+async-trait = { workspace = true }
 bincode = { workspace = true }
 zenoh-util = { workspace = true }
 flume = { workspace = true }
@@ -45,7 +46,8 @@ futures = { workspace = true }
 tracing = { workspace = true }
 serde = { workspace = true, features = ["default"] }
 leb128 = { workspace = true }
-zenoh = { workspace = true, default-features = false }
+uhlc = { workspace = true }
+zenoh = { workspace = true, features = ["default"] }
 zenoh-macros = { workspace = true }
 
 [dev-dependencies]
diff --git a/zenoh-ext/examples/Cargo.toml b/zenoh-ext/examples/Cargo.toml
index 658eba1065..688c044d67 100644
--- a/zenoh-ext/examples/Cargo.toml
+++ b/zenoh-ext/examples/Cargo.toml
@@ -42,12 +42,12 @@ zenoh-ext = { workspace = true, features = ["unstable"] }
 zenoh-config = { workspace = true }
 
 [[example]]
-name = "z_query_sub"
-path = "examples/z_query_sub.rs"
+name = "z_advanced_pub"
+path = "examples/z_advanced_pub.rs"
 
 [[example]]
-name = "z_pub_cache"
-path = "examples/z_pub_cache.rs"
+name = "z_advanced_sub"
+path = "examples/z_advanced_sub.rs"
 
 [[example]]
 name = "z_member"
diff --git a/zenoh-ext/examples/examples/README.md b/zenoh-ext/examples/examples/README.md
index 498a1ca6fe..b72e50f816 100644
--- a/zenoh-ext/examples/examples/README.md
+++ b/zenoh-ext/examples/examples/README.md
@@ -15,31 +15,33 @@
 
 ## Examples description
 
-### z_pub_cache
+### z_advanced_pub
 
-   Declares a publisher and an associated publication cache with a given key expression.  
-   All the publications are locally cached (with a configurable history size - i.e. max number of cached data per resource). The cache can be queried by a QueryingSubscriber at startup (see next example).
+   Declares an AdvancedPublisher with a given key expression.  
+   All the publications are locally cached (with a configurable history size - i.e. max number of cached data per resource, default 1). The cache can be queried by an AdvancedSubscriber for hsitory
+   or retransmission.
 
    Typical usage:
    ```bash
-      z_pub_cache
+      z_advanced_pub
    ```
    or
    ```bash
-      z_pub_cache --history 10
+      z_advanced_pub --history 10
    ```
 
-### z_query_sub
+### z_advanced_sub
 
-   Declares a querying subscriber with a selector.  
-   At startup, the subscriber issuez a query (by default on the same selector than the subscription) and merge/sort/de-duplicate the query results with the publications received in parallel.
+   Declares an AdvancedSubscriber with a given key expression.  
+   The AdvancedSubscriber can query for AdvancedPublisher history at startup
+   and on late joiner publisher detection. The AdvancedSubscriber can detect
+   sample loss and ask for retransmission.
 
    Typical usage:
    ```bash
-      z_query_sub
+      z_advanced_sub
    ```
 
-
 ### z_member
 
    Group Management example: join a group and display the received group events (Join, Leave, LeaseExpired), as well as an updated group view.
diff --git a/zenoh-ext/examples/examples/z_pub_cache.rs b/zenoh-ext/examples/examples/z_advanced_pub.rs
similarity index 61%
rename from zenoh-ext/examples/examples/z_pub_cache.rs
rename to zenoh-ext/examples/examples/z_advanced_pub.rs
index d8e13faec4..6437515c8f 100644
--- a/zenoh-ext/examples/examples/z_pub_cache.rs
+++ b/zenoh-ext/examples/examples/z_advanced_pub.rs
@@ -1,3 +1,5 @@
+use std::time::Duration;
+
 //
 // Copyright (c) 2023 ZettaScale Technology
 //
@@ -11,12 +13,10 @@
 // Contributors:
 //   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
 //
-use std::time::Duration;
-
 use clap::{arg, Parser};
 use zenoh::{config::Config, key_expr::KeyExpr};
 use zenoh_config::ModeDependentValue;
-use zenoh_ext::*;
+use zenoh_ext::{AdvancedPublisherBuilderExt, CacheConfig};
 use zenoh_ext_examples::CommonArgs;
 
 #[tokio::main]
@@ -24,27 +24,26 @@ async fn main() {
     // Initiate logging
     zenoh::init_log_from_env_or("error");
 
-    let (config, key_expr, value, history, prefix, complete) = parse_args();
+    let (config, key_expr, value, history) = parse_args();
 
     println!("Opening session...");
     let session = zenoh::open(config).await.unwrap();
 
-    println!("Declaring PublicationCache on {}", &key_expr);
-    let mut publication_cache_builder = session
-        .declare_publication_cache(&key_expr)
-        .history(history)
-        .queryable_complete(complete);
-    if let Some(prefix) = prefix {
-        publication_cache_builder = publication_cache_builder.queryable_prefix(prefix);
-    }
-    let _publication_cache = publication_cache_builder.await.unwrap();
+    println!("Declaring AdvancedPublisher on {}", &key_expr);
+    let publisher = session
+        .declare_publisher(&key_expr)
+        .cache(CacheConfig::default().max_samples(history))
+        .sample_miss_detection()
+        .publisher_detection()
+        .await
+        .unwrap();
 
     println!("Press CTRL-C to quit...");
     for idx in 0..u32::MAX {
         tokio::time::sleep(Duration::from_secs(1)).await;
         let buf = format!("[{idx:4}] {value}");
         println!("Put Data ('{}': '{}')", &key_expr, buf);
-        session.put(&key_expr, buf).await.unwrap();
+        publisher.put(buf).await.unwrap();
     }
 }
 
@@ -59,36 +58,16 @@ struct Args {
     #[arg(short = 'i', long, default_value = "1")]
     /// The number of publications to keep in cache.
     history: usize,
-    #[arg(short = 'o', long)]
-    /// Set `complete` option to true. This means that this queryable is ultimate data source, no need to scan other queryables.
-    complete: bool,
-    #[arg(short = 'x', long)]
-    /// An optional queryable prefix.
-    prefix: Option<String>,
     #[command(flatten)]
     common: CommonArgs,
 }
 
-fn parse_args() -> (
-    Config,
-    KeyExpr<'static>,
-    String,
-    usize,
-    Option<String>,
-    bool,
-) {
+fn parse_args() -> (Config, KeyExpr<'static>, String, usize) {
     let args = Args::parse();
     let mut config: Config = args.common.into();
     config
         .timestamping
         .set_enabled(Some(ModeDependentValue::Unique(true)))
         .unwrap();
-    (
-        config,
-        args.key,
-        args.value,
-        args.history,
-        args.prefix,
-        args.complete,
-    )
+    (config, args.key, args.value, args.history)
 }
diff --git a/zenoh-ext/examples/examples/z_advanced_sub.rs b/zenoh-ext/examples/examples/z_advanced_sub.rs
new file mode 100644
index 0000000000..5bea70f7d5
--- /dev/null
+++ b/zenoh-ext/examples/examples/z_advanced_sub.rs
@@ -0,0 +1,84 @@
+//
+// Copyright (c) 2023 ZettaScale Technology
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License 2.0 which is available at
+// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+//
+// Contributors:
+//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
+//
+use std::time::Duration;
+
+use clap::{arg, Parser};
+use zenoh::config::Config;
+use zenoh_ext::{AdvancedSubscriberBuilderExt, HistoryConfig, RecoveryConfig};
+use zenoh_ext_examples::CommonArgs;
+
+#[tokio::main]
+async fn main() {
+    // Initiate logging
+    zenoh::init_log_from_env_or("error");
+
+    let (config, key_expr) = parse_args();
+
+    println!("Opening session...");
+    let session = zenoh::open(config).await.unwrap();
+
+    println!("Declaring AdvancedSubscriber on {}", key_expr);
+    let subscriber = session
+        .declare_subscriber(key_expr)
+        .history(HistoryConfig::default().detect_late_publishers())
+        .recovery(RecoveryConfig::default().periodic_queries(Some(Duration::from_secs(1))))
+        .subscriber_detection()
+        .await
+        .unwrap();
+
+    let miss_listener = subscriber.sample_miss_listener().await.unwrap();
+
+    println!("Press CTRL-C to quit...");
+    loop {
+        tokio::select! {
+            sample = subscriber.recv_async() => {
+                if let Ok(sample) = sample {
+                    let payload = sample
+                        .payload()
+                        .try_to_string()
+                        .unwrap_or_else(|e| e.to_string().into());
+                    println!(
+                        ">> [Subscriber] Received {} ('{}': '{}')",
+                        sample.kind(),
+                        sample.key_expr().as_str(),
+                        payload
+                    );
+                }
+            },
+            miss = miss_listener.recv_async() => {
+                if let Ok(miss) = miss {
+                    println!(
+                        ">> [Subscriber] Missed {} samples from {:?} !!!",
+                        miss.nb(),
+                        miss.source()
+                    );
+                }
+            },
+        }
+    }
+}
+
+#[derive(clap::Parser, Clone, PartialEq, Eq, Hash, Debug)]
+struct Args {
+    #[arg(short, long, default_value = "demo/example/**")]
+    /// The key expression to subscribe onto.
+    key: String,
+    #[command(flatten)]
+    common: CommonArgs,
+}
+
+fn parse_args() -> (Config, String) {
+    let args = Args::parse();
+    (args.common.into(), args.key)
+}
diff --git a/zenoh-ext/examples/examples/z_query_sub.rs b/zenoh-ext/examples/examples/z_query_sub.rs
deleted file mode 100644
index 2e8c832670..0000000000
--- a/zenoh-ext/examples/examples/z_query_sub.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-// Copyright (c) 2023 ZettaScale Technology
-//
-// This program and the accompanying materials are made available under the
-// terms of the Eclipse Public License 2.0 which is available at
-// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
-// which is available at https://www.apache.org/licenses/LICENSE-2.0.
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-//
-// Contributors:
-//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
-//
-use clap::{arg, Parser};
-use zenoh::{config::Config, query::ReplyKeyExpr};
-use zenoh_ext::*;
-use zenoh_ext_examples::CommonArgs;
-
-#[tokio::main]
-async fn main() {
-    // Initiate logging
-    zenoh::init_log_from_env_or("error");
-
-    let (config, key_expr, query) = parse_args();
-
-    println!("Opening session...");
-    let session = zenoh::open(config).await.unwrap();
-
-    println!(
-        "Declaring QueryingSubscriber on {} with an initial query on {}",
-        key_expr,
-        query.as_ref().unwrap_or(&key_expr)
-    );
-    let subscriber = if let Some(selector) = query {
-        session
-            .declare_subscriber(key_expr)
-            .querying()
-            .query_selector(&selector)
-            .query_accept_replies(ReplyKeyExpr::Any)
-            .await
-            .unwrap()
-    } else {
-        session
-            .declare_subscriber(key_expr)
-            .querying()
-            .await
-            .unwrap()
-    };
-
-    println!("Press CTRL-C to quit...");
-    while let Ok(sample) = subscriber.recv_async().await {
-        let payload = sample
-            .payload()
-            .try_to_string()
-            .unwrap_or_else(|e| e.to_string().into());
-        println!(
-            ">> [Subscriber] Received {} ('{}': '{}')",
-            sample.kind(),
-            sample.key_expr().as_str(),
-            payload
-        );
-    }
-}
-
-#[derive(clap::Parser, Clone, PartialEq, Eq, Hash, Debug)]
-struct Args {
-    #[arg(short, long, default_value = "demo/example/**")]
-    /// The key expression to subscribe onto.
-    key: String,
-    #[arg(short, long)]
-    /// The selector to use for queries (by default it's same as 'key' option)
-    query: Option<String>,
-    #[command(flatten)]
-    common: CommonArgs,
-}
-
-fn parse_args() -> (Config, String, Option<String>) {
-    let args = Args::parse();
-    (args.common.into(), args.key, args.query)
-}
diff --git a/zenoh-ext/src/advanced_cache.rs b/zenoh-ext/src/advanced_cache.rs
new file mode 100644
index 0000000000..c18add5a59
--- /dev/null
+++ b/zenoh-ext/src/advanced_cache.rs
@@ -0,0 +1,363 @@
+//
+// 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, <zenoh@zettascale.tech>
+//
+use std::{
+    collections::VecDeque,
+    future::{IntoFuture, Ready},
+    ops::{Bound, RangeBounds},
+    sync::{Arc, RwLock},
+};
+
+use zenoh::{
+    internal::{bail, traits::QoSBuilderTrait},
+    key_expr::{
+        format::{ke, kedefine},
+        keyexpr, KeyExpr,
+    },
+    liveliness::LivelinessToken,
+    qos::{CongestionControl, Priority},
+    query::{Queryable, ZenohParameters},
+    sample::{Locality, Sample, SampleBuilder},
+    Resolvable, Result as ZResult, Session, Wait, KE_ADV_PREFIX, KE_AT, KE_STARSTAR,
+};
+
+pub(crate) static KE_UHLC: &keyexpr = ke!("uhlc");
+#[zenoh_macros::unstable]
+kedefine!(
+    pub(crate) ke_liveliness: "@adv/${entity:*}/${zid:*}/${eid:*}/${meta:**}/@/${remaining:**}",
+);
+
+#[zenoh_macros::unstable]
+/// Configure replies.
+#[derive(Clone, Debug)]
+pub struct RepliesConfig {
+    priority: Priority,
+    congestion_control: CongestionControl,
+    is_express: bool,
+}
+
+#[zenoh_macros::unstable]
+impl Default for RepliesConfig {
+    fn default() -> Self {
+        Self {
+            priority: Priority::Data,
+            congestion_control: CongestionControl::Block,
+            is_express: false,
+        }
+    }
+}
+
+#[zenoh_macros::internal_trait]
+#[zenoh_macros::unstable]
+impl QoSBuilderTrait for RepliesConfig {
+    #[allow(unused_mut)]
+    #[zenoh_macros::unstable]
+    fn congestion_control(mut self, congestion_control: CongestionControl) -> Self {
+        self.congestion_control = congestion_control;
+        self
+    }
+
+    #[allow(unused_mut)]
+    #[zenoh_macros::unstable]
+    fn priority(mut self, priority: Priority) -> Self {
+        self.priority = priority;
+        self
+    }
+
+    #[allow(unused_mut)]
+    #[zenoh_macros::unstable]
+    fn express(mut self, is_express: bool) -> Self {
+        self.is_express = is_express;
+        self
+    }
+}
+
+#[derive(Debug, Clone)]
+/// Configure an [`AdvancedPublisher`](crate::AdvancedPublisher) cache.
+#[zenoh_macros::unstable]
+pub struct CacheConfig {
+    max_samples: usize,
+    replies_config: RepliesConfig,
+}
+
+#[zenoh_macros::unstable]
+impl Default for CacheConfig {
+    fn default() -> Self {
+        Self {
+            max_samples: 1,
+            replies_config: RepliesConfig::default(),
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+impl CacheConfig {
+    /// Specify how many samples to keep for each resource.
+    #[zenoh_macros::unstable]
+    pub fn max_samples(mut self, depth: usize) -> Self {
+        self.max_samples = depth;
+        self
+    }
+
+    /// The QoS to apply to replies.
+    #[zenoh_macros::unstable]
+    pub fn replies_config(mut self, qos: RepliesConfig) -> Self {
+        self.replies_config = qos;
+        self
+    }
+}
+
+/// The builder of an [`AdvancedCache`], allowing to configure it.
+#[zenoh_macros::unstable]
+pub struct AdvancedCacheBuilder<'a, 'b, 'c> {
+    session: &'a Session,
+    pub_key_expr: ZResult<KeyExpr<'b>>,
+    queryable_prefix: Option<ZResult<KeyExpr<'c>>>,
+    queryable_origin: Locality,
+    history: CacheConfig,
+    liveliness: bool,
+}
+
+#[zenoh_macros::unstable]
+impl<'a, 'b, 'c> AdvancedCacheBuilder<'a, 'b, 'c> {
+    #[zenoh_macros::unstable]
+    pub(crate) fn new(
+        session: &'a Session,
+        pub_key_expr: ZResult<KeyExpr<'b>>,
+    ) -> AdvancedCacheBuilder<'a, 'b, 'c> {
+        AdvancedCacheBuilder {
+            session,
+            pub_key_expr,
+            queryable_prefix: Some(Ok((KE_ADV_PREFIX / KE_STARSTAR / KE_AT).into())),
+            queryable_origin: Locality::default(),
+            history: CacheConfig::default(),
+            liveliness: false,
+        }
+    }
+
+    /// Change the prefix used for queryable.
+    #[zenoh_macros::unstable]
+    pub fn queryable_prefix<TryIntoKeyExpr>(mut self, queryable_prefix: TryIntoKeyExpr) -> Self
+    where
+        TryIntoKeyExpr: TryInto<KeyExpr<'c>>,
+        <TryIntoKeyExpr as TryInto<KeyExpr<'c>>>::Error: Into<zenoh::Error>,
+    {
+        self.queryable_prefix = Some(queryable_prefix.try_into().map_err(Into::into));
+        self
+    }
+
+    /// Change the history size for each resource.
+    #[zenoh_macros::unstable]
+    pub fn history(mut self, history: CacheConfig) -> Self {
+        self.history = history;
+        self
+    }
+}
+
+#[zenoh_macros::unstable]
+impl Resolvable for AdvancedCacheBuilder<'_, '_, '_> {
+    type To = ZResult<AdvancedCache>;
+}
+
+#[zenoh_macros::unstable]
+impl Wait for AdvancedCacheBuilder<'_, '_, '_> {
+    fn wait(self) -> <Self as Resolvable>::To {
+        AdvancedCache::new(self)
+    }
+}
+
+#[zenoh_macros::unstable]
+impl IntoFuture for AdvancedCacheBuilder<'_, '_, '_> {
+    type Output = <Self as Resolvable>::To;
+    type IntoFuture = Ready<<Self as Resolvable>::To>;
+
+    #[zenoh_macros::unstable]
+    fn into_future(self) -> Self::IntoFuture {
+        std::future::ready(self.wait())
+    }
+}
+
+#[zenoh_macros::unstable]
+fn decode_range(range: &str) -> (Bound<u32>, Bound<u32>) {
+    let mut split = range.split("..");
+    let start = split
+        .next()
+        .and_then(|s| s.parse::<u32>().ok().map(Bound::Included))
+        .unwrap_or(Bound::Unbounded);
+    let end = split
+        .next()
+        .map(|s| {
+            s.parse::<u32>()
+                .ok()
+                .map(Bound::Included)
+                .unwrap_or(Bound::Unbounded)
+        })
+        .unwrap_or(start);
+    (start, end)
+}
+
+/// [`AdvancedCache`].
+#[zenoh_macros::unstable]
+pub struct AdvancedCache {
+    cache: Arc<RwLock<VecDeque<Sample>>>,
+    max_samples: usize,
+    _queryable: Queryable<()>,
+    _token: Option<LivelinessToken>,
+}
+
+#[zenoh_macros::unstable]
+impl AdvancedCache {
+    #[zenoh_macros::unstable]
+    fn new(conf: AdvancedCacheBuilder<'_, '_, '_>) -> ZResult<AdvancedCache> {
+        let key_expr = conf.pub_key_expr?.into_owned();
+        // the queryable_prefix (optional), and the key_expr for AdvancedCache's queryable ("[<queryable_prefix>]/<pub_key_expr>")
+        let queryable_key_expr = match conf.queryable_prefix {
+            None => key_expr.clone(),
+            Some(Ok(ke)) => (&ke) / &key_expr,
+            Some(Err(e)) => bail!("Invalid key expression for queryable_prefix: {}", e),
+        };
+        tracing::debug!(
+            "Create AdvancedCache on {} with max_samples={:?}",
+            &key_expr,
+            conf.history,
+        );
+        let cache = Arc::new(RwLock::new(VecDeque::<Sample>::new()));
+
+        // declare the queryable that will answer to queries on cache
+        let queryable = conf
+            .session
+            .declare_queryable(&queryable_key_expr)
+            .allowed_origin(conf.queryable_origin)
+            .callback({
+                let cache = cache.clone();
+                move |query| {
+                    let range = query
+                        .parameters()
+                        .get("_sn")
+                        .map(decode_range)
+                        .unwrap_or((Bound::Unbounded, Bound::Unbounded));
+                    let max = query
+                        .parameters()
+                        .get("_max")
+                        .and_then(|s| s.parse::<u32>().ok());
+                    if let Ok(queue) = cache.read() {
+                        if let Some(max) = max {
+                            let mut samples = VecDeque::new();
+                            for sample in queue.iter() {
+                                if range == (Bound::Unbounded, Bound::Unbounded)
+                                    || sample
+                                        .source_info()
+                                        .source_sn()
+                                        .is_some_and(|sn| range.contains(&sn))
+                                {
+                                    if let (Some(Ok(time_range)), Some(timestamp)) =
+                                        (query.parameters().time_range(), sample.timestamp())
+                                    {
+                                        if !time_range
+                                            .contains(timestamp.get_time().to_system_time())
+                                        {
+                                            continue;
+                                        }
+                                    }
+                                    samples.push_front(sample);
+                                    samples.truncate(max as usize);
+                                }
+                            }
+                            for sample in samples.drain(..).rev() {
+                                if let Err(e) = query
+                                    .reply_sample(
+                                        SampleBuilder::from(sample.clone())
+                                            .congestion_control(
+                                                conf.history.replies_config.congestion_control,
+                                            )
+                                            .priority(conf.history.replies_config.priority)
+                                            .express(conf.history.replies_config.is_express)
+                                            .into(),
+                                    )
+                                    .wait()
+                                {
+                                    tracing::warn!("Error replying to query: {}", e);
+                                }
+                            }
+                        } else {
+                            for sample in queue.iter() {
+                                if range == (Bound::Unbounded, Bound::Unbounded)
+                                    || sample
+                                        .source_info()
+                                        .source_sn()
+                                        .is_some_and(|sn| range.contains(&sn))
+                                {
+                                    if let (Some(Ok(time_range)), Some(timestamp)) =
+                                        (query.parameters().time_range(), sample.timestamp())
+                                    {
+                                        if !time_range
+                                            .contains(timestamp.get_time().to_system_time())
+                                        {
+                                            continue;
+                                        }
+                                    }
+                                    if let Err(e) = query
+                                        .reply_sample(
+                                            SampleBuilder::from(sample.clone())
+                                                .congestion_control(
+                                                    conf.history.replies_config.congestion_control,
+                                                )
+                                                .priority(conf.history.replies_config.priority)
+                                                .express(conf.history.replies_config.is_express)
+                                                .into(),
+                                        )
+                                        .wait()
+                                    {
+                                        tracing::warn!("Error replying to query: {}", e);
+                                    }
+                                }
+                            }
+                        }
+                    } else {
+                        tracing::error!("Unable to take AdvancedPublisher cache read lock");
+                    }
+                }
+            })
+            .wait()?;
+
+        let token = if conf.liveliness {
+            Some(
+                conf.session
+                    .liveliness()
+                    .declare_token(queryable_key_expr)
+                    .wait()?,
+            )
+        } else {
+            None
+        };
+
+        Ok(AdvancedCache {
+            cache,
+            max_samples: conf.history.max_samples,
+            _queryable: queryable,
+            _token: token,
+        })
+    }
+
+    #[zenoh_macros::unstable]
+    pub(crate) fn cache_sample(&self, sample: Sample) {
+        if let Ok(mut queue) = self.cache.write() {
+            if queue.len() >= self.max_samples {
+                queue.pop_front();
+            }
+            queue.push_back(sample);
+        } else {
+            tracing::error!("Unable to take AdvancedPublisher cache write lock");
+        }
+    }
+}
diff --git a/zenoh-ext/src/advanced_publisher.rs b/zenoh-ext/src/advanced_publisher.rs
new file mode 100644
index 0000000000..023e509d57
--- /dev/null
+++ b/zenoh-ext/src/advanced_publisher.rs
@@ -0,0 +1,598 @@
+//
+// Copyright (c) 2023 ZettaScale Technology
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License 2.0 which is available at
+// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+//
+// Contributors:
+//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
+//
+use std::{
+    future::{IntoFuture, Ready},
+    sync::atomic::{AtomicU32, Ordering},
+};
+
+use zenoh::{
+    bytes::{Encoding, OptionZBytes, ZBytes},
+    internal::{
+        bail,
+        traits::{
+            EncodingBuilderTrait, QoSBuilderTrait, SampleBuilderTrait, TimestampBuilderTrait,
+        },
+    },
+    key_expr::{keyexpr, KeyExpr},
+    liveliness::LivelinessToken,
+    pubsub::{
+        PublicationBuilder, PublicationBuilderDelete, PublicationBuilderPut, Publisher,
+        PublisherBuilder,
+    },
+    qos::{CongestionControl, Priority, Reliability},
+    sample::{Locality, SourceInfo},
+    session::EntityGlobalId,
+    Resolvable, Resolve, Result as ZResult, Session, Wait, KE_ADV_PREFIX, KE_AT, KE_EMPTY,
+};
+use zenoh_macros::ke;
+
+use crate::advanced_cache::{AdvancedCache, AdvancedCacheBuilder, CacheConfig, KE_UHLC};
+
+pub(crate) static KE_PUB: &keyexpr = ke!("pub");
+
+#[derive(PartialEq)]
+#[zenoh_macros::unstable]
+pub(crate) enum Sequencing {
+    None,
+    Timestamp,
+    SequenceNumber,
+}
+
+/// The builder of PublicationCache, allowing to configure it.
+#[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"]
+#[zenoh_macros::unstable]
+pub struct AdvancedPublisherBuilder<'a, 'b, 'c> {
+    session: &'a Session,
+    pub_key_expr: ZResult<KeyExpr<'b>>,
+    encoding: Encoding,
+    destination: Locality,
+    reliability: Reliability,
+    congestion_control: CongestionControl,
+    priority: Priority,
+    is_express: bool,
+    meta_key_expr: Option<ZResult<KeyExpr<'c>>>,
+    sequencing: Sequencing,
+    liveliness: bool,
+    cache: bool,
+    history: CacheConfig,
+}
+
+#[zenoh_macros::unstable]
+impl<'a, 'b, 'c> AdvancedPublisherBuilder<'a, 'b, 'c> {
+    #[zenoh_macros::unstable]
+    pub(crate) fn new(builder: PublisherBuilder<'a, 'b>) -> AdvancedPublisherBuilder<'a, 'b, 'c> {
+        AdvancedPublisherBuilder {
+            session: builder.session,
+            pub_key_expr: builder.key_expr,
+            encoding: builder.encoding,
+            destination: builder.destination,
+            reliability: builder.reliability,
+            congestion_control: builder.congestion_control,
+            priority: builder.priority,
+            is_express: builder.is_express,
+            meta_key_expr: None,
+            sequencing: Sequencing::None,
+            liveliness: false,
+            cache: false,
+            history: CacheConfig::default(),
+        }
+    }
+
+    /// Changes the [`zenoh::sample::Locality`] applied when routing the data.
+    ///
+    /// This restricts the matching subscribers that will receive the published data to the ones
+    /// that have the given [`zenoh::sample::Locality`].
+    #[zenoh_macros::unstable]
+    #[inline]
+    pub fn allowed_destination(mut self, destination: Locality) -> Self {
+        self.destination = destination;
+        self
+    }
+
+    /// Changes the [`zenoh::qos::Reliability`] to apply when routing the data.
+    ///
+    /// **NOTE**: Currently `reliability` does not trigger any data retransmission on the wire. It
+    ///   is rather used as a marker on the wire and it may be used to select the best link
+    ///   available (e.g. TCP for reliable data and UDP for best effort data).
+    #[zenoh_macros::unstable]
+    #[inline]
+    pub fn reliability(self, reliability: Reliability) -> Self {
+        Self {
+            reliability,
+            ..self
+        }
+    }
+
+    /// Allow matching [`AdvancedSubscribers`](crate::AdvancedSubscriber) to detect lost samples and optionally ask for retransimission.
+    ///
+    /// Retransmission can only be achieved if [`cache`](crate::AdvancedPublisherBuilder::cache) is enabled.
+    #[zenoh_macros::unstable]
+    pub fn sample_miss_detection(mut self) -> Self {
+        self.sequencing = Sequencing::SequenceNumber;
+        self
+    }
+
+    /// Attach a cache to this [`AdvancedPublisher`].
+    ///
+    /// The cache can be used for history and/or recovery.
+    #[zenoh_macros::unstable]
+    pub fn cache(mut self, config: CacheConfig) -> Self {
+        self.cache = true;
+        if self.sequencing == Sequencing::None {
+            self.sequencing = Sequencing::Timestamp;
+        }
+        self.history = config;
+        self
+    }
+
+    /// Allow this [`AdvancedPublisher`] to be detected by [`AdvancedSubscribers`](crate::AdvancedSubscriber).
+    ///
+    /// This allows [`AdvancedSubscribers`](crate::AdvancedSubscriber) to retrieve the local history.
+    #[zenoh_macros::unstable]
+    pub fn publisher_detection(mut self) -> Self {
+        self.liveliness = true;
+        self
+    }
+
+    /// A key expression added to the liveliness token key expression
+    /// and to the cache queryable key expression.
+    /// It can be used to convey meta data.
+    #[zenoh_macros::unstable]
+    pub fn publisher_detection_metadata<TryIntoKeyExpr>(mut self, meta: TryIntoKeyExpr) -> Self
+    where
+        TryIntoKeyExpr: TryInto<KeyExpr<'c>>,
+        <TryIntoKeyExpr as TryInto<KeyExpr<'c>>>::Error: Into<zenoh::Error>,
+    {
+        self.meta_key_expr = Some(meta.try_into().map_err(Into::into));
+        self
+    }
+}
+
+#[zenoh_macros::internal_trait]
+#[zenoh_macros::unstable]
+impl EncodingBuilderTrait for AdvancedPublisherBuilder<'_, '_, '_> {
+    #[zenoh_macros::unstable]
+    fn encoding<T: Into<Encoding>>(self, encoding: T) -> Self {
+        Self {
+            encoding: encoding.into(),
+            ..self
+        }
+    }
+}
+
+#[zenoh_macros::internal_trait]
+#[zenoh_macros::unstable]
+impl QoSBuilderTrait for AdvancedPublisherBuilder<'_, '_, '_> {
+    /// Changes the [`zenoh::qos::CongestionControl`] to apply when routing the data.
+    #[inline]
+    #[zenoh_macros::unstable]
+    fn congestion_control(self, congestion_control: CongestionControl) -> Self {
+        Self {
+            congestion_control,
+            ..self
+        }
+    }
+
+    /// Changes the [`zenoh::qos::Priority`] of the written data.
+    #[inline]
+    #[zenoh_macros::unstable]
+    fn priority(self, priority: Priority) -> Self {
+        Self { priority, ..self }
+    }
+
+    /// Changes the Express policy to apply when routing the data.
+    ///
+    /// When express is set to `true`, then the message will not be batched.
+    /// This usually has a positive impact on latency but negative impact on throughput.
+    #[inline]
+    #[zenoh_macros::unstable]
+    fn express(self, is_express: bool) -> Self {
+        Self { is_express, ..self }
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<'a> Resolvable for AdvancedPublisherBuilder<'a, '_, '_> {
+    type To = ZResult<AdvancedPublisher<'a>>;
+}
+
+#[zenoh_macros::unstable]
+impl Wait for AdvancedPublisherBuilder<'_, '_, '_> {
+    #[zenoh_macros::unstable]
+    fn wait(self) -> <Self as Resolvable>::To {
+        AdvancedPublisher::new(self)
+    }
+}
+
+#[zenoh_macros::unstable]
+impl IntoFuture for AdvancedPublisherBuilder<'_, '_, '_> {
+    type Output = <Self as Resolvable>::To;
+    type IntoFuture = Ready<<Self as Resolvable>::To>;
+
+    #[zenoh_macros::unstable]
+    fn into_future(self) -> Self::IntoFuture {
+        std::future::ready(self.wait())
+    }
+}
+
+/// [`AdvancedPublisher`].
+#[zenoh_macros::unstable]
+pub struct AdvancedPublisher<'a> {
+    publisher: Publisher<'a>,
+    seqnum: Option<AtomicU32>,
+    cache: Option<AdvancedCache>,
+    _token: Option<LivelinessToken>,
+}
+
+#[zenoh_macros::unstable]
+impl<'a> AdvancedPublisher<'a> {
+    #[zenoh_macros::unstable]
+    fn new(conf: AdvancedPublisherBuilder<'a, '_, '_>) -> ZResult<Self> {
+        let key_expr = conf.pub_key_expr?;
+        let meta = match conf.meta_key_expr {
+            Some(meta) => Some(meta?),
+            None => None,
+        };
+
+        let publisher = conf
+            .session
+            .declare_publisher(key_expr.clone().into_owned())
+            .encoding(conf.encoding)
+            .allowed_destination(conf.destination)
+            .reliability(conf.reliability)
+            .congestion_control(conf.congestion_control)
+            .priority(conf.priority)
+            .express(conf.is_express)
+            .wait()?;
+        let id = publisher.id();
+        let prefix = KE_ADV_PREFIX / KE_PUB / &id.zid().into_keyexpr();
+        let prefix = match conf.sequencing {
+            Sequencing::SequenceNumber => {
+                prefix / &KeyExpr::try_from(id.eid().to_string()).unwrap()
+            }
+            _ => prefix / KE_UHLC,
+        };
+        let prefix = match meta {
+            Some(meta) => prefix / &meta / KE_AT,
+            // We need this empty chunk because af a routing matching bug
+            _ => prefix / KE_EMPTY / KE_AT,
+        };
+
+        let seqnum = match conf.sequencing {
+            Sequencing::SequenceNumber => Some(AtomicU32::new(0)),
+            Sequencing::Timestamp => {
+                if conf.session.hlc().is_none() {
+                    bail!(
+                        "Cannot create AdvancedPublisher {} with Sequencing::Timestamp: \
+                            the 'timestamping' setting must be enabled in the Zenoh configuration.",
+                        key_expr,
+                    )
+                }
+                None
+            }
+            _ => None,
+        };
+
+        let cache = if conf.cache {
+            Some(
+                AdvancedCacheBuilder::new(conf.session, Ok(key_expr.clone().into_owned()))
+                    .history(conf.history)
+                    .queryable_prefix(&prefix)
+                    .wait()?,
+            )
+        } else {
+            None
+        };
+
+        let token = if conf.liveliness {
+            Some(
+                conf.session
+                    .liveliness()
+                    .declare_token(prefix / &key_expr)
+                    .wait()?,
+            )
+        } else {
+            None
+        };
+
+        Ok(AdvancedPublisher {
+            publisher,
+            seqnum,
+            cache,
+            _token: token,
+        })
+    }
+
+    /// Returns the [`EntityGlobalId`] of this Publisher.
+    #[zenoh_macros::unstable]
+    pub fn id(&self) -> EntityGlobalId {
+        self.publisher.id()
+    }
+
+    /// Returns the [`KeyExpr`] of this Publisher.
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn key_expr(&self) -> &KeyExpr<'a> {
+        self.publisher.key_expr()
+    }
+
+    /// Get the [`Encoding`] used when publishing data.
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn encoding(&self) -> &Encoding {
+        self.publisher.encoding()
+    }
+
+    /// Get the `congestion_control` applied when routing the data.
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn congestion_control(&self) -> CongestionControl {
+        self.publisher.congestion_control()
+    }
+
+    /// Get the priority of the written data.
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn priority(&self) -> Priority {
+        self.publisher.priority()
+    }
+
+    /// Put data.
+    ///
+    /// # Examples
+    /// ```
+    /// # #[tokio::main]
+    /// # async fn main() {
+    /// use zenoh_ext::AdvancedPublisherBuilderExt;
+    ///
+    /// let session = zenoh::open(zenoh::Config::default()).await.unwrap();
+    /// let publisher = session.declare_publisher("key/expression").advanced().await.unwrap();
+    /// publisher.put("value").await.unwrap();
+    /// # }
+    /// ```
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn put<IntoZBytes>(&self, payload: IntoZBytes) -> AdvancedPublisherPutBuilder<'_>
+    where
+        IntoZBytes: Into<ZBytes>,
+    {
+        let mut builder = self.publisher.put(payload);
+        if let Some(seqnum) = &self.seqnum {
+            builder = builder.source_info(SourceInfo::new(
+                Some(self.publisher.id()),
+                Some(seqnum.fetch_add(1, Ordering::Relaxed)),
+            ));
+        }
+        if let Some(hlc) = self.publisher.session().hlc() {
+            builder = builder.timestamp(hlc.new_timestamp());
+        }
+        AdvancedPublisherPutBuilder {
+            builder,
+            cache: self.cache.as_ref(),
+        }
+    }
+
+    /// Delete data.
+    ///
+    /// # Examples
+    /// ```
+    /// # #[tokio::main]
+    /// # async fn main() {
+    /// use zenoh_ext::AdvancedPublisherBuilderExt;
+    ///
+    /// let session = zenoh::open(zenoh::Config::default()).await.unwrap();
+    /// let publisher = session.declare_publisher("key/expression").advanced().await.unwrap();
+    /// publisher.delete().await.unwrap();
+    /// # }
+    /// ```
+    #[zenoh_macros::unstable]
+    pub fn delete(&self) -> AdvancedPublisherDeleteBuilder<'_> {
+        let mut builder = self.publisher.delete();
+        if let Some(seqnum) = &self.seqnum {
+            builder = builder.source_info(SourceInfo::new(
+                Some(self.publisher.id()),
+                Some(seqnum.fetch_add(1, Ordering::Relaxed)),
+            ));
+        }
+        if let Some(hlc) = self.publisher.session().hlc() {
+            builder = builder.timestamp(hlc.new_timestamp());
+        }
+        AdvancedPublisherDeleteBuilder {
+            builder,
+            cache: self.cache.as_ref(),
+        }
+    }
+
+    /// Return the [`MatchingStatus`](zenoh::matching::MatchingStatus) of the publisher.
+    ///
+    /// [`MatchingStatus::matching`](zenoh::matching::MatchingStatus::matching)
+    /// will return true if there exist Subscribers matching the Publisher's key expression and false otherwise.
+    ///
+    /// # Examples
+    /// ```
+    /// # #[tokio::main]
+    /// # async fn main() {
+    /// use zenoh_ext::AdvancedPublisherBuilderExt;
+    ///
+    /// let session = zenoh::open(zenoh::Config::default()).await.unwrap();
+    /// let publisher = session.declare_publisher("key/expression").advanced().await.unwrap();
+    /// let matching_subscribers: bool = publisher
+    ///     .matching_status()
+    ///     .await
+    ///     .unwrap()
+    ///     .matching();
+    /// # }
+    /// ```
+    #[zenoh_macros::unstable]
+    pub fn matching_status(&self) -> impl Resolve<ZResult<zenoh::matching::MatchingStatus>> + '_ {
+        self.publisher.matching_status()
+    }
+
+    /// Return a [`MatchingListener`](zenoh::matching::MatchingStatus) for this Publisher.
+    ///
+    /// The [`MatchingListener`](zenoh::matching::MatchingStatus) that will send a notification each time
+    /// the [`MatchingStatus`](zenoh::matching::MatchingStatus) of the Publisher changes.
+    ///
+    /// # Examples
+    /// ```no_run
+    /// # #[tokio::main]
+    /// # async fn main() {
+    /// use zenoh_ext::AdvancedPublisherBuilderExt;
+    ///
+    /// let session = zenoh::open(zenoh::Config::default()).await.unwrap();
+    /// let publisher = session.declare_publisher("key/expression").advanced().await.unwrap();
+    /// let matching_listener = publisher.matching_listener().await.unwrap();
+    /// while let Ok(matching_status) = matching_listener.recv_async().await {
+    ///     if matching_status.matching() {
+    ///         println!("Publisher has matching subscribers.");
+    ///     } else {
+    ///         println!("Publisher has NO MORE matching subscribers.");
+    ///     }
+    /// }
+    /// # }
+    /// ```
+    #[zenoh_macros::unstable]
+    pub fn matching_listener(
+        &self,
+    ) -> zenoh::matching::MatchingListenerBuilder<'_, zenoh::handlers::DefaultHandler> {
+        self.publisher.matching_listener()
+    }
+
+    /// Undeclares the [`Publisher`], informing the network that it needn't optimize publications for its key expression anymore.
+    ///
+    /// # Examples
+    /// ```
+    /// # #[tokio::main]
+    /// # async fn main() {
+    /// use zenoh_ext::AdvancedPublisherBuilderExt;
+    ///
+    /// let session = zenoh::open(zenoh::Config::default()).await.unwrap();
+    /// let publisher = session.declare_publisher("key/expression").advanced().await.unwrap();
+    /// publisher.undeclare().await.unwrap();
+    /// # }
+    /// ```
+    #[zenoh_macros::unstable]
+    pub fn undeclare(self) -> impl Resolve<ZResult<()>> + 'a {
+        self.publisher.undeclare()
+    }
+}
+
+#[zenoh_macros::unstable]
+pub type AdvancedPublisherPutBuilder<'a> = AdvancedPublicationBuilder<'a, PublicationBuilderPut>;
+#[zenoh_macros::unstable]
+pub type AdvancedPublisherDeleteBuilder<'a> =
+    AdvancedPublicationBuilder<'a, PublicationBuilderDelete>;
+
+#[must_use = "Resolvables do nothing unless you resolve them using `.await` or `zenoh::Wait::wait`"]
+#[derive(Clone)]
+#[zenoh_macros::unstable]
+pub struct AdvancedPublicationBuilder<'a, P> {
+    pub(crate) builder: PublicationBuilder<&'a Publisher<'a>, P>,
+    pub(crate) cache: Option<&'a AdvancedCache>,
+}
+
+#[zenoh_macros::internal_trait]
+#[zenoh_macros::unstable]
+impl EncodingBuilderTrait for AdvancedPublicationBuilder<'_, PublicationBuilderPut> {
+    #[zenoh_macros::unstable]
+    fn encoding<T: Into<Encoding>>(self, encoding: T) -> Self {
+        Self {
+            builder: self.builder.encoding(encoding),
+            ..self
+        }
+    }
+}
+
+#[zenoh_macros::internal_trait]
+#[zenoh_macros::unstable]
+impl<P> SampleBuilderTrait for AdvancedPublicationBuilder<'_, P> {
+    #[zenoh_macros::unstable]
+    fn source_info(self, source_info: SourceInfo) -> Self {
+        Self {
+            builder: self.builder.source_info(source_info),
+            ..self
+        }
+    }
+    #[zenoh_macros::unstable]
+    fn attachment<TA: Into<OptionZBytes>>(self, attachment: TA) -> Self {
+        let attachment: OptionZBytes = attachment.into();
+        Self {
+            builder: self.builder.attachment(attachment),
+            ..self
+        }
+    }
+}
+
+#[zenoh_macros::internal_trait]
+#[zenoh_macros::unstable]
+impl<P> TimestampBuilderTrait for AdvancedPublicationBuilder<'_, P> {
+    #[zenoh_macros::unstable]
+    fn timestamp<TS: Into<Option<uhlc::Timestamp>>>(self, timestamp: TS) -> Self {
+        Self {
+            builder: self.builder.timestamp(timestamp),
+            ..self
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<P> Resolvable for AdvancedPublicationBuilder<'_, P> {
+    type To = ZResult<()>;
+}
+
+#[zenoh_macros::unstable]
+impl Wait for AdvancedPublisherPutBuilder<'_> {
+    #[inline]
+    #[zenoh_macros::unstable]
+    fn wait(self) -> <Self as Resolvable>::To {
+        if let Some(cache) = self.cache {
+            cache.cache_sample(zenoh::sample::Sample::from(&self.builder));
+        }
+        self.builder.wait()
+    }
+}
+
+#[zenoh_macros::unstable]
+impl Wait for AdvancedPublisherDeleteBuilder<'_> {
+    #[inline]
+    #[zenoh_macros::unstable]
+    fn wait(self) -> <Self as Resolvable>::To {
+        if let Some(cache) = self.cache {
+            cache.cache_sample(zenoh::sample::Sample::from(&self.builder));
+        }
+        self.builder.wait()
+    }
+}
+
+#[zenoh_macros::unstable]
+impl IntoFuture for AdvancedPublisherPutBuilder<'_> {
+    type Output = <Self as Resolvable>::To;
+    type IntoFuture = Ready<<Self as Resolvable>::To>;
+
+    #[zenoh_macros::unstable]
+    fn into_future(self) -> Self::IntoFuture {
+        std::future::ready(self.wait())
+    }
+}
+
+#[zenoh_macros::unstable]
+impl IntoFuture for AdvancedPublisherDeleteBuilder<'_> {
+    type Output = <Self as Resolvable>::To;
+    type IntoFuture = Ready<<Self as Resolvable>::To>;
+
+    #[zenoh_macros::unstable]
+    fn into_future(self) -> Self::IntoFuture {
+        std::future::ready(self.wait())
+    }
+}
diff --git a/zenoh-ext/src/advanced_subscriber.rs b/zenoh-ext/src/advanced_subscriber.rs
new file mode 100644
index 0000000000..e0d9ed7e3a
--- /dev/null
+++ b/zenoh-ext/src/advanced_subscriber.rs
@@ -0,0 +1,1324 @@
+//
+// 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, <zenoh@zettascale.tech>
+//
+use std::{collections::BTreeMap, future::IntoFuture, str::FromStr};
+
+use zenoh::{
+    config::ZenohId,
+    handlers::{Callback, IntoHandler},
+    key_expr::KeyExpr,
+    liveliness::{LivelinessSubscriberBuilder, LivelinessToken},
+    pubsub::SubscriberBuilder,
+    query::{
+        ConsolidationMode, Parameters, Selector, TimeBound, TimeExpr, TimeRange, ZenohParameters,
+    },
+    sample::{Locality, Sample, SampleKind},
+    session::{EntityGlobalId, EntityId},
+    Resolvable, Resolve, Session, Wait, KE_ADV_PREFIX, KE_AT, KE_EMPTY, KE_PUB, KE_STAR,
+    KE_STARSTAR, KE_SUB,
+};
+use zenoh_util::{Timed, TimedEvent, Timer};
+#[zenoh_macros::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,
+    uhlc::ID,
+    zenoh::handlers::{locked, DefaultHandler},
+    zenoh::internal::{runtime::ZRuntime, zlock},
+    zenoh::pubsub::Subscriber,
+    zenoh::query::{QueryTarget, Reply, ReplyKeyExpr},
+    zenoh::time::Timestamp,
+    zenoh::Result as ZResult,
+};
+
+use crate::advanced_cache::{ke_liveliness, KE_UHLC};
+
+#[derive(Debug, Default, Clone)]
+/// Configure query for historical data.
+#[zenoh_macros::unstable]
+pub struct HistoryConfig {
+    liveliness: bool,
+    sample_depth: Option<usize>,
+    age: Option<f64>,
+}
+
+#[zenoh_macros::unstable]
+impl HistoryConfig {
+    /// Enable detection of late joiner publishers and query for their historical data.
+    ///
+    /// Late joiner detection can only be achieved for [`AdvancedPublishers`](crate::AdvancedPublisher) that enable publisher_detection.
+    /// History can only be retransmitted by [`AdvancedPublishers`](crate::AdvancedPublisher) that enable [`cache`](crate::AdvancedPublisherBuilder::cache).
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn detect_late_publishers(mut self) -> Self {
+        self.liveliness = true;
+        self
+    }
+
+    /// Specify how many samples to query for each resource.
+    #[zenoh_macros::unstable]
+    pub fn max_samples(mut self, depth: usize) -> Self {
+        self.sample_depth = Some(depth);
+        self
+    }
+
+    /// Specify the maximum age of samples to query.
+    #[zenoh_macros::unstable]
+    pub fn max_age(mut self, seconds: f64) -> Self {
+        self.age = Some(seconds);
+        self
+    }
+}
+
+#[derive(Default)]
+/// Configure retransmission.
+#[zenoh_macros::unstable]
+pub struct RecoveryConfig {
+    periodic_queries: Option<Duration>,
+}
+
+impl std::fmt::Debug for RecoveryConfig {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let mut s = f.debug_struct("RetransmissionConf");
+        s.field("periodic_queries", &self.periodic_queries);
+        s.finish()
+    }
+}
+
+#[zenoh_macros::unstable]
+impl RecoveryConfig {
+    /// Enable periodic queries for not yet received Samples and specify their period.
+    ///
+    /// This allows to retrieve the last Sample(s) if the last Sample(s) is/are lost.
+    /// So it is useful for sporadic publications but useless for periodic publications
+    /// with a period smaller or equal to this period.
+    /// Retransmission can only be achieved by [`AdvancedPublishers`](crate::AdvancedPublisher)
+    /// that enable [`cache`](crate::AdvancedPublisherBuilder::cache) and
+    /// [`sample_miss_detection`](crate::AdvancedPublisherBuilder::sample_miss_detection).
+    #[zenoh_macros::unstable]
+    #[inline]
+    pub fn periodic_queries(mut self, period: Option<Duration>) -> Self {
+        self.periodic_queries = period;
+        self
+    }
+}
+
+/// The builder of an [`AdvancedSubscriber`], allowing to configure it.
+#[zenoh_macros::unstable]
+pub struct AdvancedSubscriberBuilder<'a, 'b, 'c, Handler, const BACKGROUND: bool = false> {
+    pub(crate) session: &'a Session,
+    pub(crate) key_expr: ZResult<KeyExpr<'b>>,
+    pub(crate) origin: Locality,
+    pub(crate) retransmission: Option<RecoveryConfig>,
+    pub(crate) query_target: QueryTarget,
+    pub(crate) query_timeout: Duration,
+    pub(crate) history: Option<HistoryConfig>,
+    pub(crate) liveliness: bool,
+    pub(crate) meta_key_expr: Option<ZResult<KeyExpr<'c>>>,
+    pub(crate) handler: Handler,
+}
+
+#[zenoh_macros::unstable]
+impl<'a, 'b, Handler> AdvancedSubscriberBuilder<'a, 'b, '_, Handler> {
+    #[zenoh_macros::unstable]
+    pub(crate) fn new(builder: SubscriberBuilder<'a, 'b, Handler>) -> Self {
+        AdvancedSubscriberBuilder {
+            session: builder.session,
+            key_expr: builder.key_expr,
+            origin: builder.origin,
+            handler: builder.handler,
+            retransmission: None,
+            query_target: QueryTarget::All,
+            query_timeout: Duration::from_secs(10),
+            history: None,
+            liveliness: false,
+            meta_key_expr: None,
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<'a, 'b, 'c> AdvancedSubscriberBuilder<'a, 'b, 'c, DefaultHandler> {
+    /// Add callback to AdvancedSubscriber.
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn callback<F>(self, callback: F) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Callback<Sample>>
+    where
+        F: Fn(Sample) + Send + Sync + 'static,
+    {
+        self.with(Callback::new(Arc::new(callback)))
+    }
+
+    /// Add callback to `AdvancedSubscriber`.
+    ///
+    /// Using this guarantees that your callback will never be called concurrently.
+    /// If your callback is also accepted by the [`callback`](AdvancedSubscriberBuilder::callback) method, we suggest you use it instead of `callback_mut`
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn callback_mut<F>(
+        self,
+        callback: F,
+    ) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Callback<Sample>>
+    where
+        F: FnMut(Sample) + Send + Sync + 'static,
+    {
+        self.callback(locked(callback))
+    }
+
+    /// Make the built AdvancedSubscriber an [`AdvancedSubscriber`](AdvancedSubscriber).
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn with<Handler>(self, handler: Handler) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Handler>
+    where
+        Handler: IntoHandler<Sample>,
+    {
+        AdvancedSubscriberBuilder {
+            session: self.session,
+            key_expr: self.key_expr.map(|s| s.into_owned()),
+            origin: self.origin,
+            retransmission: self.retransmission,
+            query_target: self.query_target,
+            query_timeout: self.query_timeout,
+            history: self.history,
+            liveliness: self.liveliness,
+            meta_key_expr: self.meta_key_expr,
+            handler,
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<'a, 'c, Handler> AdvancedSubscriberBuilder<'a, '_, 'c, Handler> {
+    /// Restrict the matching publications that will be receive by this [`Subscriber`]
+    /// to the ones that have the given [`Locality`](crate::prelude::Locality).
+    #[zenoh_macros::unstable]
+    #[inline]
+    pub fn allowed_origin(mut self, origin: Locality) -> Self {
+        self.origin = origin;
+        self
+    }
+
+    /// Ask for retransmission of detected lost Samples.
+    ///
+    /// Retransmission can only be achieved by [`AdvancedPublishers`](crate::AdvancedPublisher)
+    /// that enable [`cache`](crate::AdvancedPublisherBuilder::cache) and
+    /// [`sample_miss_detection`](crate::AdvancedPublisherBuilder::sample_miss_detection).
+    #[zenoh_macros::unstable]
+    #[inline]
+    pub fn recovery(mut self, conf: RecoveryConfig) -> Self {
+        self.retransmission = Some(conf);
+        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 timeout to be used for queries (history, retransmission).
+    #[zenoh_macros::unstable]
+    #[inline]
+    pub fn query_timeout(mut self, query_timeout: Duration) -> Self {
+        self.query_timeout = query_timeout;
+        self
+    }
+
+    /// Enable query for historical data.
+    ///
+    /// History can only be retransmitted by [`AdvancedPublishers`](crate::AdvancedPublisher) that enable [`cache`](crate::AdvancedPublisherBuilder::cache).
+    #[zenoh_macros::unstable]
+    #[inline]
+    pub fn history(mut self, config: HistoryConfig) -> Self {
+        self.history = Some(config);
+        self
+    }
+
+    /// Allow this subscriber to be detected through liveliness.
+    #[zenoh_macros::unstable]
+    pub fn subscriber_detection(mut self) -> Self {
+        self.liveliness = true;
+        self
+    }
+
+    /// A key expression added to the liveliness token key expression.
+    /// It can be used to convey meta data.
+    #[zenoh_macros::unstable]
+    pub fn subscriber_detection_metadata<TryIntoKeyExpr>(mut self, meta: TryIntoKeyExpr) -> Self
+    where
+        TryIntoKeyExpr: TryInto<KeyExpr<'c>>,
+        <TryIntoKeyExpr as TryInto<KeyExpr<'c>>>::Error: Into<zenoh::Error>,
+    {
+        self.meta_key_expr = Some(meta.try_into().map_err(Into::into));
+        self
+    }
+
+    #[zenoh_macros::unstable]
+    fn with_static_keys(self) -> AdvancedSubscriberBuilder<'a, 'static, 'static, Handler> {
+        AdvancedSubscriberBuilder {
+            session: self.session,
+            key_expr: self.key_expr.map(|s| s.into_owned()),
+            origin: self.origin,
+            retransmission: self.retransmission,
+            query_target: self.query_target,
+            query_timeout: self.query_timeout,
+            history: self.history,
+            liveliness: self.liveliness,
+            meta_key_expr: self.meta_key_expr.map(|s| s.map(|s| s.into_owned())),
+            handler: self.handler,
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<Handler> Resolvable for AdvancedSubscriberBuilder<'_, '_, '_, Handler>
+where
+    Handler: IntoHandler<Sample>,
+    Handler::Handler: Send,
+{
+    type To = ZResult<AdvancedSubscriber<Handler::Handler>>;
+}
+
+#[zenoh_macros::unstable]
+impl<Handler> Wait for AdvancedSubscriberBuilder<'_, '_, '_, Handler>
+where
+    Handler: IntoHandler<Sample> + Send,
+    Handler::Handler: Send,
+{
+    #[zenoh_macros::unstable]
+    fn wait(self) -> <Self as Resolvable>::To {
+        AdvancedSubscriber::new(self.with_static_keys())
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<Handler> IntoFuture for AdvancedSubscriberBuilder<'_, '_, '_, Handler>
+where
+    Handler: IntoHandler<Sample> + Send,
+    Handler::Handler: Send,
+{
+    type Output = <Self as Resolvable>::To;
+    type IntoFuture = Ready<<Self as Resolvable>::To>;
+
+    #[zenoh_macros::unstable]
+    fn into_future(self) -> Self::IntoFuture {
+        std::future::ready(self.wait())
+    }
+}
+
+#[zenoh_macros::unstable]
+struct Period {
+    timer: Timer,
+    period: Duration,
+}
+
+#[zenoh_macros::unstable]
+struct State {
+    next_id: usize,
+    global_pending_queries: u64,
+    sequenced_states: HashMap<EntityGlobalId, SourceState<u32>>,
+    timestamped_states: HashMap<ID, SourceState<Timestamp>>,
+    session: Session,
+    key_expr: KeyExpr<'static>,
+    retransmission: bool,
+    period: Option<Period>,
+    query_target: QueryTarget,
+    query_timeout: Duration,
+    callback: Callback<Sample>,
+    miss_handlers: HashMap<usize, Callback<Miss>>,
+}
+
+#[zenoh_macros::unstable]
+impl State {
+    #[zenoh_macros::unstable]
+    fn register_miss_callback(&mut self, callback: Callback<Miss>) -> usize {
+        let id = self.next_id;
+        self.next_id += 1;
+        self.miss_handlers.insert(id, callback);
+        id
+    }
+    #[zenoh_macros::unstable]
+    fn unregister_miss_callback(&mut self, id: &usize) {
+        self.miss_handlers.remove(id);
+    }
+}
+
+macro_rules! spawn_periodoic_queries {
+    ($p:expr,$s:expr,$r:expr) => {{
+        if let Some(period) = &$p.period {
+            period.timer.add(TimedEvent::periodic(
+                period.period,
+                PeriodicQuery {
+                    source_id: $s,
+                    statesref: $r,
+                },
+            ))
+        }
+    }};
+}
+
+#[zenoh_macros::unstable]
+struct SourceState<T> {
+    last_delivered: Option<T>,
+    pending_queries: u64,
+    pending_samples: BTreeMap<T, Sample>,
+}
+
+/// [`AdvancedSubscriber`].
+#[zenoh_macros::unstable]
+pub struct AdvancedSubscriber<Receiver> {
+    statesref: Arc<Mutex<State>>,
+    subscriber: Subscriber<()>,
+    receiver: Receiver,
+    _liveliness_subscriber: Option<Subscriber<()>>,
+    _token: Option<LivelinessToken>,
+}
+
+#[zenoh_macros::unstable]
+impl<Receiver> std::ops::Deref for AdvancedSubscriber<Receiver> {
+    type Target = Receiver;
+    fn deref(&self) -> &Self::Target {
+        &self.receiver
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<Receiver> std::ops::DerefMut for AdvancedSubscriber<Receiver> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.receiver
+    }
+}
+
+#[zenoh_macros::unstable]
+fn handle_sample(states: &mut State, sample: Sample) -> bool {
+    if let (Some(source_id), Some(source_sn)) = (
+        sample.source_info().source_id(),
+        sample.source_info().source_sn(),
+    ) {
+        let entry = states.sequenced_states.entry(*source_id);
+        let new = matches!(&entry, Entry::Vacant(_));
+        let state = entry.or_insert(SourceState::<u32> {
+            last_delivered: None,
+            pending_queries: 0,
+            pending_samples: BTreeMap::new(),
+        });
+        if states.global_pending_queries != 0 {
+            state.pending_samples.insert(source_sn, sample);
+        } else if state.last_delivered.is_some() && source_sn != state.last_delivered.unwrap() + 1 {
+            if source_sn > state.last_delivered.unwrap() {
+                if states.retransmission {
+                    state.pending_samples.insert(source_sn, sample);
+                } else {
+                    tracing::info!(
+                        "Sample missed: missed {} samples from {:?}.",
+                        source_sn - state.last_delivered.unwrap() - 1,
+                        source_id,
+                    );
+                    for miss_callback in states.miss_handlers.values() {
+                        miss_callback.call(Miss {
+                            source: *source_id,
+                            nb: source_sn - state.last_delivered.unwrap() - 1,
+                        });
+                    }
+                    states.callback.call(sample);
+                    state.last_delivered = Some(source_sn);
+                }
+            }
+        } else {
+            states.callback.call(sample);
+            let mut last_seq_num = source_sn;
+            state.last_delivered = Some(last_seq_num);
+            while let Some(s) = state.pending_samples.remove(&(last_seq_num + 1)) {
+                states.callback.call(s);
+                last_seq_num += 1;
+                state.last_delivered = Some(last_seq_num);
+            }
+        }
+        new
+    } else if let Some(timestamp) = sample.timestamp() {
+        let entry = states.timestamped_states.entry(*timestamp.get_id());
+        let state = entry.or_insert(SourceState::<Timestamp> {
+            last_delivered: None,
+            pending_queries: 0,
+            pending_samples: BTreeMap::new(),
+        });
+        if state.last_delivered.map(|t| t < *timestamp).unwrap_or(true) {
+            if states.global_pending_queries == 0 && state.pending_queries == 0 {
+                state.last_delivered = Some(*timestamp);
+                states.callback.call(sample);
+            } else {
+                state.pending_samples.entry(*timestamp).or_insert(sample);
+            }
+        }
+        false
+    } else {
+        states.callback.call(sample);
+        false
+    }
+}
+
+#[zenoh_macros::unstable]
+fn seq_num_range(start: Option<u32>, end: Option<u32>) -> 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(),
+    }
+}
+
+#[zenoh_macros::unstable]
+#[derive(Clone)]
+struct PeriodicQuery {
+    source_id: EntityGlobalId,
+    statesref: Arc<Mutex<State>>,
+}
+
+#[zenoh_macros::unstable]
+#[async_trait]
+impl Timed for PeriodicQuery {
+    async fn run(&mut self) {
+        let mut lock = zlock!(self.statesref);
+        let states = &mut *lock;
+        if let Some(state) = states.sequenced_states.get_mut(&self.source_id) {
+            state.pending_queries += 1;
+            let query_expr = KE_ADV_PREFIX
+                / KE_STAR
+                / &self.source_id.zid().into_keyexpr()
+                / &KeyExpr::try_from(self.source_id.eid().to_string()).unwrap()
+                / KE_STARSTAR
+                / KE_AT
+                / &states.key_expr;
+            let seq_num_range = seq_num_range(state.last_delivered.map(|s| s + 1), None);
+
+            let session = states.session.clone();
+            let key_expr = states.key_expr.clone().into_owned();
+            let query_target = states.query_target;
+            let query_timeout = states.query_timeout;
+            drop(lock);
+            let handler = SequencedRepliesHandler {
+                source_id: self.source_id,
+                statesref: self.statesref.clone(),
+            };
+            let _ = session
+                .get(Selector::from((query_expr, seq_num_range)))
+                .callback({
+                    move |r: Reply| {
+                        if let Ok(s) = r.into_result() {
+                            if key_expr.intersects(s.key_expr()) {
+                                let states = &mut *zlock!(handler.statesref);
+                                handle_sample(states, s);
+                            }
+                        }
+                    }
+                })
+                .consolidation(ConsolidationMode::None)
+                .accept_replies(ReplyKeyExpr::Any)
+                .target(query_target)
+                .timeout(query_timeout)
+                .wait();
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<Handler> AdvancedSubscriber<Handler> {
+    fn new<H>(conf: AdvancedSubscriberBuilder<'_, '_, '_, H>) -> ZResult<Self>
+    where
+        H: IntoHandler<Sample, Handler = Handler> + Send,
+    {
+        let (callback, receiver) = conf.handler.into_handler();
+        let key_expr = conf.key_expr?;
+        let meta = match conf.meta_key_expr {
+            Some(meta) => Some(meta?),
+            None => None,
+        };
+        let retransmission = conf.retransmission;
+        let query_target = conf.query_target;
+        let query_timeout = conf.query_timeout;
+        let session = conf.session.clone();
+        let statesref = Arc::new(Mutex::new(State {
+            next_id: 0,
+            sequenced_states: HashMap::new(),
+            timestamped_states: HashMap::new(),
+            global_pending_queries: if conf.history.is_some() { 1 } else { 0 },
+            session,
+            period: retransmission.as_ref().and_then(|r| {
+                let _rt = ZRuntime::Application.enter();
+                r.periodic_queries.map(|p| Period {
+                    timer: Timer::new(false),
+                    period: p,
+                })
+            }),
+            key_expr: key_expr.clone().into_owned(),
+            retransmission: retransmission.is_some(),
+            query_target: conf.query_target,
+            query_timeout: conf.query_timeout,
+            callback: callback.clone(),
+            miss_handlers: HashMap::new(),
+        }));
+
+        let sub_callback = {
+            let statesref = statesref.clone();
+            let session = conf.session.clone();
+            let key_expr = key_expr.clone().into_owned();
+
+            move |s: Sample| {
+                let mut lock = zlock!(statesref);
+                let states = &mut *lock;
+                let source_id = s.source_info().source_id().cloned();
+                let new = handle_sample(states, s);
+
+                if let Some(source_id) = source_id {
+                    if new {
+                        spawn_periodoic_queries!(states, source_id, statesref.clone());
+                    }
+
+                    if let Some(state) = states.sequenced_states.get_mut(&source_id) {
+                        if retransmission.is_some()
+                            && state.pending_queries == 0
+                            && !state.pending_samples.is_empty()
+                        {
+                            state.pending_queries += 1;
+                            let query_expr = KE_ADV_PREFIX
+                                / KE_STAR
+                                / &source_id.zid().into_keyexpr()
+                                / &KeyExpr::try_from(source_id.eid().to_string()).unwrap()
+                                / KE_STARSTAR
+                                / KE_AT
+                                / &key_expr;
+                            let seq_num_range =
+                                seq_num_range(state.last_delivered.map(|s| s + 1), None);
+                            drop(lock);
+                            let handler = SequencedRepliesHandler {
+                                source_id,
+                                statesref: statesref.clone(),
+                            };
+                            let _ = session
+                                .get(Selector::from((query_expr, seq_num_range)))
+                                .callback({
+                                    let key_expr = key_expr.clone().into_owned();
+                                    move |r: Reply| {
+                                        if let Ok(s) = r.into_result() {
+                                            if key_expr.intersects(s.key_expr()) {
+                                                let states = &mut *zlock!(handler.statesref);
+                                                handle_sample(states, s);
+                                            }
+                                        }
+                                    }
+                                })
+                                .consolidation(ConsolidationMode::None)
+                                .accept_replies(ReplyKeyExpr::Any)
+                                .target(query_target)
+                                .timeout(query_timeout)
+                                .wait();
+                        }
+                    }
+                }
+            }
+        };
+
+        let subscriber = conf
+            .session
+            .declare_subscriber(&key_expr)
+            .callback(sub_callback)
+            .allowed_origin(conf.origin)
+            .wait()?;
+
+        if let Some(historyconf) = conf.history.as_ref() {
+            let handler = InitialRepliesHandler {
+                statesref: statesref.clone(),
+            };
+            let mut params = Parameters::empty();
+            if let Some(max) = historyconf.sample_depth {
+                params.insert("_max", max.to_string());
+            }
+            if let Some(age) = historyconf.age {
+                params.set_time_range(TimeRange {
+                    start: TimeBound::Inclusive(TimeExpr::Now { offset_secs: -age }),
+                    end: TimeBound::Unbounded,
+                });
+            }
+            let _ = conf
+                .session
+                .get(Selector::from((
+                    KE_ADV_PREFIX / KE_STARSTAR / KE_AT / &key_expr,
+                    params,
+                )))
+                .callback({
+                    let key_expr = key_expr.clone().into_owned();
+                    move |r: Reply| {
+                        if let Ok(s) = r.into_result() {
+                            if key_expr.intersects(s.key_expr()) {
+                                let states = &mut *zlock!(handler.statesref);
+                                handle_sample(states, s);
+                            }
+                        }
+                    }
+                })
+                .consolidation(ConsolidationMode::None)
+                .accept_replies(ReplyKeyExpr::Any)
+                .target(query_target)
+                .timeout(query_timeout)
+                .wait();
+        }
+
+        let liveliness_subscriber = if let Some(historyconf) = conf.history {
+            if historyconf.liveliness {
+                let live_callback = {
+                    let session = conf.session.clone();
+                    let statesref = statesref.clone();
+                    let key_expr = key_expr.clone().into_owned();
+                    move |s: Sample| {
+                        if s.kind() == SampleKind::Put {
+                            if let Ok(parsed) = ke_liveliness::parse(s.key_expr().as_keyexpr()) {
+                                if let Ok(zid) = ZenohId::from_str(parsed.zid().as_str()) {
+                                    // TODO : If we already have a state associated to this discovered source
+                                    // we should query with the appropriate range to avoid unnecessary retransmissions
+                                    if parsed.eid() == KE_UHLC {
+                                        let mut lock = zlock!(statesref);
+                                        let states = &mut *lock;
+                                        let entry = states.timestamped_states.entry(ID::from(zid));
+                                        let state = entry.or_insert(SourceState::<Timestamp> {
+                                            last_delivered: None,
+                                            pending_queries: 0,
+                                            pending_samples: BTreeMap::new(),
+                                        });
+                                        state.pending_queries += 1;
+                                        drop(lock);
+
+                                        let handler = TimestampedRepliesHandler {
+                                            id: ID::from(zid),
+                                            statesref: statesref.clone(),
+                                            callback: callback.clone(),
+                                        };
+                                        let mut params = Parameters::empty();
+                                        if let Some(max) = historyconf.sample_depth {
+                                            params.insert("_max", max.to_string());
+                                        }
+                                        if let Some(age) = historyconf.age {
+                                            params.set_time_range(TimeRange {
+                                                start: TimeBound::Inclusive(TimeExpr::Now {
+                                                    offset_secs: -age,
+                                                }),
+                                                end: TimeBound::Unbounded,
+                                            });
+                                        }
+                                        let _ = session
+                                            .get(Selector::from((s.key_expr(), params)))
+                                            .callback({
+                                                let key_expr = key_expr.clone().into_owned();
+                                                move |r: Reply| {
+                                                    if let Ok(s) = r.into_result() {
+                                                        if key_expr.intersects(s.key_expr()) {
+                                                            let states =
+                                                                &mut *zlock!(handler.statesref);
+                                                            handle_sample(states, s);
+                                                        }
+                                                    }
+                                                }
+                                            })
+                                            .consolidation(ConsolidationMode::None)
+                                            .accept_replies(ReplyKeyExpr::Any)
+                                            .target(query_target)
+                                            .timeout(query_timeout)
+                                            .wait();
+                                    } else if let Ok(eid) =
+                                        EntityId::from_str(parsed.eid().as_str())
+                                    {
+                                        let source_id = EntityGlobalId::new(zid, eid);
+                                        let mut lock = zlock!(statesref);
+                                        let states = &mut *lock;
+                                        let entry = states.sequenced_states.entry(source_id);
+                                        let new = matches!(&entry, Entry::Vacant(_));
+                                        let state = entry.or_insert(SourceState::<u32> {
+                                            last_delivered: None,
+                                            pending_queries: 0,
+                                            pending_samples: BTreeMap::new(),
+                                        });
+                                        state.pending_queries += 1;
+                                        drop(lock);
+
+                                        let handler = SequencedRepliesHandler {
+                                            source_id,
+                                            statesref: statesref.clone(),
+                                        };
+                                        let mut params = Parameters::empty();
+                                        if let Some(max) = historyconf.sample_depth {
+                                            params.insert("_max", max.to_string());
+                                        }
+                                        if let Some(age) = historyconf.age {
+                                            params.set_time_range(TimeRange {
+                                                start: TimeBound::Inclusive(TimeExpr::Now {
+                                                    offset_secs: -age,
+                                                }),
+                                                end: TimeBound::Unbounded,
+                                            });
+                                        }
+                                        let _ = session
+                                            .get(Selector::from((s.key_expr(), params)))
+                                            .callback({
+                                                let key_expr = key_expr.clone().into_owned();
+                                                move |r: Reply| {
+                                                    if let Ok(s) = r.into_result() {
+                                                        if key_expr.intersects(s.key_expr()) {
+                                                            let states =
+                                                                &mut *zlock!(handler.statesref);
+                                                            handle_sample(states, s);
+                                                        }
+                                                    }
+                                                }
+                                            })
+                                            .consolidation(ConsolidationMode::None)
+                                            .accept_replies(ReplyKeyExpr::Any)
+                                            .target(query_target)
+                                            .timeout(query_timeout)
+                                            .wait();
+
+                                        if new {
+                                            spawn_periodoic_queries!(
+                                                zlock!(statesref),
+                                                source_id,
+                                                statesref.clone()
+                                            );
+                                        }
+                                    }
+                                } else {
+                                    let mut lock = zlock!(statesref);
+                                    let states = &mut *lock;
+                                    states.global_pending_queries += 1;
+                                    drop(lock);
+
+                                    let handler = InitialRepliesHandler {
+                                        statesref: statesref.clone(),
+                                    };
+                                    let mut params = Parameters::empty();
+                                    if let Some(max) = historyconf.sample_depth {
+                                        params.insert("_max", max.to_string());
+                                    }
+                                    if let Some(age) = historyconf.age {
+                                        params.set_time_range(TimeRange {
+                                            start: TimeBound::Inclusive(TimeExpr::Now {
+                                                offset_secs: -age,
+                                            }),
+                                            end: TimeBound::Unbounded,
+                                        });
+                                    }
+                                    let _ = session
+                                        .get(Selector::from((s.key_expr(), params)))
+                                        .callback({
+                                            let key_expr = key_expr.clone().into_owned();
+                                            move |r: Reply| {
+                                                if let Ok(s) = r.into_result() {
+                                                    if key_expr.intersects(s.key_expr()) {
+                                                        let states =
+                                                            &mut *zlock!(handler.statesref);
+                                                        handle_sample(states, s);
+                                                    }
+                                                }
+                                            }
+                                        })
+                                        .consolidation(ConsolidationMode::None)
+                                        .accept_replies(ReplyKeyExpr::Any)
+                                        .target(query_target)
+                                        .timeout(query_timeout)
+                                        .wait();
+                                }
+                            } else {
+                                tracing::warn!(
+                                    "Received malformed liveliness token key expression: {}",
+                                    s.key_expr()
+                                );
+                            }
+                        }
+                    }
+                };
+
+                Some(
+                    conf
+                .session
+                .liveliness()
+                .declare_subscriber(KE_ADV_PREFIX / KE_PUB / KE_STARSTAR / KE_AT / &key_expr)
+                // .declare_subscriber(keformat!(ke_liveliness_all::formatter(), zid = 0, eid = 0, remaining = key_expr).unwrap())
+                .history(true)
+                .callback(live_callback)
+                .wait()?,
+                )
+            } else {
+                None
+            }
+        } else {
+            None
+        };
+
+        let token = if conf.liveliness {
+            let prefix = KE_ADV_PREFIX
+                / KE_SUB
+                / &subscriber.id().zid().into_keyexpr()
+                / &KeyExpr::try_from(subscriber.id().eid().to_string()).unwrap();
+            let prefix = match meta {
+                Some(meta) => prefix / &meta / KE_AT,
+                // We need this empty chunk because af a routing matching bug
+                _ => prefix / KE_EMPTY / KE_AT,
+            };
+            Some(
+                conf.session
+                    .liveliness()
+                    .declare_token(prefix / &key_expr)
+                    .wait()?,
+            )
+        } else {
+            None
+        };
+
+        let reliable_subscriber = AdvancedSubscriber {
+            statesref,
+            subscriber,
+            receiver,
+            _liveliness_subscriber: liveliness_subscriber,
+            _token: token,
+        };
+
+        Ok(reliable_subscriber)
+    }
+
+    /// Returns the [`EntityGlobalId`] of this AdvancedSubscriber.
+    #[zenoh_macros::unstable]
+    pub fn id(&self) -> EntityGlobalId {
+        self.subscriber.id()
+    }
+
+    /// Returns the [`KeyExpr`] this subscriber subscribes to.
+    #[zenoh_macros::unstable]
+    pub fn key_expr(&self) -> &KeyExpr<'static> {
+        self.subscriber.key_expr()
+    }
+
+    /// Returns a reference to this subscriber's handler.
+    /// An handler is anything that implements [`zenoh::handlers::IntoHandler`].
+    /// The default handler is [`zenoh::handlers::DefaultHandler`].
+    #[zenoh_macros::unstable]
+    pub fn handler(&self) -> &Handler {
+        &self.receiver
+    }
+
+    /// Returns a mutable reference to this subscriber's handler.
+    /// An handler is anything that implements [`zenoh::handlers::IntoHandler`].
+    /// The default handler is [`zenoh::handlers::DefaultHandler`].
+    #[zenoh_macros::unstable]
+    pub fn handler_mut(&mut self) -> &mut Handler {
+        &mut self.receiver
+    }
+
+    /// Declares a listener to detect missed samples.
+    ///
+    /// Missed samples can only be detected from [`AdvancedPublisher`](crate::AdvancedPublisher) that
+    /// enable [`sample_miss_detection`](crate::AdvancedPublisherBuilder::sample_miss_detection).
+    #[zenoh_macros::unstable]
+    pub fn sample_miss_listener(&self) -> SampleMissListenerBuilder<'_, DefaultHandler> {
+        SampleMissListenerBuilder {
+            statesref: &self.statesref,
+            handler: DefaultHandler::default(),
+        }
+    }
+
+    /// Declares a listener to detect matching publishers.
+    ///
+    /// Only [`AdvancedPublisher`](crate::AdvancedPublisher) that enable
+    /// [`publisher_detection`](crate::AdvancedPublisherBuilder::publisher_detection) can be detected.
+    #[zenoh_macros::unstable]
+    pub fn detect_publishers(&self) -> LivelinessSubscriberBuilder<'_, '_, DefaultHandler> {
+        self.subscriber.session().liveliness().declare_subscriber(
+            KE_ADV_PREFIX / KE_PUB / KE_STARSTAR / KE_AT / self.subscriber.key_expr(),
+        )
+    }
+
+    /// Undeclares this AdvancedSubscriber
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn undeclare(self) -> impl Resolve<ZResult<()>> {
+        self.subscriber.undeclare()
+    }
+}
+
+#[zenoh_macros::unstable]
+#[inline]
+fn flush_sequenced_source(
+    state: &mut SourceState<u32>,
+    callback: &Callback<Sample>,
+    source_id: &EntityGlobalId,
+    miss_handlers: &HashMap<usize, Callback<Miss>>,
+) {
+    if state.pending_queries == 0 && !state.pending_samples.is_empty() {
+        let mut pending_samples = BTreeMap::new();
+        std::mem::swap(&mut state.pending_samples, &mut pending_samples);
+        for (seq_num, sample) in pending_samples {
+            match state.last_delivered {
+                None => {
+                    state.last_delivered = Some(seq_num);
+                    callback.call(sample);
+                }
+                Some(last) if seq_num == last + 1 => {
+                    state.last_delivered = Some(seq_num);
+                    callback.call(sample);
+                }
+                Some(last) if seq_num > last + 1 => {
+                    tracing::warn!(
+                        "Sample missed: missed {} samples from {:?}.",
+                        seq_num - last - 1,
+                        source_id,
+                    );
+                    for miss_callback in miss_handlers.values() {
+                        miss_callback.call(Miss {
+                            source: *source_id,
+                            nb: seq_num - last - 1,
+                        })
+                    }
+                    state.last_delivered = Some(seq_num);
+                    callback.call(sample);
+                }
+                _ => {
+                    // duplicate
+                }
+            }
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+#[inline]
+fn flush_timestamped_source(state: &mut SourceState<Timestamp>, callback: &Callback<Sample>) {
+    if state.pending_queries == 0 && !state.pending_samples.is_empty() {
+        let mut pending_samples = BTreeMap::new();
+        std::mem::swap(&mut state.pending_samples, &mut pending_samples);
+        for (timestamp, sample) in pending_samples {
+            if state
+                .last_delivered
+                .map(|last| timestamp > last)
+                .unwrap_or(true)
+            {
+                state.last_delivered = Some(timestamp);
+                callback.call(sample);
+            }
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+#[derive(Clone)]
+struct InitialRepliesHandler {
+    statesref: Arc<Mutex<State>>,
+}
+
+#[zenoh_macros::unstable]
+impl Drop for InitialRepliesHandler {
+    fn drop(&mut self) {
+        let states = &mut *zlock!(self.statesref);
+        states.global_pending_queries = states.global_pending_queries.saturating_sub(1);
+
+        if states.global_pending_queries == 0 {
+            for (source_id, state) in states.sequenced_states.iter_mut() {
+                flush_sequenced_source(state, &states.callback, source_id, &states.miss_handlers);
+                spawn_periodoic_queries!(states, *source_id, self.statesref.clone());
+            }
+            for state in states.timestamped_states.values_mut() {
+                flush_timestamped_source(state, &states.callback);
+            }
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+#[derive(Clone)]
+struct SequencedRepliesHandler {
+    source_id: EntityGlobalId,
+    statesref: Arc<Mutex<State>>,
+}
+
+#[zenoh_macros::unstable]
+impl Drop for SequencedRepliesHandler {
+    fn drop(&mut self) {
+        let states = &mut *zlock!(self.statesref);
+        if let Some(state) = states.sequenced_states.get_mut(&self.source_id) {
+            state.pending_queries = state.pending_queries.saturating_sub(1);
+            if states.global_pending_queries == 0 {
+                flush_sequenced_source(
+                    state,
+                    &states.callback,
+                    &self.source_id,
+                    &states.miss_handlers,
+                )
+            }
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+#[derive(Clone)]
+struct TimestampedRepliesHandler {
+    id: ID,
+    statesref: Arc<Mutex<State>>,
+    callback: Callback<Sample>,
+}
+
+#[zenoh_macros::unstable]
+impl Drop for TimestampedRepliesHandler {
+    fn drop(&mut self) {
+        let states = &mut *zlock!(self.statesref);
+        if let Some(state) = states.timestamped_states.get_mut(&self.id) {
+            state.pending_queries = state.pending_queries.saturating_sub(1);
+            if states.global_pending_queries == 0 {
+                flush_timestamped_source(state, &self.callback);
+            }
+        }
+    }
+}
+
+/// A struct that represent missed samples.
+#[zenoh_macros::unstable]
+pub struct Miss {
+    source: EntityGlobalId,
+    nb: u32,
+}
+
+impl Miss {
+    /// The source of missed samples.
+    pub fn source(&self) -> EntityGlobalId {
+        self.source
+    }
+
+    /// The number of missed samples.
+    pub fn nb(&self) -> u32 {
+        self.nb
+    }
+}
+
+/// A listener to detect missed samples.
+///
+/// Missed samples can only be detected from [`AdvancedPublisher`](crate::AdvancedPublisher) that
+/// enable [`sample_miss_detection`](crate::AdvancedPublisherBuilder::sample_miss_detection).
+#[zenoh_macros::unstable]
+pub struct SampleMissListener<Handler> {
+    id: usize,
+    statesref: Arc<Mutex<State>>,
+    handler: Handler,
+}
+
+#[zenoh_macros::unstable]
+impl<Handler> SampleMissListener<Handler> {
+    #[inline]
+    pub fn undeclare(self) -> SampleMissHandlerUndeclaration<Handler>
+    where
+        Handler: Send,
+    {
+        // self.undeclare_inner(())
+        SampleMissHandlerUndeclaration(self)
+    }
+
+    fn undeclare_impl(&mut self) -> ZResult<()> {
+        // set the flag first to avoid double panic if this function panic
+        zlock!(self.statesref).unregister_miss_callback(&self.id);
+        Ok(())
+    }
+}
+
+#[cfg(feature = "unstable")]
+impl<Handler> Drop for SampleMissListener<Handler> {
+    fn drop(&mut self) {
+        if let Err(error) = self.undeclare_impl() {
+            tracing::error!(error);
+        }
+    }
+}
+
+// #[zenoh_macros::unstable]
+// impl<Handler: Send> UndeclarableSealed<()> for SampleMissHandler<Handler> {
+//     type Undeclaration = SampleMissHandlerUndeclaration<Handler>;
+
+//     fn undeclare_inner(self, _: ()) -> Self::Undeclaration {
+//         SampleMissHandlerUndeclaration(self)
+//     }
+// }
+
+#[zenoh_macros::unstable]
+impl<Handler> std::ops::Deref for SampleMissListener<Handler> {
+    type Target = Handler;
+
+    fn deref(&self) -> &Self::Target {
+        &self.handler
+    }
+}
+#[zenoh_macros::unstable]
+impl<Handler> std::ops::DerefMut for SampleMissListener<Handler> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.handler
+    }
+}
+
+/// A [`Resolvable`] returned when undeclaring a [`SampleMissListener`].
+#[zenoh_macros::unstable]
+pub struct SampleMissHandlerUndeclaration<Handler>(SampleMissListener<Handler>);
+
+#[zenoh_macros::unstable]
+impl<Handler> Resolvable for SampleMissHandlerUndeclaration<Handler> {
+    type To = ZResult<()>;
+}
+
+#[zenoh_macros::unstable]
+impl<Handler> Wait for SampleMissHandlerUndeclaration<Handler> {
+    fn wait(mut self) -> <Self as Resolvable>::To {
+        self.0.undeclare_impl()
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<Handler> IntoFuture for SampleMissHandlerUndeclaration<Handler> {
+    type Output = <Self as Resolvable>::To;
+    type IntoFuture = Ready<<Self as Resolvable>::To>;
+
+    fn into_future(self) -> Self::IntoFuture {
+        std::future::ready(self.wait())
+    }
+}
+
+/// A builder for initializing a [`SampleMissListener`].
+#[zenoh_macros::unstable]
+pub struct SampleMissListenerBuilder<'a, Handler, const BACKGROUND: bool = false> {
+    statesref: &'a Arc<Mutex<State>>,
+    handler: Handler,
+}
+
+#[zenoh_macros::unstable]
+impl<'a> SampleMissListenerBuilder<'a, DefaultHandler> {
+    /// Receive the sample miss notification with a callback.
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn callback<F>(self, callback: F) -> SampleMissListenerBuilder<'a, Callback<Miss>>
+    where
+        F: Fn(Miss) + Send + Sync + 'static,
+    {
+        self.with(Callback::new(Arc::new(callback)))
+    }
+
+    /// Receive the sample miss notification with a mutable callback.
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn callback_mut<F>(self, callback: F) -> SampleMissListenerBuilder<'a, Callback<Miss>>
+    where
+        F: FnMut(Miss) + Send + Sync + 'static,
+    {
+        self.callback(zenoh::handlers::locked(callback))
+    }
+
+    /// Receive the sample miss notification with a [`Handler`](IntoHandler).
+    #[inline]
+    #[zenoh_macros::unstable]
+    pub fn with<Handler>(self, handler: Handler) -> SampleMissListenerBuilder<'a, Handler>
+    where
+        Handler: IntoHandler<Miss>,
+    {
+        SampleMissListenerBuilder {
+            statesref: self.statesref,
+            handler,
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<'a> SampleMissListenerBuilder<'a, Callback<Miss>> {
+    /// Register the sample miss notification callback to be run in background until the adanced subscriber is undeclared.
+    ///
+    /// Background builder doesn't return a `SampleMissHandler` object anymore.
+    #[zenoh_macros::unstable]
+    pub fn background(self) -> SampleMissListenerBuilder<'a, Callback<Miss>, true> {
+        SampleMissListenerBuilder {
+            statesref: self.statesref,
+            handler: self.handler,
+        }
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<Handler> Resolvable for SampleMissListenerBuilder<'_, Handler>
+where
+    Handler: IntoHandler<Miss> + Send,
+    Handler::Handler: Send,
+{
+    type To = ZResult<SampleMissListener<Handler::Handler>>;
+}
+
+#[zenoh_macros::unstable]
+impl<Handler> Wait for SampleMissListenerBuilder<'_, Handler>
+where
+    Handler: IntoHandler<Miss> + Send,
+    Handler::Handler: Send,
+{
+    #[zenoh_macros::unstable]
+    fn wait(self) -> <Self as Resolvable>::To {
+        let (callback, handler) = self.handler.into_handler();
+        let id = zlock!(self.statesref).register_miss_callback(callback);
+        Ok(SampleMissListener {
+            id,
+            statesref: self.statesref.clone(),
+            handler,
+        })
+    }
+}
+
+#[zenoh_macros::unstable]
+impl<Handler> IntoFuture for SampleMissListenerBuilder<'_, Handler>
+where
+    Handler: IntoHandler<Miss> + Send,
+    Handler::Handler: Send,
+{
+    type Output = <Self as Resolvable>::To;
+    type IntoFuture = Ready<<Self as Resolvable>::To>;
+
+    #[zenoh_macros::unstable]
+    fn into_future(self) -> Self::IntoFuture {
+        std::future::ready(self.wait())
+    }
+}
+
+#[zenoh_macros::unstable]
+impl Resolvable for SampleMissListenerBuilder<'_, Callback<Miss>, true> {
+    type To = ZResult<()>;
+}
+
+#[zenoh_macros::unstable]
+impl Wait for SampleMissListenerBuilder<'_, Callback<Miss>, true> {
+    #[zenoh_macros::unstable]
+    fn wait(self) -> <Self as Resolvable>::To {
+        let (callback, _) = self.handler.into_handler();
+        zlock!(self.statesref).register_miss_callback(callback);
+        Ok(())
+    }
+}
+
+#[zenoh_macros::unstable]
+impl IntoFuture for SampleMissListenerBuilder<'_, Callback<Miss>, true> {
+    type Output = <Self as Resolvable>::To;
+    type IntoFuture = Ready<<Self as Resolvable>::To>;
+
+    #[zenoh_macros::unstable]
+    fn into_future(self) -> Self::IntoFuture {
+        std::future::ready(self.wait())
+    }
+}
diff --git a/zenoh-ext/src/lib.rs b/zenoh-ext/src/lib.rs
index 4bab50804e..2d73bd16c7 100644
--- a/zenoh-ext/src/lib.rs
+++ b/zenoh-ext/src/lib.rs
@@ -12,10 +12,18 @@
 //   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
 //
 #[cfg(feature = "unstable")]
+mod advanced_cache;
+#[cfg(feature = "unstable")]
+mod advanced_publisher;
+#[cfg(feature = "unstable")]
+mod advanced_subscriber;
+#[cfg(feature = "unstable")]
 pub mod group;
 #[cfg(feature = "unstable")]
 mod publication_cache;
 #[cfg(feature = "unstable")]
+mod publisher_ext;
+#[cfg(feature = "unstable")]
 mod querying_subscriber;
 mod serialization;
 #[cfg(feature = "unstable")]
@@ -30,12 +38,20 @@ pub use crate::serialization::{
     ZReadIter, ZSerializer,
 };
 #[cfg(feature = "unstable")]
+#[allow(deprecated)]
 pub use crate::{
+    advanced_cache::{CacheConfig, RepliesConfig},
+    advanced_publisher::{AdvancedPublisher, AdvancedPublisherBuilder},
+    advanced_subscriber::{
+        AdvancedSubscriber, AdvancedSubscriberBuilder, HistoryConfig, Miss, RecoveryConfig,
+        SampleMissHandlerUndeclaration, SampleMissListener, SampleMissListenerBuilder,
+    },
     publication_cache::{PublicationCache, PublicationCacheBuilder},
+    publisher_ext::AdvancedPublisherBuilderExt,
     querying_subscriber::{
         ExtractSample, FetchingSubscriber, FetchingSubscriberBuilder, KeySpace, LivelinessSpace,
         QueryingSubscriberBuilder, UserSpace,
     },
     session_ext::SessionExt,
-    subscriber_ext::{SubscriberBuilderExt, SubscriberForward},
+    subscriber_ext::{AdvancedSubscriberBuilderExt, SubscriberBuilderExt, SubscriberForward},
 };
diff --git a/zenoh-ext/src/publication_cache.rs b/zenoh-ext/src/publication_cache.rs
index 403a887100..bdc1d3d11e 100644
--- a/zenoh-ext/src/publication_cache.rs
+++ b/zenoh-ext/src/publication_cache.rs
@@ -31,6 +31,7 @@ use zenoh::{
 /// The builder of PublicationCache, allowing to configure it.
 #[zenoh_macros::unstable]
 #[must_use = "Resolvables do nothing unless you resolve them using `.await` or `zenoh::Wait::wait`"]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
 pub struct PublicationCacheBuilder<'a, 'b, 'c, const BACKGROUND: bool = false> {
     session: &'a Session,
     pub_key_expr: ZResult<KeyExpr<'b>>,
@@ -41,6 +42,8 @@ pub struct PublicationCacheBuilder<'a, 'b, 'c, const BACKGROUND: bool = false> {
     resources_limit: Option<usize>,
 }
 
+#[allow(deprecated)]
+#[zenoh_macros::unstable]
 impl<'a, 'b, 'c> PublicationCacheBuilder<'a, 'b, 'c> {
     pub(crate) fn new(
         session: &'a Session,
@@ -58,6 +61,8 @@ impl<'a, 'b, 'c> PublicationCacheBuilder<'a, 'b, 'c> {
     }
 
     /// Change the prefix used for queryable.
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn queryable_prefix<TryIntoKeyExpr>(mut self, queryable_prefix: TryIntoKeyExpr) -> Self
     where
         TryIntoKeyExpr: TryInto<KeyExpr<'c>>,
@@ -71,29 +76,38 @@ impl<'a, 'b, 'c> PublicationCacheBuilder<'a, 'b, 'c> {
     /// to the ones that have the given [`Locality`](zenoh::prelude::Locality).
     #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn queryable_allowed_origin(mut self, origin: Locality) -> Self {
         self.queryable_origin = Some(origin);
         self
     }
 
     /// Set completeness option for the queryable.
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn queryable_complete(mut self, complete: bool) -> Self {
         self.complete = Some(complete);
         self
     }
 
     /// Change the history size for each resource.
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn history(mut self, history: usize) -> Self {
         self.history = history;
         self
     }
 
     /// Change the limit number of cached resources.
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn resources_limit(mut self, limit: usize) -> Self {
         self.resources_limit = Some(limit);
         self
     }
 
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn background(self) -> PublicationCacheBuilder<'a, 'b, 'c, true> {
         PublicationCacheBuilder {
             session: self.session,
@@ -107,52 +121,73 @@ impl<'a, 'b, 'c> PublicationCacheBuilder<'a, 'b, 'c> {
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl Resolvable for PublicationCacheBuilder<'_, '_, '_> {
     type To = ZResult<PublicationCache>;
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl Wait for PublicationCacheBuilder<'_, '_, '_> {
+    #[zenoh_macros::unstable]
     fn wait(self) -> <Self as Resolvable>::To {
         PublicationCache::new(self)
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl IntoFuture for PublicationCacheBuilder<'_, '_, '_> {
     type Output = <Self as Resolvable>::To;
     type IntoFuture = Ready<<Self as Resolvable>::To>;
 
+    #[zenoh_macros::unstable]
     fn into_future(self) -> Self::IntoFuture {
         std::future::ready(self.wait())
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl Resolvable for PublicationCacheBuilder<'_, '_, '_, true> {
     type To = ZResult<()>;
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl Wait for PublicationCacheBuilder<'_, '_, '_, true> {
+    #[zenoh_macros::unstable]
     fn wait(self) -> <Self as Resolvable>::To {
         PublicationCache::new(self).map(|_| ())
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl IntoFuture for PublicationCacheBuilder<'_, '_, '_, true> {
     type Output = <Self as Resolvable>::To;
     type IntoFuture = Ready<<Self as Resolvable>::To>;
 
+    #[zenoh_macros::unstable]
     fn into_future(self) -> Self::IntoFuture {
         std::future::ready(self.wait())
     }
 }
 
+/// [`PublicationCache`].
 #[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
 pub struct PublicationCache {
     local_sub: Subscriber<FifoChannelHandler<Sample>>,
     _queryable: Queryable<FifoChannelHandler<Query>>,
     task: TerminatableTask,
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl PublicationCache {
+    #[zenoh_macros::unstable]
     fn new<const BACKGROUND: bool>(
         conf: PublicationCacheBuilder<'_, '_, '_, BACKGROUND>,
     ) -> ZResult<PublicationCache> {
@@ -297,7 +332,9 @@ impl PublicationCache {
     }
 
     /// Undeclare this [`PublicationCache`]`.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn undeclare(self) -> impl Resolve<ZResult<()>> {
         ResolveFuture::new(async move {
             let PublicationCache {
@@ -313,11 +350,15 @@ impl PublicationCache {
     }
 
     #[zenoh_macros::internal]
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn set_background(&mut self, background: bool) {
         self.local_sub.set_background(background);
         self._queryable.set_background(background);
     }
 
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn key_expr(&self) -> &KeyExpr<'static> {
         self.local_sub.key_expr()
     }
diff --git a/zenoh-ext/src/publisher_ext.rs b/zenoh-ext/src/publisher_ext.rs
new file mode 100644
index 0000000000..de045d0ff0
--- /dev/null
+++ b/zenoh-ext/src/publisher_ext.rs
@@ -0,0 +1,73 @@
+//
+// Copyright (c) 2023 ZettaScale Technology
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License 2.0 which is available at
+// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+//
+// Contributors:
+//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
+//
+use zenoh::pubsub::PublisherBuilder;
+
+use crate::{advanced_cache::CacheConfig, AdvancedPublisherBuilder};
+
+/// Some extensions to the [`zenoh::publication::PublisherBuilder`](zenoh::publication::PublisherBuilder)
+#[zenoh_macros::unstable]
+pub trait AdvancedPublisherBuilderExt<'a, 'b, 'c> {
+    /// Allow matching [`AdvancedSubscribers`](crate::AdvancedSubscriber) to recover history and/or missed samples.
+    #[zenoh_macros::unstable]
+    fn cache(self, config: CacheConfig) -> AdvancedPublisherBuilder<'a, 'b, 'c>;
+
+    /// Allow matching [`AdvancedSubscribers`](crate::AdvancedSubscriber) to detect lost samples
+    /// and optionally ask for retransimission.
+    ///
+    /// Retransmission can only be achieved if [`cache`](crate::AdvancedPublisherBuilder::cache) is also enabled.
+    #[zenoh_macros::unstable]
+    fn sample_miss_detection(self) -> AdvancedPublisherBuilder<'a, 'b, 'c>;
+
+    /// Allow this publisher to be detected by [`AdvancedSubscribers`](crate::AdvancedSubscriber).
+    ///
+    /// This allows [`AdvancedSubscribers`](crate::AdvancedSubscriber) to retrieve the local history.
+    #[zenoh_macros::unstable]
+    fn publisher_detection(self) -> AdvancedPublisherBuilder<'a, 'b, 'c>;
+
+    /// Turn this `Publisher` into an `AdvancedPublisher`.
+    #[zenoh_macros::unstable]
+    fn advanced(self) -> AdvancedPublisherBuilder<'a, 'b, 'c>;
+}
+
+#[zenoh_macros::unstable]
+impl<'a, 'b, 'c> AdvancedPublisherBuilderExt<'a, 'b, 'c> for PublisherBuilder<'a, 'b> {
+    /// Allow matching [`AdvancedSubscribers`](crate::AdvancedSubscriber) to recover history and/or missed samples.
+    #[zenoh_macros::unstable]
+    fn cache(self, config: CacheConfig) -> AdvancedPublisherBuilder<'a, 'b, 'c> {
+        AdvancedPublisherBuilder::new(self).cache(config)
+    }
+
+    /// Allow matching [`AdvancedSubscribers`](crate::AdvancedSubscriber) to detect lost samples
+    /// and optionally ask for retransimission.
+    ///
+    /// Retransmission can only be achieved if [`cache`](crate::AdvancedPublisherBuilder::cache) is also enabled.
+    #[zenoh_macros::unstable]
+    fn sample_miss_detection(self) -> AdvancedPublisherBuilder<'a, 'b, 'c> {
+        AdvancedPublisherBuilder::new(self).sample_miss_detection()
+    }
+
+    /// Allow this publisher to be detected by [`AdvancedSubscribers`](crate::AdvancedSubscriber).
+    ///
+    /// This allows [`AdvancedSubscribers`](crate::AdvancedSubscriber) to retrieve the local history.
+    #[zenoh_macros::unstable]
+    fn publisher_detection(self) -> AdvancedPublisherBuilder<'a, 'b, 'c> {
+        AdvancedPublisherBuilder::new(self).publisher_detection()
+    }
+
+    /// Turn this `Publisher` into an `AdvancedPublisher`.
+    #[zenoh_macros::unstable]
+    fn advanced(self) -> AdvancedPublisherBuilder<'a, 'b, 'c> {
+        AdvancedPublisherBuilder::new(self)
+    }
+}
diff --git a/zenoh-ext/src/querying_subscriber.rs b/zenoh-ext/src/querying_subscriber.rs
index fa9981e2b5..9a4943756b 100644
--- a/zenoh-ext/src/querying_subscriber.rs
+++ b/zenoh-ext/src/querying_subscriber.rs
@@ -33,6 +33,7 @@ use zenoh::{
 
 /// The space of keys to use in a [`FetchingSubscriber`].
 #[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
 pub enum KeySpace {
     User,
     Liveliness,
@@ -42,8 +43,10 @@ pub enum KeySpace {
 #[zenoh_macros::unstable]
 #[non_exhaustive]
 #[derive(Debug, Clone, Copy)]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
 pub struct UserSpace;
 
+#[allow(deprecated)]
 impl From<UserSpace> for KeySpace {
     fn from(_: UserSpace) -> Self {
         KeySpace::User
@@ -54,9 +57,13 @@ impl From<UserSpace> for KeySpace {
 #[zenoh_macros::unstable]
 #[non_exhaustive]
 #[derive(Debug, Clone, Copy)]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
 pub struct LivelinessSpace;
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl From<LivelinessSpace> for KeySpace {
+    #[zenoh_macros::unstable]
     fn from(_: LivelinessSpace) -> Self {
         KeySpace::Liveliness
     }
@@ -65,6 +72,7 @@ impl From<LivelinessSpace> for KeySpace {
 /// The builder of [`FetchingSubscriber`], allowing to configure it.
 #[zenoh_macros::unstable]
 #[must_use = "Resolvables do nothing unless you resolve them using `.await` or `zenoh::Wait::wait`"]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
 pub struct QueryingSubscriberBuilder<'a, 'b, KeySpace, Handler, const BACKGROUND: bool = false> {
     pub(crate) session: &'a Session,
     pub(crate) key_expr: ZResult<KeyExpr<'b>>,
@@ -78,9 +86,13 @@ pub struct QueryingSubscriberBuilder<'a, 'b, KeySpace, Handler, const BACKGROUND
     pub(crate) handler: Handler,
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<'a, 'b, KeySpace> QueryingSubscriberBuilder<'a, 'b, KeySpace, DefaultHandler> {
     /// Add callback to [`FetchingSubscriber`].
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn callback<F>(
         self,
         callback: F,
@@ -99,7 +111,9 @@ impl<'a, 'b, KeySpace> QueryingSubscriberBuilder<'a, 'b, KeySpace, DefaultHandle
     ///
     /// Subscriber will not be undeclared when dropped, with the callback running
     /// in background until the session is closed.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn callback_mut<F>(
         self,
         callback: F,
@@ -111,7 +125,9 @@ impl<'a, 'b, KeySpace> QueryingSubscriberBuilder<'a, 'b, KeySpace, DefaultHandle
     }
 
     /// Use the given handler to receive Samples.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn with<Handler>(
         self,
         handler: Handler,
@@ -146,10 +162,15 @@ impl<'a, 'b, KeySpace> QueryingSubscriberBuilder<'a, 'b, KeySpace, DefaultHandle
     }
 }
 
+#[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 impl<'a, 'b, KeySpace> QueryingSubscriberBuilder<'a, 'b, KeySpace, Callback<Sample>> {
     /// Register the subscriber callback to be run in background until the session is closed.
     ///
     /// Background builder doesn't return a `FetchingSubscriber` object anymore.
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn background(self) -> QueryingSubscriberBuilder<'a, 'b, KeySpace, Callback<Sample>, true> {
         QueryingSubscriberBuilder {
             session: self.session,
@@ -166,6 +187,9 @@ impl<'a, 'b, KeySpace> QueryingSubscriberBuilder<'a, 'b, KeySpace, Callback<Samp
     }
 }
 
+#[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 impl<'b, Handler, const BACKGROUND: bool>
     QueryingSubscriberBuilder<'_, 'b, UserSpace, Handler, BACKGROUND>
 {
@@ -175,13 +199,16 @@ impl<'b, Handler, const BACKGROUND: bool>
     /// to the ones that have the given [`Locality`](Locality).
     #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn allowed_origin(mut self, origin: Locality) -> Self {
         self.origin = origin;
         self
     }
 
     /// Change the selector to be used for queries.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn query_selector<IntoSelector>(mut self, query_selector: IntoSelector) -> Self
     where
         IntoSelector: TryInto<Selector<'b>>,
@@ -192,14 +219,18 @@ impl<'b, Handler, const BACKGROUND: bool>
     }
 
     /// Change the target to be used for queries.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     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.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn query_consolidation<QC: Into<QueryConsolidation>>(
         mut self,
         query_consolidation: QC,
@@ -209,24 +240,33 @@ impl<'b, Handler, const BACKGROUND: bool>
     }
 
     /// Change the accepted replies for queries.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn query_accept_replies(mut self, accept_replies: ReplyKeyExpr) -> Self {
         self.query_accept_replies = accept_replies;
         self
     }
 }
 
+#[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 impl<'a, 'b, KeySpace, Handler, const BACKGROUND: bool>
     QueryingSubscriberBuilder<'a, 'b, KeySpace, Handler, BACKGROUND>
 {
     /// Change the timeout to be used for queries.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn query_timeout(mut self, query_timeout: Duration) -> Self {
         self.query_timeout = query_timeout;
         self
     }
 
+    #[zenoh_macros::unstable]
     #[allow(clippy::type_complexity)]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     fn into_fetching_subscriber_builder(
         self,
     ) -> ZResult<
@@ -285,6 +325,8 @@ impl<'a, 'b, KeySpace, Handler, const BACKGROUND: bool>
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<KeySpace, Handler> Resolvable for QueryingSubscriberBuilder<'_, '_, KeySpace, Handler>
 where
     Handler: IntoHandler<Sample>,
@@ -293,17 +335,22 @@ where
     type To = ZResult<FetchingSubscriber<Handler::Handler>>;
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<KeySpace, Handler> Wait for QueryingSubscriberBuilder<'_, '_, KeySpace, Handler>
 where
     KeySpace: Into<self::KeySpace> + Clone,
     Handler: IntoHandler<Sample> + Send,
     Handler::Handler: Send,
 {
+    #[zenoh_macros::unstable]
     fn wait(self) -> <Self as Resolvable>::To {
         self.into_fetching_subscriber_builder()?.wait()
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<KeySpace, Handler> IntoFuture for QueryingSubscriberBuilder<'_, '_, KeySpace, Handler>
 where
     KeySpace: Into<self::KeySpace> + Clone,
@@ -313,24 +360,32 @@ where
     type Output = <Self as Resolvable>::To;
     type IntoFuture = Ready<<Self as Resolvable>::To>;
 
+    #[zenoh_macros::unstable]
     fn into_future(self) -> Self::IntoFuture {
         std::future::ready(self.wait())
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<KeySpace> Resolvable for QueryingSubscriberBuilder<'_, '_, KeySpace, Callback<Sample>, true> {
     type To = ZResult<()>;
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<KeySpace> Wait for QueryingSubscriberBuilder<'_, '_, KeySpace, Callback<Sample>, true>
 where
     KeySpace: Into<self::KeySpace> + Clone,
 {
+    #[zenoh_macros::unstable]
     fn wait(self) -> <Self as Resolvable>::To {
         self.into_fetching_subscriber_builder()?.wait()
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<KeySpace> IntoFuture for QueryingSubscriberBuilder<'_, '_, KeySpace, Callback<Sample>, true>
 where
     KeySpace: Into<self::KeySpace> + Clone,
@@ -338,6 +393,7 @@ where
     type Output = <Self as Resolvable>::To;
     type IntoFuture = Ready<<Self as Resolvable>::To>;
 
+    #[zenoh_macros::unstable]
     fn into_future(self) -> Self::IntoFuture {
         std::future::ready(self.wait())
     }
@@ -406,6 +462,8 @@ struct InnerState {
 /// The builder of [`FetchingSubscriber`], allowing to configure it.
 #[zenoh_macros::unstable]
 #[must_use = "Resolvables do nothing unless you resolve them using `.await` or `zenoh::Wait::wait`"]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 pub struct FetchingSubscriberBuilder<
     'a,
     'b,
@@ -426,6 +484,8 @@ pub struct FetchingSubscriberBuilder<
     pub(crate) phantom: std::marker::PhantomData<TryIntoSample>,
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<
         'a,
         KeySpace,
@@ -437,6 +497,7 @@ impl<
 where
     TryIntoSample: ExtractSample,
 {
+    #[zenoh_macros::unstable]
     fn with_static_keys(
         self,
     ) -> FetchingSubscriberBuilder<'a, 'static, KeySpace, Handler, Fetch, TryIntoSample> {
@@ -452,6 +513,9 @@ where
     }
 }
 
+#[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 impl<
         'a,
         'b,
@@ -463,7 +527,9 @@ where
     TryIntoSample: ExtractSample,
 {
     /// Add callback to [`FetchingSubscriber`].
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn callback<F>(
         self,
         callback: F,
@@ -482,7 +548,9 @@ where
     ///
     /// Subscriber will not be undeclared when dropped, with the callback running
     /// in background until the session is closed.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn callback_mut<F>(
         self,
         callback: F,
@@ -494,7 +562,9 @@ where
     }
 
     /// Use the given handler to receive Samples.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn with<Handler>(
         self,
         handler: Handler,
@@ -523,6 +593,9 @@ where
     }
 }
 
+#[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 impl<
         'a,
         'b,
@@ -536,6 +609,8 @@ where
     /// Register the subscriber callback to be run in background until the session is closed.
     ///
     /// Background builder doesn't return a `FetchingSubscriber` object anymore.
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn background(
         self,
     ) -> FetchingSubscriberBuilder<'a, 'b, KeySpace, Callback<Sample>, Fetch, TryIntoSample, true>
@@ -552,6 +627,9 @@ where
     }
 }
 
+#[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 impl<
         Handler,
         Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()>,
@@ -565,12 +643,15 @@ where
     /// to the ones that have the given [`Locality`](Locality).
     #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn allowed_origin(mut self, origin: Locality) -> Self {
         self.origin = origin;
         self
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<
         KeySpace,
         Handler,
@@ -585,6 +666,8 @@ where
     type To = ZResult<FetchingSubscriber<Handler::Handler>>;
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<
         KeySpace,
         Handler,
@@ -597,11 +680,14 @@ where
     Handler::Handler: Send,
     TryIntoSample: ExtractSample + Send + Sync,
 {
+    #[zenoh_macros::unstable]
     fn wait(self) -> <Self as Resolvable>::To {
         FetchingSubscriber::new(self.with_static_keys())
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<
         KeySpace,
         Handler,
@@ -617,11 +703,14 @@ where
     type Output = <Self as Resolvable>::To;
     type IntoFuture = Ready<<Self as Resolvable>::To>;
 
+    #[zenoh_macros::unstable]
     fn into_future(self) -> Self::IntoFuture {
         std::future::ready(self.wait())
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<
         KeySpace,
         Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()>,
@@ -634,6 +723,8 @@ where
     type To = ZResult<()>;
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<
         KeySpace,
         Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()> + Send + Sync,
@@ -644,6 +735,7 @@ where
     KeySpace: Into<self::KeySpace>,
     TryIntoSample: ExtractSample + Send + Sync,
 {
+    #[zenoh_macros::unstable]
     fn wait(self) -> <Self as Resolvable>::To {
         FetchingSubscriber::new(self.with_static_keys())?
             .subscriber
@@ -652,6 +744,8 @@ where
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<
         KeySpace,
         Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()> + Send + Sync,
@@ -665,6 +759,7 @@ where
     type Output = <Self as Resolvable>::To;
     type IntoFuture = Ready<<Self as Resolvable>::To>;
 
+    #[zenoh_macros::unstable]
     fn into_future(self) -> Self::IntoFuture {
         std::future::ready(self.wait())
     }
@@ -702,6 +797,7 @@ where
 /// # }
 /// ```
 #[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
 pub struct FetchingSubscriber<Handler> {
     subscriber: Subscriber<()>,
     callback: Callback<Sample>,
@@ -709,19 +805,28 @@ pub struct FetchingSubscriber<Handler> {
     handler: Handler,
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<Handler> std::ops::Deref for FetchingSubscriber<Handler> {
     type Target = Handler;
+    #[zenoh_macros::unstable]
     fn deref(&self) -> &Self::Target {
         &self.handler
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<Handler> std::ops::DerefMut for FetchingSubscriber<Handler> {
+    #[zenoh_macros::unstable]
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.handler
     }
 }
 
+#[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 impl<Handler> FetchingSubscriber<Handler> {
     fn new<
         'a,
@@ -805,18 +910,24 @@ impl<Handler> FetchingSubscriber<Handler> {
     }
 
     /// Undeclare this [`FetchingSubscriber`]`.
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn undeclare(self) -> impl Resolve<ZResult<()>> {
         self.subscriber.undeclare()
     }
 
+    #[zenoh_macros::unstable]
     #[zenoh_macros::internal]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn set_background(&mut self, background: bool) {
         self.subscriber.set_background(background)
     }
 
     /// Return the key expression of this FetchingSubscriber
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn key_expr(&self) -> &KeyExpr<'static> {
         self.subscriber.key_expr()
     }
@@ -857,7 +968,9 @@ impl<Handler> FetchingSubscriber<Handler> {
     ///     .unwrap();
     /// # }
     /// ```
+    #[zenoh_macros::unstable]
     #[inline]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     pub fn fetch<
         Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()> + Send + Sync,
         TryIntoSample,
@@ -936,6 +1049,8 @@ impl Drop for RepliesHandler {
 /// ```
 #[zenoh_macros::unstable]
 #[must_use = "Resolvables do nothing unless you resolve them using `.await` or `zenoh::Wait::wait`"]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 pub struct FetchBuilder<
     Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()>,
     TryIntoSample,
@@ -948,6 +1063,8 @@ pub struct FetchBuilder<
     callback: Callback<Sample>,
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()>, TryIntoSample>
     Resolvable for FetchBuilder<Fetch, TryIntoSample>
 where
@@ -956,17 +1073,22 @@ where
     type To = ZResult<()>;
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()>, TryIntoSample> Wait
     for FetchBuilder<Fetch, TryIntoSample>
 where
     TryIntoSample: ExtractSample,
 {
+    #[zenoh_macros::unstable]
     fn wait(self) -> <Self as Resolvable>::To {
         let handler = register_handler(self.state, self.callback);
         run_fetch(self.fetch, handler)
     }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()>, TryIntoSample>
     IntoFuture for FetchBuilder<Fetch, TryIntoSample>
 where
@@ -975,6 +1097,7 @@ where
     type Output = <Self as Resolvable>::To;
     type IntoFuture = Ready<<Self as Resolvable>::To>;
 
+    #[zenoh_macros::unstable]
     fn into_future(self) -> Self::IntoFuture {
         std::future::ready(self.wait())
     }
@@ -986,6 +1109,8 @@ fn register_handler(state: Arc<Mutex<InnerState>>, callback: Callback<Sample>) -
     RepliesHandler { state, callback }
 }
 
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 fn run_fetch<
     Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()>,
     TryIntoSample,
@@ -1007,11 +1132,16 @@ where
     }))
 }
 
+/// [`ExtractSample`].
 #[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 pub trait ExtractSample {
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     fn extract(self) -> ZResult<Sample>;
 }
 
+#[allow(deprecated)]
 impl ExtractSample for Reply {
     fn extract(self) -> ZResult<Sample> {
         self.into_result().map_err(|e| zerror!("{:?}", e).into())
diff --git a/zenoh-ext/src/session_ext.rs b/zenoh-ext/src/session_ext.rs
index 9d3c430aaf..8daa25e78d 100644
--- a/zenoh-ext/src/session_ext.rs
+++ b/zenoh-ext/src/session_ext.rs
@@ -14,12 +14,16 @@
 
 use zenoh::{key_expr::KeyExpr, session::Session, Error};
 
+#[allow(deprecated)]
 use super::PublicationCacheBuilder;
 
 /// Some extensions to the [`zenoh::Session`](zenoh::Session)
 #[zenoh_macros::unstable]
+#[allow(deprecated)]
 pub trait SessionExt {
     // REVIEW(fuzzypixelz): this doc test is the only one to use the programmatic configuration API..
+    /// Declare a [`PublicationCache`](crate::PublicationCache).
+    ///
     /// Examples:
     /// ```
     /// # #[tokio::main]
@@ -37,6 +41,8 @@ pub trait SessionExt {
     /// }).await;
     /// # }
     /// ```
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+    #[zenoh_macros::unstable]
     fn declare_publication_cache<'a, 'b, 'c, TryIntoKeyExpr>(
         &'a self,
         pub_key_expr: TryIntoKeyExpr,
@@ -46,7 +52,9 @@ pub trait SessionExt {
         <TryIntoKeyExpr as TryInto<KeyExpr<'b>>>::Error: Into<Error>;
 }
 
+#[allow(deprecated)]
 impl SessionExt for Session {
+    #[zenoh_macros::unstable]
     fn declare_publication_cache<'a, 'b, 'c, TryIntoKeyExpr>(
         &'a self,
         pub_key_expr: TryIntoKeyExpr,
diff --git a/zenoh-ext/src/subscriber_ext.rs b/zenoh-ext/src/subscriber_ext.rs
index 8d931726c3..4441ebabdc 100644
--- a/zenoh-ext/src/subscriber_ext.rs
+++ b/zenoh-ext/src/subscriber_ext.rs
@@ -23,8 +23,10 @@ use zenoh::{
     Result as ZResult,
 };
 
+#[allow(deprecated)]
 use crate::{
-    querying_subscriber::QueryingSubscriberBuilder, ExtractSample, FetchingSubscriberBuilder,
+    advanced_subscriber::HistoryConfig, querying_subscriber::QueryingSubscriberBuilder,
+    AdvancedSubscriberBuilder, ExtractSample, FetchingSubscriberBuilder, RecoveryConfig,
 };
 
 /// Allows writing `subscriber.forward(receiver)` instead of `subscriber.stream().map(Ok).forward(publisher)`
@@ -37,6 +39,7 @@ impl<'a, S> SubscriberForward<'a, S> for Subscriber<FifoChannelHandler<Sample>>
 where
     S: futures::sink::Sink<Sample>,
 {
+    #[zenoh_macros::unstable]
     type Output =
         Forward<Map<fifo::RecvStream<'a, Sample>, fn(Sample) -> Result<Sample, S::Error>>, S>;
     fn forward(&'a mut self, sink: S) -> Self::Output {
@@ -46,6 +49,8 @@ where
 
 /// Some extensions to the [`zenoh::subscriber::SubscriberBuilder`](zenoh::pubsub::SubscriberBuilder)
 #[zenoh_macros::unstable]
+#[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
+#[allow(deprecated)]
 pub trait SubscriberBuilderExt<'a, 'b, Handler> {
     type KeySpace;
 
@@ -82,6 +87,8 @@ pub trait SubscriberBuilderExt<'a, 'b, Handler> {
     /// }
     /// # }
     /// ```
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     fn fetching<
         Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()>,
         TryIntoSample,
@@ -120,9 +127,39 @@ pub trait SubscriberBuilderExt<'a, 'b, Handler> {
     /// }
     /// # }
     /// ```
+    #[zenoh_macros::unstable]
+    #[deprecated = "Use `AdvancedPublisher` and `AdvancedSubscriber` instead."]
     fn querying(self) -> QueryingSubscriberBuilder<'a, 'b, Self::KeySpace, Handler>;
 }
 
+/// Some extensions to the [`zenoh::subscriber::SubscriberBuilder`](zenoh::pubsub::SubscriberBuilder)
+#[zenoh_macros::unstable]
+pub trait AdvancedSubscriberBuilderExt<'a, 'b, 'c, Handler> {
+    /// Enable query for historical data.
+    ///
+    /// History can only be retransmitted by [`AdvancedPublishers`](crate::AdvancedPublisher) that enable [`cache`](crate::AdvancedPublisherBuilder::cache).
+    #[zenoh_macros::unstable]
+    fn history(self, config: HistoryConfig) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Handler>;
+
+    /// Ask for retransmission of detected lost Samples.
+    ///
+    /// Retransmission can only be achieved by [`AdvancedPublishers`](crate::AdvancedPublisher)
+    /// that enable [`cache`](crate::AdvancedPublisherBuilder::cache) and
+    /// [`sample_miss_detection`](crate::AdvancedPublisherBuilder::sample_miss_detection).
+    #[zenoh_macros::unstable]
+    fn recovery(self, conf: RecoveryConfig) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Handler>;
+
+    /// Allow this subscriber to be detected through liveliness.
+    #[zenoh_macros::unstable]
+    fn subscriber_detection(self) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Handler>;
+
+    /// Turn this `Subscriber`into an `AdvancedSubscriber`.
+    #[zenoh_macros::unstable]
+    fn advanced(self) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Handler>;
+}
+
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<'a, 'b, Handler> SubscriberBuilderExt<'a, 'b, Handler> for SubscriberBuilder<'a, 'b, Handler> {
     type KeySpace = crate::UserSpace;
 
@@ -159,6 +196,7 @@ impl<'a, 'b, Handler> SubscriberBuilderExt<'a, 'b, Handler> for SubscriberBuilde
     /// }
     /// # }
     /// ```
+    #[zenoh_macros::unstable]
     fn fetching<
         Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()>,
         TryIntoSample,
@@ -208,6 +246,7 @@ impl<'a, 'b, Handler> SubscriberBuilderExt<'a, 'b, Handler> for SubscriberBuilde
     /// }
     /// # }
     /// ```
+    #[zenoh_macros::unstable]
     fn querying(self) -> QueryingSubscriberBuilder<'a, 'b, Self::KeySpace, Handler> {
         QueryingSubscriberBuilder {
             session: self.session,
@@ -227,6 +266,43 @@ impl<'a, 'b, Handler> SubscriberBuilderExt<'a, 'b, Handler> for SubscriberBuilde
     }
 }
 
+#[zenoh_macros::unstable]
+impl<'a, 'b, 'c, Handler> AdvancedSubscriberBuilderExt<'a, 'b, 'c, Handler>
+    for SubscriberBuilder<'a, 'b, Handler>
+{
+    /// Enable query for historical data.
+    ///
+    /// History can only be retransmitted by [`AdvancedPublishers`](crate::AdvancedPublisher) that enable [`cache`](crate::AdvancedPublisherBuilder::cache).
+    #[zenoh_macros::unstable]
+    fn history(self, config: HistoryConfig) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Handler> {
+        AdvancedSubscriberBuilder::new(self).history(config)
+    }
+
+    /// Ask for retransmission of detected lost Samples.
+    ///
+    /// Retransmission can only be achieved by [`AdvancedPublishers`](crate::AdvancedPublisher)
+    /// that enable [`cache`](crate::AdvancedPublisherBuilder::cache) and
+    /// [`sample_miss_detection`](crate::AdvancedPublisherBuilder::sample_miss_detection).
+    #[zenoh_macros::unstable]
+    fn recovery(self, conf: RecoveryConfig) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Handler> {
+        AdvancedSubscriberBuilder::new(self).recovery(conf)
+    }
+
+    /// Allow this subscriber to be detected through liveliness.
+    #[zenoh_macros::unstable]
+    fn subscriber_detection(self) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Handler> {
+        AdvancedSubscriberBuilder::new(self).subscriber_detection()
+    }
+
+    /// Turn this `Subscriber`into an `AdvancedSubscriber`.
+    #[zenoh_macros::unstable]
+    fn advanced(self) -> AdvancedSubscriberBuilder<'a, 'b, 'c, Handler> {
+        AdvancedSubscriberBuilder::new(self)
+    }
+}
+
+#[zenoh_macros::unstable]
+#[allow(deprecated)]
 impl<'a, 'b, Handler> SubscriberBuilderExt<'a, 'b, Handler>
     for LivelinessSubscriberBuilder<'a, 'b, Handler>
 {
@@ -268,6 +344,7 @@ impl<'a, 'b, Handler> SubscriberBuilderExt<'a, 'b, Handler>
     /// }
     /// # }
     /// ```
+    #[zenoh_macros::unstable]
     fn fetching<
         Fetch: FnOnce(Box<dyn Fn(TryIntoSample) + Send + Sync>) -> ZResult<()>,
         TryIntoSample,
@@ -319,6 +396,7 @@ impl<'a, 'b, Handler> SubscriberBuilderExt<'a, 'b, Handler>
     /// }
     /// # }
     /// ```
+    #[zenoh_macros::unstable]
     fn querying(self) -> QueryingSubscriberBuilder<'a, 'b, Self::KeySpace, Handler> {
         QueryingSubscriberBuilder {
             session: self.session,
diff --git a/zenoh-ext/tests/advanced.rs b/zenoh-ext/tests/advanced.rs
new file mode 100644
index 0000000000..f7d3593c63
--- /dev/null
+++ b/zenoh-ext/tests/advanced.rs
@@ -0,0 +1,715 @@
+//
+// Copyright (c) 2024 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, <zenoh@zettascale.tech>
+//
+
+use zenoh::sample::SampleKind;
+use zenoh_config::{EndPoint, ModeDependentValue, WhatAmI};
+use zenoh_ext::{
+    AdvancedPublisherBuilderExt, AdvancedSubscriberBuilderExt, CacheConfig, HistoryConfig,
+    RecoveryConfig,
+};
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+async fn test_advanced_history() {
+    use std::time::Duration;
+
+    use zenoh::internal::ztimeout;
+
+    const TIMEOUT: Duration = Duration::from_secs(60);
+    const SLEEP: Duration = Duration::from_secs(1);
+    const PEER1_ENDPOINT: &str = "tcp/localhost:47450";
+
+    const ADVANCED_HISTORY_KEYEXPR: &str = "test/advanced/history";
+
+    zenoh_util::init_log_from_env_or("error");
+
+    let peer1 = {
+        let mut c = zenoh::Config::default();
+        c.listen
+            .endpoints
+            .set(vec![PEER1_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        c.timestamping
+            .set_enabled(Some(ModeDependentValue::Unique(true)))
+            .unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Peer));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Peer (1) ZID: {}", s.zid());
+        s
+    };
+
+    let publ = ztimeout!(peer1
+        .declare_publisher(ADVANCED_HISTORY_KEYEXPR)
+        .cache(CacheConfig::default().max_samples(3)))
+    .unwrap();
+    ztimeout!(publ.put("1")).unwrap();
+    ztimeout!(publ.put("2")).unwrap();
+    ztimeout!(publ.put("3")).unwrap();
+    ztimeout!(publ.put("4")).unwrap();
+
+    tokio::time::sleep(SLEEP).await;
+
+    let peer2 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![PEER1_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Peer));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Peer (2) ZID: {}", s.zid());
+        s
+    };
+
+    let sub = ztimeout!(peer2
+        .declare_subscriber(ADVANCED_HISTORY_KEYEXPR)
+        .history(HistoryConfig::default()))
+    .unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    ztimeout!(publ.put("5")).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "2");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "3");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "4");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "5");
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    publ.undeclare().await.unwrap();
+    // sub.undeclare().await.unwrap();
+
+    peer1.close().await.unwrap();
+    peer2.close().await.unwrap();
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+async fn test_advanced_retransmission() {
+    use std::time::Duration;
+
+    use zenoh::internal::ztimeout;
+
+    const TIMEOUT: Duration = Duration::from_secs(60);
+    const SLEEP: Duration = Duration::from_secs(1);
+    const RECONNECT_SLEEP: Duration = Duration::from_secs(5);
+    const ROUTER_ENDPOINT: &str = "tcp/localhost:47451";
+
+    const ADVANCED_RETRANSMISSION_KEYEXPR: &str = "test/advanced/retransmission";
+
+    zenoh_util::init_log_from_env_or("error");
+
+    let router = {
+        let mut c = zenoh::Config::default();
+        c.listen
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Router));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Router ZID: {}", s.zid());
+        s
+    };
+
+    let client1 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Client));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Client (1) ZID: {}", s.zid());
+        s
+    };
+
+    let client2 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Client));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Client (2) ZID: {}", s.zid());
+        s
+    };
+
+    let sub = ztimeout!(client2
+        .declare_subscriber(ADVANCED_RETRANSMISSION_KEYEXPR)
+        .recovery(RecoveryConfig::default()))
+    .unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    let publ = ztimeout!(client1
+        .declare_publisher(ADVANCED_RETRANSMISSION_KEYEXPR)
+        .cache(CacheConfig::default().max_samples(10))
+        .sample_miss_detection())
+    .unwrap();
+    ztimeout!(publ.put("1")).unwrap();
+
+    tokio::time::sleep(SLEEP).await;
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "1");
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    router.close().await.unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    ztimeout!(publ.put("2")).unwrap();
+    ztimeout!(publ.put("3")).unwrap();
+    ztimeout!(publ.put("4")).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    let router = {
+        let mut c = zenoh::Config::default();
+        c.listen
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Router));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Router ZID: {}", s.zid());
+        s
+    };
+    tokio::time::sleep(RECONNECT_SLEEP).await;
+
+    ztimeout!(publ.put("5")).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "2");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "3");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "4");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "5");
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    publ.undeclare().await.unwrap();
+    // sub.undeclare().await.unwrap();
+
+    client1.close().await.unwrap();
+    client2.close().await.unwrap();
+
+    router.close().await.unwrap();
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+async fn test_advanced_retransmission_periodic() {
+    use std::time::Duration;
+
+    use zenoh::internal::ztimeout;
+
+    const TIMEOUT: Duration = Duration::from_secs(60);
+    const SLEEP: Duration = Duration::from_secs(1);
+    const RECONNECT_SLEEP: Duration = Duration::from_secs(8);
+    const ROUTER_ENDPOINT: &str = "tcp/localhost:47452";
+
+    const ADVANCED_RETRANSMISSION_PERIODIC_KEYEXPR: &str = "test/advanced/retransmission/periodic";
+
+    zenoh_util::init_log_from_env_or("error");
+
+    let router = {
+        let mut c = zenoh::Config::default();
+        c.listen
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Router));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Router ZID: {}", s.zid());
+        s
+    };
+
+    let client1 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Client));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Client (1) ZID: {}", s.zid());
+        s
+    };
+
+    let client2 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Client));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Client (2) ZID: {}", s.zid());
+        s
+    };
+
+    let sub = ztimeout!(client2
+        .declare_subscriber(ADVANCED_RETRANSMISSION_PERIODIC_KEYEXPR)
+        .recovery(RecoveryConfig::default().periodic_queries(Some(Duration::from_secs(1)))))
+    .unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    let publ = ztimeout!(client1
+        .declare_publisher(ADVANCED_RETRANSMISSION_PERIODIC_KEYEXPR)
+        .cache(CacheConfig::default().max_samples(10))
+        .sample_miss_detection())
+    .unwrap();
+    ztimeout!(publ.put("1")).unwrap();
+
+    tokio::time::sleep(SLEEP).await;
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "1");
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    router.close().await.unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    ztimeout!(publ.put("2")).unwrap();
+    ztimeout!(publ.put("3")).unwrap();
+    ztimeout!(publ.put("4")).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    let router = {
+        let mut c = zenoh::Config::default();
+        c.listen
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Router));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Router ZID: {}", s.zid());
+        s
+    };
+    tokio::time::sleep(RECONNECT_SLEEP).await;
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "2");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "3");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "4");
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    publ.undeclare().await.unwrap();
+    // sub.undeclare().await.unwrap();
+
+    client1.close().await.unwrap();
+    client2.close().await.unwrap();
+
+    router.close().await.unwrap();
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+async fn test_advanced_sample_miss() {
+    use std::time::Duration;
+
+    use zenoh::internal::ztimeout;
+
+    const TIMEOUT: Duration = Duration::from_secs(60);
+    const SLEEP: Duration = Duration::from_secs(1);
+    const RECONNECT_SLEEP: Duration = Duration::from_secs(5);
+    const ROUTER_ENDPOINT: &str = "tcp/localhost:47453";
+
+    const ADVANCED_SAMPLE_MISS_KEYEXPR: &str = "test/advanced/sample_miss";
+
+    zenoh_util::init_log_from_env_or("error");
+
+    let router = {
+        let mut c = zenoh::Config::default();
+        c.listen
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Router));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Router ZID: {}", s.zid());
+        s
+    };
+
+    let client1 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Client));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Client (1) ZID: {}", s.zid());
+        s
+    };
+
+    let client2 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Client));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Client (2) ZID: {}", s.zid());
+        s
+    };
+
+    let sub = ztimeout!(client2
+        .declare_subscriber(ADVANCED_SAMPLE_MISS_KEYEXPR)
+        .advanced())
+    .unwrap();
+    let miss_listener = ztimeout!(sub.sample_miss_listener()).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    let publ = ztimeout!(client1
+        .declare_publisher(ADVANCED_SAMPLE_MISS_KEYEXPR)
+        .sample_miss_detection())
+    .unwrap();
+    ztimeout!(publ.put("1")).unwrap();
+
+    tokio::time::sleep(SLEEP).await;
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "1");
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    router.close().await.unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    ztimeout!(publ.put("2")).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    let router = {
+        let mut c = zenoh::Config::default();
+        c.listen
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Router));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Router ZID: {}", s.zid());
+        s
+    };
+    tokio::time::sleep(RECONNECT_SLEEP).await;
+
+    ztimeout!(publ.put("3")).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    let miss = ztimeout!(miss_listener.recv_async()).unwrap();
+    assert_eq!(miss.source(), publ.id());
+    assert_eq!(miss.nb(), 1);
+
+    assert!(miss_listener.try_recv().unwrap().is_none());
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "3");
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    publ.undeclare().await.unwrap();
+    // sub.undeclare().await.unwrap();
+
+    client1.close().await.unwrap();
+    client2.close().await.unwrap();
+
+    router.close().await.unwrap();
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+async fn test_advanced_retransmission_sample_miss() {
+    use std::time::Duration;
+
+    use zenoh::internal::ztimeout;
+
+    const TIMEOUT: Duration = Duration::from_secs(60);
+    const SLEEP: Duration = Duration::from_secs(1);
+    const RECONNECT_SLEEP: Duration = Duration::from_secs(5);
+    const ROUTER_ENDPOINT: &str = "tcp/localhost:47454";
+
+    const ADVANCED_RETRANSMISSION_SAMPLE_MISS_KEYEXPR: &str =
+        "test/advanced/retransmission/sample_miss";
+
+    zenoh_util::init_log_from_env_or("error");
+
+    let router = {
+        let mut c = zenoh::Config::default();
+        c.listen
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Router));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Router ZID: {}", s.zid());
+        s
+    };
+
+    let client1 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Client));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Client (1) ZID: {}", s.zid());
+        s
+    };
+
+    let client2 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Client));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Client (2) ZID: {}", s.zid());
+        s
+    };
+
+    let sub = ztimeout!(client2
+        .declare_subscriber(ADVANCED_RETRANSMISSION_SAMPLE_MISS_KEYEXPR)
+        .recovery(RecoveryConfig::default().periodic_queries(Some(Duration::from_secs(1)))))
+    .unwrap();
+    let miss_listener = ztimeout!(sub.sample_miss_listener()).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    let publ = ztimeout!(client1
+        .declare_publisher(ADVANCED_RETRANSMISSION_SAMPLE_MISS_KEYEXPR)
+        .cache(CacheConfig::default().max_samples(1))
+        .sample_miss_detection())
+    .unwrap();
+    ztimeout!(publ.put("1")).unwrap();
+
+    tokio::time::sleep(SLEEP).await;
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "1");
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    router.close().await.unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    ztimeout!(publ.put("2")).unwrap();
+    ztimeout!(publ.put("3")).unwrap();
+    ztimeout!(publ.put("4")).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    let router = {
+        let mut c = zenoh::Config::default();
+        c.listen
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Router));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Router ZID: {}", s.zid());
+        s
+    };
+    tokio::time::sleep(RECONNECT_SLEEP).await;
+
+    ztimeout!(publ.put("5")).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    let miss = ztimeout!(miss_listener.recv_async()).unwrap();
+    assert_eq!(miss.source(), publ.id());
+    assert_eq!(miss.nb(), 2);
+
+    assert!(miss_listener.try_recv().unwrap().is_none());
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "4");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "5");
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    publ.undeclare().await.unwrap();
+    // sub.undeclare().await.unwrap();
+
+    client1.close().await.unwrap();
+    client2.close().await.unwrap();
+
+    router.close().await.unwrap();
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+async fn test_advanced_late_joiner() {
+    use std::time::Duration;
+
+    use zenoh::internal::ztimeout;
+
+    const TIMEOUT: Duration = Duration::from_secs(60);
+    const SLEEP: Duration = Duration::from_secs(1);
+    const RECONNECT_SLEEP: Duration = Duration::from_secs(8);
+    const ROUTER_ENDPOINT: &str = "tcp/localhost:47455";
+
+    const ADVANCED_LATE_JOINER_KEYEXPR: &str = "test/advanced/late_joiner";
+
+    zenoh_util::init_log_from_env_or("error");
+
+    let peer1 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        c.timestamping
+            .set_enabled(Some(ModeDependentValue::Unique(true)))
+            .unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Peer));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Peer (1) ZID: {}", s.zid());
+        s
+    };
+
+    let peer2 = {
+        let mut c = zenoh::Config::default();
+        c.connect
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Peer));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Peer (2) ZID: {}", s.zid());
+        s
+    };
+
+    let sub = ztimeout!(peer2
+        .declare_subscriber(ADVANCED_LATE_JOINER_KEYEXPR)
+        .history(HistoryConfig::default().detect_late_publishers()))
+    .unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    let publ = ztimeout!(peer1
+        .declare_publisher(ADVANCED_LATE_JOINER_KEYEXPR)
+        .cache(CacheConfig::default().max_samples(10))
+        .publisher_detection())
+    .unwrap();
+    ztimeout!(publ.put("1")).unwrap();
+    ztimeout!(publ.put("2")).unwrap();
+    ztimeout!(publ.put("3")).unwrap();
+
+    tokio::time::sleep(SLEEP).await;
+
+    assert!(sub.try_recv().unwrap().is_none());
+    let router = {
+        let mut c = zenoh::Config::default();
+        c.listen
+            .endpoints
+            .set(vec![ROUTER_ENDPOINT.parse::<EndPoint>().unwrap()])
+            .unwrap();
+        c.scouting.multicast.set_enabled(Some(false)).unwrap();
+        let _ = c.set_mode(Some(WhatAmI::Router));
+        let s = ztimeout!(zenoh::open(c)).unwrap();
+        tracing::info!("Router ZID: {}", s.zid());
+        s
+    };
+    tokio::time::sleep(RECONNECT_SLEEP).await;
+
+    ztimeout!(publ.put("4")).unwrap();
+    tokio::time::sleep(SLEEP).await;
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "1");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "2");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "3");
+
+    let sample = ztimeout!(sub.recv_async()).unwrap();
+    assert_eq!(sample.kind(), SampleKind::Put);
+    assert_eq!(sample.payload().try_to_string().unwrap().as_ref(), "4");
+
+    assert!(sub.try_recv().unwrap().is_none());
+
+    publ.undeclare().await.unwrap();
+    // sub.undeclare().await.unwrap();
+
+    peer1.close().await.unwrap();
+    peer2.close().await.unwrap();
+
+    router.close().await.unwrap();
+}
diff --git a/zenoh-ext/tests/liveliness.rs b/zenoh-ext/tests/liveliness.rs
index aebbc52b5d..cc47cdca74 100644
--- a/zenoh-ext/tests/liveliness.rs
+++ b/zenoh-ext/tests/liveliness.rs
@@ -19,6 +19,7 @@ use zenoh::{
 };
 
 #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[allow(deprecated)]
 async fn test_liveliness_querying_subscriber_clique() {
     use std::time::Duration;
 
@@ -97,6 +98,7 @@ async fn test_liveliness_querying_subscriber_clique() {
 }
 
 #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[allow(deprecated)]
 async fn test_liveliness_querying_subscriber_brokered() {
     use std::time::Duration;
 
@@ -203,6 +205,7 @@ async fn test_liveliness_querying_subscriber_brokered() {
 }
 
 #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[allow(deprecated)]
 async fn test_liveliness_fetching_subscriber_clique() {
     use std::time::Duration;
 
@@ -285,6 +288,7 @@ async fn test_liveliness_fetching_subscriber_clique() {
 }
 
 #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[allow(deprecated)]
 async fn test_liveliness_fetching_subscriber_brokered() {
     use std::time::Duration;
 
diff --git a/zenoh/Cargo.toml b/zenoh/Cargo.toml
index 6a998b2962..39faaf148c 100644
--- a/zenoh/Cargo.toml
+++ b/zenoh/Cargo.toml
@@ -84,6 +84,7 @@ paste = { workspace = true }
 petgraph = { workspace = true }
 phf = { workspace = true }
 rand = { workspace = true, features = ["default"] }
+ref-cast = { workspace = true }
 serde = { workspace = true, features = ["default"] }
 serde_json = { workspace = true }
 socket2 = { workspace = true }
diff --git a/zenoh/src/api/admin.rs b/zenoh/src/api/admin.rs
index d043ec856c..28808ee664 100644
--- a/zenoh/src/api/admin.rs
+++ b/zenoh/src/api/admin.rs
@@ -19,6 +19,7 @@ use std::{
 
 use zenoh_core::{Result as ZResult, Wait};
 use zenoh_keyexpr::keyexpr;
+use zenoh_macros::ke;
 #[cfg(feature = "unstable")]
 use zenoh_protocol::core::Reliability;
 use zenoh_protocol::{core::WireExpr, network::NetworkMessage};
@@ -26,6 +27,7 @@ use zenoh_transport::{
     TransportEventHandler, TransportMulticastEventHandler, TransportPeer, TransportPeerEventHandler,
 };
 
+use crate as zenoh;
 use crate::{
     api::{
         encoding::Encoding,
@@ -38,39 +40,71 @@ use crate::{
     handlers::Callback,
 };
 
-lazy_static::lazy_static!(
-    static ref KE_STARSTAR: &'static keyexpr = unsafe { keyexpr::from_str_unchecked("**") };
-    static ref KE_PREFIX: &'static keyexpr = unsafe { keyexpr::from_str_unchecked("@") };
-    static ref KE_SESSION: &'static keyexpr = unsafe { keyexpr::from_str_unchecked("session") };
-    static ref KE_TRANSPORT_UNICAST: &'static keyexpr = unsafe { keyexpr::from_str_unchecked("transport/unicast") };
-    static ref KE_LINK: &'static keyexpr = unsafe { keyexpr::from_str_unchecked("link") };
-);
+#[cfg(feature = "internal")]
+pub static KE_AT: &keyexpr = ke!("@");
+#[cfg(not(feature = "internal"))]
+static KE_AT: &keyexpr = ke!("@");
+#[cfg(feature = "internal")]
+pub static KE_ADV_PREFIX: &keyexpr = ke!("@adv");
+#[cfg(not(feature = "internal"))]
+static KE_ADV_PREFIX: &keyexpr = ke!("@adv");
+#[cfg(feature = "internal")]
+pub static KE_PUB: &keyexpr = ke!("pub");
+#[cfg(not(feature = "internal"))]
+static KE_PUB: &keyexpr = ke!("pub");
+#[cfg(feature = "internal")]
+pub static KE_SUB: &keyexpr = ke!("sub");
+#[cfg(feature = "internal")]
+pub static KE_EMPTY: &keyexpr = ke!("_");
+#[cfg(not(feature = "internal"))]
+static KE_EMPTY: &keyexpr = ke!("_");
+#[cfg(feature = "internal")]
+pub static KE_STAR: &keyexpr = ke!("*");
+#[cfg(feature = "internal")]
+pub static KE_STARSTAR: &keyexpr = ke!("**");
+#[cfg(not(feature = "internal"))]
+static KE_STARSTAR: &keyexpr = ke!("**");
+
+static KE_SESSION: &keyexpr = ke!("session");
+static KE_TRANSPORT_UNICAST: &keyexpr = ke!("transport/unicast");
+static KE_LINK: &keyexpr = ke!("link");
 
 pub(crate) fn init(session: WeakSession) {
     if let Ok(own_zid) = keyexpr::new(&session.zid().to_string()) {
-        let admin_key = KeyExpr::from(*KE_PREFIX / own_zid / *KE_SESSION / *KE_STARSTAR);
-
         let _admin_qabl = session.declare_queryable_inner(
-            &admin_key,
+            &KeyExpr::from(KE_AT / own_zid / KE_SESSION / KE_STARSTAR),
+            true,
+            Locality::SessionLocal,
+            Callback::new(Arc::new({
+                let session = session.clone();
+                move |q| on_admin_query(&session, KE_AT, q)
+            })),
+        );
+
+        let adv_prefix = KE_ADV_PREFIX / KE_PUB / own_zid / KE_EMPTY / KE_EMPTY / KE_AT / KE_AT;
+
+        let _admin_adv_qabl = session.declare_queryable_inner(
+            &KeyExpr::from(&adv_prefix / own_zid / KE_SESSION / KE_STARSTAR),
             true,
             Locality::SessionLocal,
             Callback::new(Arc::new({
                 let session = session.clone();
-                move |q| on_admin_query(&session, q)
+                move |q| on_admin_query(&session, &adv_prefix, q)
             })),
         );
     }
 }
 
-pub(crate) fn on_admin_query(session: &WeakSession, query: Query) {
-    fn reply_peer(own_zid: &keyexpr, query: &Query, peer: TransportPeer) {
+pub(crate) fn on_admin_query(session: &WeakSession, prefix: &keyexpr, query: Query) {
+    fn reply_peer(prefix: &keyexpr, own_zid: &keyexpr, query: &Query, peer: TransportPeer) {
         let zid = peer.zid.to_string();
         if let Ok(zid) = keyexpr::new(&zid) {
-            let key_expr = *KE_PREFIX / own_zid / *KE_SESSION / *KE_TRANSPORT_UNICAST / zid;
+            let key_expr = prefix / own_zid / KE_SESSION / KE_TRANSPORT_UNICAST / zid;
             if query.key_expr().intersects(&key_expr) {
                 match serde_json::to_vec(&peer) {
                     Ok(bytes) => {
-                        let _ = query.reply(key_expr, bytes).wait();
+                        let reply_expr = KE_AT / own_zid / KE_SESSION / KE_TRANSPORT_UNICAST / zid;
+                        let _ = query.reply(reply_expr, bytes).wait();
                     }
                     Err(e) => tracing::debug!("Admin query error: {}", e),
                 }
@@ -80,17 +114,19 @@ pub(crate) fn on_admin_query(session: &WeakSession, query: Query) {
                 let mut s = DefaultHasher::new();
                 link.hash(&mut s);
                 if let Ok(lid) = keyexpr::new(&s.finish().to_string()) {
-                    let key_expr = *KE_PREFIX
-                        / own_zid
-                        / *KE_SESSION
-                        / *KE_TRANSPORT_UNICAST
-                        / zid
-                        / *KE_LINK
-                        / lid;
+                    let key_expr =
+                        prefix / own_zid / KE_SESSION / KE_TRANSPORT_UNICAST / zid / KE_LINK / lid;
                     if query.key_expr().intersects(&key_expr) {
                         match serde_json::to_vec(&link) {
                             Ok(bytes) => {
-                                let _ = query.reply(key_expr, bytes).wait();
+                                let reply_expr = KE_AT
+                                    / own_zid
+                                    / KE_SESSION
+                                    / KE_TRANSPORT_UNICAST
+                                    / zid
+                                    / KE_LINK
+                                    / lid;
+                                let _ = query.reply(reply_expr, bytes).wait();
                             }
                             Err(e) => tracing::debug!("Admin query error: {}", e),
                         }
@@ -105,14 +141,14 @@ pub(crate) fn on_admin_query(session: &WeakSession, query: Query) {
             .block_in_place(session.runtime.manager().get_transports_unicast())
         {
             if let Ok(peer) = transport.get_peer() {
-                reply_peer(own_zid, &query, peer);
+                reply_peer(prefix, own_zid, &query, peer);
             }
         }
         for transport in zenoh_runtime::ZRuntime::Net
             .block_in_place(session.runtime.manager().get_transports_multicast())
         {
             for peer in transport.get_peers().unwrap_or_default() {
-                reply_peer(own_zid, &query, peer);
+                reply_peer(prefix, own_zid, &query, peer);
             }
         }
     }
@@ -153,10 +189,9 @@ impl TransportMulticastEventHandler for Handler {
     ) -> ZResult<Arc<dyn TransportPeerEventHandler>> {
         if let Ok(own_zid) = keyexpr::new(&self.session.zid().to_string()) {
             if let Ok(zid) = keyexpr::new(&peer.zid.to_string()) {
-                let expr = WireExpr::from(
-                    &(*KE_PREFIX / own_zid / *KE_SESSION / *KE_TRANSPORT_UNICAST / zid),
-                )
-                .to_owned();
+                let expr =
+                    WireExpr::from(&(KE_AT / own_zid / KE_SESSION / KE_TRANSPORT_UNICAST / zid))
+                        .to_owned();
                 let info = DataInfo {
                     encoding: Some(Encoding::APPLICATION_JSON),
                     ..Default::default()
diff --git a/zenoh/src/api/builders/publisher.rs b/zenoh/src/api/builders/publisher.rs
index 54070ad607..f72548bfc2 100644
--- a/zenoh/src/api/builders/publisher.rs
+++ b/zenoh/src/api/builders/publisher.rs
@@ -288,14 +288,41 @@ impl IntoFuture for PublicationBuilder<PublisherBuilder<'_, '_>, PublicationBuil
 #[must_use = "Resolvables do nothing unless you resolve them using `.await` or `zenoh::Wait::wait`"]
 #[derive(Debug)]
 pub struct PublisherBuilder<'a, 'b> {
+    #[cfg(feature = "internal")]
+    pub session: &'a Session,
+    #[cfg(not(feature = "internal"))]
     pub(crate) session: &'a Session,
+
+    #[cfg(feature = "internal")]
+    pub key_expr: ZResult<KeyExpr<'b>>,
+    #[cfg(not(feature = "internal"))]
     pub(crate) key_expr: ZResult<KeyExpr<'b>>,
+
+    #[cfg(feature = "internal")]
+    pub encoding: Encoding,
+    #[cfg(not(feature = "internal"))]
     pub(crate) encoding: Encoding,
+    #[cfg(feature = "internal")]
+    pub congestion_control: CongestionControl,
+    #[cfg(not(feature = "internal"))]
     pub(crate) congestion_control: CongestionControl,
+    #[cfg(feature = "internal")]
+    pub priority: Priority,
+    #[cfg(not(feature = "internal"))]
     pub(crate) priority: Priority,
+    #[cfg(feature = "internal")]
+    pub is_express: bool,
+    #[cfg(not(feature = "internal"))]
     pub(crate) is_express: bool,
+    #[cfg(feature = "internal")]
+    #[cfg(feature = "unstable")]
+    pub reliability: Reliability,
+    #[cfg(not(feature = "internal"))]
     #[cfg(feature = "unstable")]
     pub(crate) reliability: Reliability,
+    #[cfg(feature = "internal")]
+    pub destination: Locality,
+    #[cfg(not(feature = "internal"))]
     pub(crate) destination: Locality,
 }
 
diff --git a/zenoh/src/api/builders/sample.rs b/zenoh/src/api/builders/sample.rs
index ebec624626..f00875d185 100644
--- a/zenoh/src/api/builders/sample.rs
+++ b/zenoh/src/api/builders/sample.rs
@@ -26,9 +26,12 @@ use crate::api::{
     publisher::Priority,
     sample::{QoS, QoSBuilder, Sample, SampleKind},
 };
+#[zenoh_macros::internal]
+use crate::pubsub::{
+    PublicationBuilder, PublicationBuilderDelete, PublicationBuilderPut, Publisher,
+};
 #[cfg(feature = "unstable")]
 use crate::sample::SourceInfo;
-
 pub trait QoSBuilderTrait {
     /// Change the `congestion_control` to apply when routing the data.
     fn congestion_control(self, congestion_control: CongestionControl) -> Self;
@@ -285,3 +288,49 @@ impl<T> From<SampleBuilder<T>> for Sample {
         sample_builder.sample
     }
 }
+
+#[zenoh_macros::internal]
+impl From<&PublicationBuilder<&Publisher<'_>, PublicationBuilderPut>> for Sample {
+    fn from(builder: &PublicationBuilder<&Publisher<'_>, PublicationBuilderPut>) -> Self {
+        Sample {
+            key_expr: builder.publisher.key_expr.clone().into_owned(),
+            payload: builder.kind.payload.clone(),
+            kind: SampleKind::Put,
+            encoding: builder.kind.encoding.clone(),
+            timestamp: builder.timestamp,
+            qos: QoSBuilder::from(QoS::default())
+                .congestion_control(builder.publisher.congestion_control)
+                .priority(builder.publisher.priority)
+                .express(builder.publisher.is_express)
+                .into(),
+            #[cfg(feature = "unstable")]
+            reliability: builder.publisher.reliability,
+            #[cfg(feature = "unstable")]
+            source_info: builder.source_info.clone(),
+            attachment: builder.attachment.clone(),
+        }
+    }
+}
+
+#[zenoh_macros::internal]
+impl From<&PublicationBuilder<&Publisher<'_>, PublicationBuilderDelete>> for Sample {
+    fn from(builder: &PublicationBuilder<&Publisher<'_>, PublicationBuilderDelete>) -> Self {
+        Sample {
+            key_expr: builder.publisher.key_expr.clone().into_owned(),
+            payload: ZBytes::new(),
+            kind: SampleKind::Put,
+            encoding: Encoding::ZENOH_BYTES,
+            timestamp: builder.timestamp,
+            qos: QoSBuilder::from(QoS::default())
+                .congestion_control(builder.publisher.congestion_control)
+                .priority(builder.publisher.priority)
+                .express(builder.publisher.is_express)
+                .into(),
+            #[cfg(feature = "unstable")]
+            reliability: builder.publisher.reliability,
+            #[cfg(feature = "unstable")]
+            source_info: builder.source_info.clone(),
+            attachment: builder.attachment.clone(),
+        }
+    }
+}
diff --git a/zenoh/src/api/publisher.rs b/zenoh/src/api/publisher.rs
index 1217a12382..eb0858fe21 100644
--- a/zenoh/src/api/publisher.rs
+++ b/zenoh/src/api/publisher.rs
@@ -314,6 +314,11 @@ impl<'a> Publisher<'a> {
         }
         self.session.undeclare_publisher_inner(self.id)
     }
+
+    #[zenoh_macros::internal]
+    pub fn session(&self) -> &crate::Session {
+        self.session.session()
+    }
 }
 
 impl<'a> UndeclarableSealed<()> for Publisher<'a> {
diff --git a/zenoh/src/api/session.rs b/zenoh/src/api/session.rs
index 013536e540..cdd1f464c9 100644
--- a/zenoh/src/api/session.rs
+++ b/zenoh/src/api/session.rs
@@ -26,6 +26,9 @@ use std::{
 };
 
 use async_trait::async_trait;
+#[zenoh_macros::internal]
+use ref_cast::ref_cast_custom;
+use ref_cast::RefCastCustom;
 use tracing::{error, info, trace, warn};
 use uhlc::Timestamp;
 #[cfg(feature = "internal")]
@@ -557,12 +560,18 @@ impl fmt::Debug for SessionInner {
 /// let session = zenoh::open(zenoh::Config::default()).await.unwrap();
 /// session.put("key/expression", "value").await.unwrap();
 /// # }
+#[derive(RefCastCustom)]
+#[repr(transparent)]
 pub struct Session(pub(crate) Arc<SessionInner>);
 
 impl Session {
     pub(crate) fn downgrade(&self) -> WeakSession {
         WeakSession::new(&self.0)
     }
+
+    #[cfg(feature = "internal")]
+    #[ref_cast_custom]
+    pub(crate) const fn ref_cast(from: &Arc<SessionInner>) -> &Self;
 }
 
 impl fmt::Debug for Session {
@@ -612,6 +621,11 @@ impl WeakSession {
         *weak += 1;
         Self(session.clone())
     }
+
+    #[zenoh_macros::internal]
+    pub(crate) fn session(&self) -> &Session {
+        Session::ref_cast(&self.0)
+    }
 }
 
 impl Clone for WeakSession {
@@ -2164,7 +2178,7 @@ impl SessionInner {
                             timestamp,
                             encoding: encoding.clone().into(),
                             #[cfg(feature = "unstable")]
-                            ext_sinfo: source_info.into(),
+                            ext_sinfo: source_info.clone().into(),
                             #[cfg(not(feature = "unstable"))]
                             ext_sinfo: None,
                             #[cfg(feature = "shared-memory")]
@@ -2176,7 +2190,7 @@ impl SessionInner {
                         SampleKind::Delete => PushBody::Del(Del {
                             timestamp,
                             #[cfg(feature = "unstable")]
-                            ext_sinfo: source_info.into(),
+                            ext_sinfo: source_info.clone().into(),
                             #[cfg(not(feature = "unstable"))]
                             ext_sinfo: None,
                             ext_attachment: attachment.clone().map(|a| a.into()),
@@ -2195,7 +2209,13 @@ impl SessionInner {
                 kind,
                 encoding: Some(encoding),
                 timestamp,
+                #[cfg(feature = "unstable")]
+                source_id: source_info.source_id,
+                #[cfg(not(feature = "unstable"))]
                 source_id: None,
+                #[cfg(feature = "unstable")]
+                source_sn: source_info.source_sn,
+                #[cfg(not(feature = "unstable"))]
                 source_sn: None,
                 qos: QoS::from(push::ext::QoSType::new(
                     priority.into(),
diff --git a/zenoh/src/api/subscriber.rs b/zenoh/src/api/subscriber.rs
index 099a1d174a..094757f8d2 100644
--- a/zenoh/src/api/subscriber.rs
+++ b/zenoh/src/api/subscriber.rs
@@ -221,6 +221,11 @@ impl<Handler> Subscriber<Handler> {
     pub fn set_background(&mut self, background: bool) {
         self.inner.undeclare_on_drop = !background;
     }
+
+    #[zenoh_macros::internal]
+    pub fn session(&self) -> &crate::Session {
+        self.inner.session.session()
+    }
 }
 
 impl<Handler> Drop for Subscriber<Handler> {
diff --git a/zenoh/src/lib.rs b/zenoh/src/lib.rs
index ec39dd29c3..119ee34533 100644
--- a/zenoh/src/lib.rs
+++ b/zenoh/src/lib.rs
@@ -81,6 +81,21 @@ extern crate zenoh_result;
 mod api;
 mod net;
 
+#[cfg(feature = "internal")]
+pub use api::admin::KE_ADV_PREFIX;
+#[cfg(feature = "internal")]
+pub use api::admin::KE_AT;
+#[cfg(feature = "internal")]
+pub use api::admin::KE_EMPTY;
+#[cfg(feature = "internal")]
+pub use api::admin::KE_PUB;
+#[cfg(feature = "internal")]
+pub use api::admin::KE_STAR;
+#[cfg(feature = "internal")]
+pub use api::admin::KE_STARSTAR;
+#[cfg(feature = "internal")]
+pub use api::admin::KE_SUB;
+
 lazy_static::lazy_static!(
     static ref LONG_VERSION: String = format!("{} built with {}", GIT_VERSION, env!("RUSTC_VERSION"));
 );
@@ -172,7 +187,7 @@ pub mod key_expr {
     #[zenoh_macros::unstable]
     pub mod format {
         pub use zenoh_keyexpr::format::*;
-        pub use zenoh_macros::{kedefine, keformat, kewrite};
+        pub use zenoh_macros::{ke, kedefine, keformat, kewrite};
         pub mod macro_support {
             pub use zenoh_keyexpr::format::macro_support::*;
         }