fix(nats): explicit consumer config, ack timeouts, unknown-event acking, delivery_count
This commit is contained in:
@@ -10,6 +10,13 @@ const STREAM_SUBJECT: &str = "thoughts-events.>";
|
||||
const CONSUMER_NAME: &str = "worker";
|
||||
const MAX_MESSAGES: i64 = 100_000;
|
||||
|
||||
/// Maximum NATS delivery attempts before a message is considered exhausted.
|
||||
pub const CONSUMER_MAX_DELIVER: i64 = 5;
|
||||
/// How long NATS waits for an ack before redelivering.
|
||||
const CONSUMER_ACK_WAIT_SECS: u64 = 30;
|
||||
/// Timeout for spawned ack/nack async tasks.
|
||||
const ACK_TASK_TIMEOUT_SECS: u64 = 5;
|
||||
|
||||
fn stream_config() -> StreamConfig {
|
||||
StreamConfig {
|
||||
name: STREAM_NAME.to_string(),
|
||||
@@ -121,6 +128,10 @@ impl MessageSource for NatsMessageSource {
|
||||
CONSUMER_NAME,
|
||||
jetstream::consumer::pull::Config {
|
||||
durable_name: Some(CONSUMER_NAME.to_string()),
|
||||
deliver_policy: jetstream::consumer::DeliverPolicy::New,
|
||||
ack_policy: jetstream::consumer::AckPolicy::Explicit,
|
||||
ack_wait: std::time::Duration::from_secs(CONSUMER_ACK_WAIT_SECS),
|
||||
max_deliver: CONSUMER_MAX_DELIVER,
|
||||
// No filter_subject — consume everything from the stream.
|
||||
// filter_subject matching the stream's own wildcard can be
|
||||
// inconsistent across NATS server versions.
|
||||
@@ -164,25 +175,48 @@ impl MessageSource for NatsMessageSource {
|
||||
|
||||
let subject = msg.subject.to_string();
|
||||
let payload = msg.payload.to_vec();
|
||||
let delivery_count = msg
|
||||
.info()
|
||||
.map(|info| info.delivered.max(0) as u64)
|
||||
.unwrap_or(1);
|
||||
let msg = Arc::new(msg);
|
||||
let msg_nack = Arc::clone(&msg);
|
||||
|
||||
let raw = RawMessage {
|
||||
subject,
|
||||
payload,
|
||||
delivery_count,
|
||||
ack: Box::new(move || {
|
||||
let m = Arc::clone(&msg);
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = m.ack().await {
|
||||
tracing::warn!("NATS ack failed: {e}");
|
||||
let result = tokio::time::timeout(
|
||||
std::time::Duration::from_secs(ACK_TASK_TIMEOUT_SECS),
|
||||
m.ack(),
|
||||
)
|
||||
.await;
|
||||
match result {
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(e)) => tracing::warn!("NATS ack failed: {e}"),
|
||||
Err(_) => tracing::warn!(
|
||||
"NATS ack timed out after {ACK_TASK_TIMEOUT_SECS}s"
|
||||
),
|
||||
}
|
||||
});
|
||||
}),
|
||||
nack: Box::new(move || {
|
||||
let m = Arc::clone(&msg_nack);
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = m.ack_with(AckKind::Nak(None)).await {
|
||||
tracing::warn!("NATS nak failed: {e}");
|
||||
let result = tokio::time::timeout(
|
||||
std::time::Duration::from_secs(ACK_TASK_TIMEOUT_SECS),
|
||||
m.ack_with(AckKind::Nak(None)),
|
||||
)
|
||||
.await;
|
||||
match result {
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(e)) => tracing::warn!("NATS nack failed: {e}"),
|
||||
Err(_) => tracing::warn!(
|
||||
"NATS nack timed out after {ACK_TASK_TIMEOUT_SECS}s"
|
||||
),
|
||||
}
|
||||
});
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user