Mix: Persistent rate as stream + Extension (#860)
* forward msgs immediately without any processing * Mix: Offload transmission rate and message processing from libp2p behaviour/handler * Mix: Core skeleton used in `MixService` * rename Processor to MessageBlend * Mix: Implement Persistent Transmission (Tier 1) (#845) * Mix: Add Persistent Transmission (Tier 1) * add test * define Coin struct with Uniform distribution for fast repeated sampling * use ChaCha12Rng for Coin * improve comment * Mix: Implement Temporal Processor (Tier 2) (#846) * Mix: Add Persistent Transmission (Tier 1) * Mix: Implement TemporalProcessor * use pub(crate) * Mix: Use TemporalProcessor in MessageBlend (#847) * Mix: Add Persistent Transmission (Tier 1) * Mix: Implement TemporalProcessor * Mix: Use TemporalProcessor in MessageBlend * remove duplicate members in Cargo.toml * Implement persistent transmission as stream trait extension * Replicate test for stream version --------- Co-authored-by: Youngjoon Lee <5462944+youngjoon-lee@users.noreply.github.com>
This commit is contained in:
parent
7aea30132d
commit
e207743f69
@ -5,10 +5,15 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cached = "0.53"
|
cached = "0.53"
|
||||||
tokio = { version = "1" }
|
tokio = { version = "1", features = ["time", "sync", "macros"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
nomos-mix-message = { path = "../message" }
|
nomos-mix-message = { path = "../message" }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
rand_chacha = "0.3"
|
rand_chacha = "0.3"
|
||||||
|
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio = { version = "1", features = ["rt-multi-thread"] }
|
||||||
|
tokio-stream = "0.1"
|
@ -1,9 +1,12 @@
|
|||||||
use std::time::Duration;
|
use futures::Stream;
|
||||||
|
|
||||||
use nomos_mix_message::DROP_MESSAGE;
|
use nomos_mix_message::DROP_MESSAGE;
|
||||||
use rand::{distributions::Uniform, prelude::Distribution, Rng, SeedableRng};
|
use rand::{distributions::Uniform, prelude::Distribution, Rng, SeedableRng};
|
||||||
use rand_chacha::ChaCha12Rng;
|
use rand_chacha::ChaCha12Rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::pin::{pin, Pin};
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::time::Interval;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::mpsc::{self, error::TryRecvError},
|
sync::mpsc::{self, error::TryRecvError},
|
||||||
time,
|
time,
|
||||||
@ -26,6 +29,79 @@ impl Default for PersistentTransmissionSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PersistentTransmissionStream<S>
|
||||||
|
where
|
||||||
|
S: Stream,
|
||||||
|
{
|
||||||
|
interval: Interval,
|
||||||
|
coin: Coin<ChaCha12Rng>,
|
||||||
|
stream: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> PersistentTransmissionStream<S>
|
||||||
|
where
|
||||||
|
S: Stream,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
settings: PersistentTransmissionSettings,
|
||||||
|
stream: S,
|
||||||
|
) -> PersistentTransmissionStream<S> {
|
||||||
|
let interval = time::interval(Duration::from_secs_f64(
|
||||||
|
1.0 / settings.max_emission_frequency,
|
||||||
|
));
|
||||||
|
let coin = Coin::<_>::new(
|
||||||
|
ChaCha12Rng::from_entropy(),
|
||||||
|
settings.drop_message_probability,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Self {
|
||||||
|
interval,
|
||||||
|
coin,
|
||||||
|
stream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Stream for PersistentTransmissionStream<S>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Vec<u8>> + Unpin,
|
||||||
|
{
|
||||||
|
type Item = S::Item;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
let Self {
|
||||||
|
ref mut interval,
|
||||||
|
ref mut stream,
|
||||||
|
ref mut coin,
|
||||||
|
..
|
||||||
|
} = self.get_mut();
|
||||||
|
if pin!(interval).poll_tick(cx).is_pending() {
|
||||||
|
return Poll::Pending;
|
||||||
|
}
|
||||||
|
if let Poll::Ready(Some(item)) = pin!(stream).poll_next(cx) {
|
||||||
|
Poll::Ready(Some(item))
|
||||||
|
} else if coin.flip() {
|
||||||
|
Poll::Ready(Some(DROP_MESSAGE.to_vec()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PersistentTransmissionExt: Stream {
|
||||||
|
fn persistent_transmission(
|
||||||
|
self,
|
||||||
|
settings: PersistentTransmissionSettings,
|
||||||
|
) -> PersistentTransmissionStream<Self>
|
||||||
|
where
|
||||||
|
Self: Sized + Unpin,
|
||||||
|
{
|
||||||
|
PersistentTransmissionStream::new(settings, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> PersistentTransmissionExt for S where S: Stream {}
|
||||||
|
|
||||||
/// Transmit scheduled messages with a persistent rate to the transmission channel.
|
/// Transmit scheduled messages with a persistent rate to the transmission channel.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -109,6 +185,7 @@ enum CoinError {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use futures::StreamExt;
|
||||||
|
|
||||||
macro_rules! assert_interval {
|
macro_rules! assert_interval {
|
||||||
($last_time:expr, $lower_bound:expr, $upper_bound:expr) => {
|
($last_time:expr, $lower_bound:expr, $upper_bound:expr) => {
|
||||||
@ -188,4 +265,66 @@ mod tests {
|
|||||||
assert_eq!(emission_receiver.recv().await.unwrap(), vec![4]);
|
assert_eq!(emission_receiver.recv().await.unwrap(), vec![4]);
|
||||||
assert_interval!(&mut last_time, lower_bound, upper_bound);
|
assert_interval!(&mut last_time, lower_bound, upper_bound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_persistent_transmission_stream() {
|
||||||
|
let (schedule_sender, schedule_receiver) = mpsc::unbounded_channel();
|
||||||
|
let stream = tokio_stream::wrappers::UnboundedReceiverStream::new(schedule_receiver);
|
||||||
|
let settings = PersistentTransmissionSettings {
|
||||||
|
max_emission_frequency: 1.0,
|
||||||
|
// Set to always emit drop messages if no scheduled messages for easy testing
|
||||||
|
drop_message_probability: 1.0,
|
||||||
|
};
|
||||||
|
// Prepare the expected emission interval with torelance
|
||||||
|
let expected_emission_interval =
|
||||||
|
Duration::from_secs_f64(1.0 / settings.max_emission_frequency);
|
||||||
|
let torelance = expected_emission_interval / 10; // 10% torelance
|
||||||
|
let lower_bound = expected_emission_interval - torelance;
|
||||||
|
let upper_bound = expected_emission_interval + torelance;
|
||||||
|
// prepare stream
|
||||||
|
let mut persistent_transmission_stream = stream.persistent_transmission(settings);
|
||||||
|
// Messages must be scheduled in non-blocking manner.
|
||||||
|
schedule_sender.send(vec![1]).unwrap();
|
||||||
|
schedule_sender.send(vec![2]).unwrap();
|
||||||
|
schedule_sender.send(vec![3]).unwrap();
|
||||||
|
|
||||||
|
// Check if expected messages are emitted with the expected interval
|
||||||
|
assert_eq!(
|
||||||
|
persistent_transmission_stream.next().await.unwrap(),
|
||||||
|
vec![1]
|
||||||
|
);
|
||||||
|
let mut last_time = time::Instant::now();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
persistent_transmission_stream.next().await.unwrap(),
|
||||||
|
vec![2]
|
||||||
|
);
|
||||||
|
assert_interval!(&mut last_time, lower_bound, upper_bound);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
persistent_transmission_stream.next().await.unwrap(),
|
||||||
|
vec![3]
|
||||||
|
);
|
||||||
|
assert_interval!(&mut last_time, lower_bound, upper_bound);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
persistent_transmission_stream.next().await.unwrap(),
|
||||||
|
DROP_MESSAGE.to_vec()
|
||||||
|
);
|
||||||
|
assert_interval!(&mut last_time, lower_bound, upper_bound);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
persistent_transmission_stream.next().await.unwrap(),
|
||||||
|
DROP_MESSAGE.to_vec()
|
||||||
|
);
|
||||||
|
assert_interval!(&mut last_time, lower_bound, upper_bound);
|
||||||
|
|
||||||
|
// Schedule a new message and check if it is emitted at the next interval
|
||||||
|
schedule_sender.send(vec![4]).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
persistent_transmission_stream.next().await.unwrap(),
|
||||||
|
vec![4]
|
||||||
|
);
|
||||||
|
assert_interval!(&mut last_time, lower_bound, upper_bound);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user