fix: apply dedup window + max_deliver to existing jetstream stream/consumer

This commit is contained in:
2026-10-26 01:35:33 +01:00
parent 818ab893a1
commit 2fb577af65
2 changed files with 96 additions and 1 deletions

View File

@@ -126,6 +126,40 @@ fn consume_from_rx(rx: EnvelopeRx) -> BoxStream<'static, Result<EventEnvelope, D
/// silently follow the server default.
const DEDUP_WINDOW: std::time::Duration = std::time::Duration::from_secs(60 * 60);
const ACK_WAIT: std::time::Duration = std::time::Duration::from_secs(600);
/// `Some(updated)` when an existing stream's config differs from what this
/// build needs. `get_or_create_stream` only applies config on creation, so a
/// stream created by an older build would otherwise keep the server default
/// duplicate window (2 minutes). Only the fields this code owns are changed;
/// everything else the operator set is preserved.
fn stream_config_update(existing: &StreamConfig) -> Option<StreamConfig> {
(existing.duplicate_window != DEDUP_WINDOW).then(|| StreamConfig {
duplicate_window: DEDUP_WINDOW,
..existing.clone()
})
}
/// `Some(updated)` when an existing durable consumer disagrees on `max_deliver`
/// or `ack_wait`. Both are mutable on a live consumer, so the update is safe
/// and keeps the consumer's delivery position. Immutable fields (durable name,
/// filter subject, deliver policy) are never touched.
fn consumer_config_update(
existing: &jetstream::consumer::Config,
desired_max_deliver: i64,
) -> Option<pull::Config> {
if existing.max_deliver == desired_max_deliver && existing.ack_wait == ACK_WAIT {
return None;
}
Some(pull::Config {
durable_name: existing.durable_name.clone(),
filter_subject: existing.filter_subject.clone(),
ack_wait: ACK_WAIT,
max_deliver: desired_max_deliver,
..Default::default()
})
}
// ── JetStream consumer ────────────────────────────────────────────────────────
pub struct NatsJetStreamConsumer {
@@ -145,6 +179,14 @@ impl NatsJetStreamConsumer {
..Default::default()
})
.await?;
if let Some(updated) = stream_config_update(&stream.cached_info().config) {
tracing::warn!(
stream = %cfg.stream_name,
"existing JetStream stream has a different duplicate window; updating to {:?}",
DEDUP_WINDOW
);
js.update_stream(updated).await?;
}
let subject_filter = consumer_subject_filter(&cfg.subject_prefix);
let consumer = stream
@@ -153,12 +195,22 @@ impl NatsJetStreamConsumer {
pull::Config {
durable_name: Some(cfg.consumer_name.clone()),
filter_subject: subject_filter,
ack_wait: std::time::Duration::from_secs(600),
ack_wait: ACK_WAIT,
max_deliver: cfg.max_deliver,
..Default::default()
},
)
.await?;
if let Some(updated) =
consumer_config_update(&consumer.cached_info().config, cfg.max_deliver)
{
tracing::warn!(
consumer = %cfg.consumer_name,
"existing JetStream consumer has different max_deliver/ack_wait; updating to max_deliver={}",
cfg.max_deliver
);
stream.update_consumer(updated).await?;
}
let (tx, rx) = mpsc::channel(128);