Monorepo consolidation: workspace, shared types, transport plans, docker/swam assets
This commit is contained in:
90
projection/src/stream/jetstream.rs
Normal file
90
projection/src/stream/jetstream.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use crate::config::Settings;
|
||||
use crate::types::ProjectionError;
|
||||
use async_nats::jetstream::{
|
||||
self, consumer::pull::Config as PullConfig, consumer::AckPolicy, consumer::DeliverPolicy,
|
||||
consumer::ReplayPolicy,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JetStreamClient {
|
||||
stream: jetstream::stream::Stream,
|
||||
consumer: jetstream::consumer::PullConsumer,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConsumerOptions {
|
||||
pub durable_name: String,
|
||||
pub filter_subject: String,
|
||||
pub deliver_policy: DeliverPolicy,
|
||||
}
|
||||
|
||||
impl JetStreamClient {
|
||||
pub async fn connect(settings: &Settings) -> Result<Self, ProjectionError> {
|
||||
let filter_subject = settings
|
||||
.subject_filters
|
||||
.first()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| "tenant.*.aggregate.*.*".to_string());
|
||||
|
||||
let options = ConsumerOptions {
|
||||
durable_name: settings.durable_name.clone(),
|
||||
filter_subject,
|
||||
deliver_policy: DeliverPolicy::All,
|
||||
};
|
||||
|
||||
Self::connect_with(settings, options).await
|
||||
}
|
||||
|
||||
pub async fn connect_with(
|
||||
settings: &Settings,
|
||||
options: ConsumerOptions,
|
||||
) -> Result<Self, ProjectionError> {
|
||||
let client = async_nats::connect(&settings.nats_url).await.map_err(|e| {
|
||||
ProjectionError::StreamError(format!("Failed to connect to NATS: {}", e))
|
||||
})?;
|
||||
|
||||
let jetstream = jetstream::new(client);
|
||||
|
||||
let stream = jetstream
|
||||
.get_stream(&settings.stream_name)
|
||||
.await
|
||||
.map_err(|e| ProjectionError::StreamError(format!("Stream not found: {}", e)))?;
|
||||
|
||||
let consumer_config = PullConfig {
|
||||
durable_name: Some(options.durable_name.clone()),
|
||||
deliver_policy: options.deliver_policy,
|
||||
ack_policy: AckPolicy::Explicit,
|
||||
ack_wait: std::time::Duration::from_millis(settings.ack_timeout_ms),
|
||||
filter_subject: options.filter_subject,
|
||||
replay_policy: ReplayPolicy::Instant,
|
||||
max_ack_pending: settings.max_in_flight as i64,
|
||||
max_deliver: settings.max_deliver,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let consumer = stream
|
||||
.get_or_create_consumer(&options.durable_name, consumer_config)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
ProjectionError::StreamError(format!("Consumer creation failed: {}", e))
|
||||
})?;
|
||||
|
||||
Ok(Self { stream, consumer })
|
||||
}
|
||||
|
||||
pub async fn messages(&self) -> Result<jetstream::consumer::pull::Stream, ProjectionError> {
|
||||
self.consumer
|
||||
.messages()
|
||||
.await
|
||||
.map_err(|e| ProjectionError::StreamError(format!("Message stream error: {}", e)))
|
||||
}
|
||||
|
||||
pub async fn stream_last_sequence(&self) -> Result<u64, ProjectionError> {
|
||||
let mut stream = self.stream.clone();
|
||||
let info = stream
|
||||
.info()
|
||||
.await
|
||||
.map_err(|e| ProjectionError::StreamError(e.to_string()))?;
|
||||
Ok(info.state.last_sequence)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user