feat: with many producers
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
7b08b16cdb
commit
0a78124489
@ -53,3 +53,9 @@ pub enum PersistenceError {
|
|||||||
#[error("failed to publish item {0}")]
|
#[error("failed to publish item {0}")]
|
||||||
UpdatePublished(anyhow::Error),
|
UpdatePublished(anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum BuilderError {
|
||||||
|
#[error("dependency not added to builder: {0}")]
|
||||||
|
DependencyError(anyhow::Error),
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crunch::errors::*;
|
use crunch::errors::*;
|
||||||
use crunch::traits::Event;
|
use crunch::traits::Event;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct SomeEvent {
|
struct SomeEvent {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
@ -36,50 +37,49 @@ impl crunch::traits::Event for SomeEvent {
|
|||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let in_memory = crunch::Persistence::in_memory();
|
let crunch = crunch::builder::Builder::default().build()?;
|
||||||
let transport = crunch::Transport::in_memory();
|
let counter = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
|
||||||
crunch::OutboxHandler::new(in_memory.clone(), transport.clone()).spawn();
|
|
||||||
let publisher = crunch::Publisher::new(in_memory);
|
|
||||||
let subscriber = crunch::Subscriber::new(transport);
|
|
||||||
|
|
||||||
subscriber
|
let inner_counter = counter.clone();
|
||||||
.subscribe(|item: SomeEvent| async move {
|
crunch
|
||||||
|
.subscribe(move |item: SomeEvent| {
|
||||||
|
let counter = inner_counter.clone();
|
||||||
|
|
||||||
|
async move {
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
"subscription got event: {}, info: {}",
|
"subscription got event: {}, info: {}",
|
||||||
item.name,
|
item.name,
|
||||||
item.int_event_info(),
|
item.int_event_info(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
publisher
|
let event = SomeEvent {
|
||||||
.publish(SomeEvent {
|
|
||||||
name: "something".into(),
|
name: "something".into(),
|
||||||
})
|
};
|
||||||
.await?;
|
|
||||||
publisher
|
|
||||||
.publish(SomeEvent {
|
|
||||||
name: "something".into(),
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
publisher
|
|
||||||
.publish(SomeEvent {
|
|
||||||
name: "something".into(),
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
publisher
|
|
||||||
.publish(SomeEvent {
|
|
||||||
name: "something".into(),
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
publisher
|
|
||||||
.publish(SomeEvent {
|
|
||||||
name: "something".into(),
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
for _ in 0..50 {
|
||||||
|
tokio::spawn({
|
||||||
|
let event = event.clone();
|
||||||
|
let crunch = crunch.clone();
|
||||||
|
|
||||||
|
async move {
|
||||||
|
loop {
|
||||||
|
crunch.publish(event.clone()).await.unwrap();
|
||||||
|
tokio::time::sleep(std::time::Duration::from_millis(1)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
|
||||||
|
|
||||||
|
let amount_run = counter.load(std::sync::atomic::Ordering::SeqCst);
|
||||||
|
tracing::error!("ran {} amount of times", amount_run);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
mod impls;
|
mod impls;
|
||||||
mod outbox;
|
mod outbox;
|
||||||
mod publisher;
|
mod publisher;
|
||||||
|
mod subscriber;
|
||||||
mod transport;
|
mod transport;
|
||||||
|
|
||||||
#[cfg(feature = "traits")]
|
#[cfg(feature = "traits")]
|
||||||
@ -12,67 +13,115 @@ pub mod errors {
|
|||||||
pub use crunch_traits::errors::*;
|
pub use crunch_traits::errors::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crunch_traits::Event;
|
||||||
pub use impls::Persistence;
|
pub use impls::Persistence;
|
||||||
pub use outbox::OutboxHandler;
|
pub use outbox::OutboxHandler;
|
||||||
pub use publisher::Publisher;
|
pub use publisher::Publisher;
|
||||||
pub use subscriber::Subscriber;
|
pub use subscriber::Subscriber;
|
||||||
pub use transport::Transport;
|
pub use transport::Transport;
|
||||||
|
|
||||||
mod subscriber {
|
#[derive(Clone)]
|
||||||
use crunch_traits::{Event, EventInfo};
|
pub struct Crunch {
|
||||||
use futures::StreamExt;
|
publisher: Publisher,
|
||||||
|
subscriber: Subscriber,
|
||||||
use crate::{errors, Transport};
|
}
|
||||||
|
impl Crunch {
|
||||||
pub struct Subscriber {
|
pub fn new(publisher: Publisher, subscriber: Subscriber) -> Self {
|
||||||
transport: Transport,
|
Self {
|
||||||
|
publisher,
|
||||||
|
subscriber,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Subscriber {
|
pub async fn subscribe<I, F, Fut>(&self, callback: F) -> Result<(), errors::SubscriptionError>
|
||||||
pub fn new(transport: Transport) -> Self {
|
|
||||||
Self { transport }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn subscribe<I, F, Fut>(
|
|
||||||
&self,
|
|
||||||
callback: F,
|
|
||||||
) -> Result<(), errors::SubscriptionError>
|
|
||||||
where
|
where
|
||||||
F: Fn(I) -> Fut + Send + Sync + 'static,
|
F: Fn(I) -> Fut + Send + Sync + 'static,
|
||||||
Fut: futures::Future<Output = Result<(), errors::SubscriptionError>> + Send + 'static,
|
Fut: futures::Future<Output = Result<(), errors::SubscriptionError>> + Send + 'static,
|
||||||
I: Event + Send + 'static,
|
I: Event + Send + 'static,
|
||||||
{
|
{
|
||||||
let mut stream = self
|
self.subscriber.subscribe(callback).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for Crunch {
|
||||||
|
type Target = Publisher;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.publisher
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod builder {
|
||||||
|
use crate::{errors, Crunch, OutboxHandler, Persistence, Publisher, Subscriber, Transport};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Builder {
|
||||||
|
persistence: Option<Persistence>,
|
||||||
|
transport: Option<Transport>,
|
||||||
|
outbox_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Builder {
|
||||||
|
#[cfg(feature = "in-memory")]
|
||||||
|
pub fn with_in_memory_persistence(&mut self) -> &mut Self {
|
||||||
|
self.persistence = Some(Persistence::in_memory());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "in-memory")]
|
||||||
|
pub fn with_in_memory_transport(&mut self) -> &mut Self {
|
||||||
|
self.transport = Some(Transport::in_memory());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_outbox(&mut self, enabled: bool) -> &mut Self {
|
||||||
|
self.outbox_enabled = enabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(&mut self) -> Result<Crunch, errors::BuilderError> {
|
||||||
|
let persistence =
|
||||||
|
self.persistence
|
||||||
|
.clone()
|
||||||
|
.ok_or(errors::BuilderError::DependencyError(anyhow::anyhow!(
|
||||||
|
"persistence was not set"
|
||||||
|
)))?;
|
||||||
|
let transport = self
|
||||||
.transport
|
.transport
|
||||||
.subscriber(&I::event_info())
|
.clone()
|
||||||
.await
|
.ok_or(errors::BuilderError::DependencyError(anyhow::anyhow!(
|
||||||
.map_err(errors::SubscriptionError::ConnectionFailed)?
|
"transport was not set"
|
||||||
.ok_or(errors::SubscriptionError::FailedToSubscribe(
|
)))?;
|
||||||
anyhow::anyhow!("failed to find channel to subscribe to"),
|
|
||||||
))?;
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
let publisher = Publisher::new(persistence.clone());
|
||||||
while let Some(item) = stream.next().await {
|
let subscriber = Subscriber::new(transport.clone());
|
||||||
let item = match I::deserialize(item)
|
if self.outbox_enabled {
|
||||||
.map_err(errors::SubscriptionError::DeserializationFailed)
|
OutboxHandler::new(persistence.clone(), transport.clone()).spawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Crunch::new(publisher, subscriber))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Builder {
|
||||||
|
fn default() -> Self {
|
||||||
|
#[cfg(feature = "in-memory")]
|
||||||
{
|
{
|
||||||
Ok(i) => i,
|
return Self {
|
||||||
Err(e) => {
|
outbox_enabled: true,
|
||||||
tracing::warn!("deserialization failed: {}", e);
|
persistence: None,
|
||||||
continue;
|
transport: None,
|
||||||
|
}
|
||||||
|
.with_in_memory_persistence()
|
||||||
|
.with_in_memory_transport()
|
||||||
|
.clone();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
match callback(item).await {
|
Self {
|
||||||
Ok(_) => {}
|
persistence: None,
|
||||||
Err(e) => {
|
transport: None,
|
||||||
tracing::error!("subscription callback failed: {}", e)
|
outbox_enabled: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use crunch_traits::{errors::PublishError, Event};
|
|||||||
|
|
||||||
use crate::Persistence;
|
use crate::Persistence;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Publisher {
|
pub struct Publisher {
|
||||||
persistence: Persistence,
|
persistence: Persistence,
|
||||||
}
|
}
|
||||||
|
54
crates/crunch/src/subscriber.rs
Normal file
54
crates/crunch/src/subscriber.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use crunch_traits::{Event, EventInfo};
|
||||||
|
use futures::StreamExt;
|
||||||
|
|
||||||
|
use crate::{errors, Transport};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Subscriber {
|
||||||
|
transport: Transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Subscriber {
|
||||||
|
pub fn new(transport: Transport) -> Self {
|
||||||
|
Self { transport }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn subscribe<I, F, Fut>(&self, callback: F) -> Result<(), errors::SubscriptionError>
|
||||||
|
where
|
||||||
|
F: Fn(I) -> Fut + Send + Sync + 'static,
|
||||||
|
Fut: futures::Future<Output = Result<(), errors::SubscriptionError>> + Send + 'static,
|
||||||
|
I: Event + Send + 'static,
|
||||||
|
{
|
||||||
|
let mut stream = self
|
||||||
|
.transport
|
||||||
|
.subscriber(&I::event_info())
|
||||||
|
.await
|
||||||
|
.map_err(errors::SubscriptionError::ConnectionFailed)?
|
||||||
|
.ok_or(errors::SubscriptionError::FailedToSubscribe(
|
||||||
|
anyhow::anyhow!("failed to find channel to subscribe to"),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Some(item) = stream.next().await {
|
||||||
|
let item = match I::deserialize(item)
|
||||||
|
.map_err(errors::SubscriptionError::DeserializationFailed)
|
||||||
|
{
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("deserialization failed: {}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match callback(item).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("subscription callback failed: {}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user