use std::collections::{BTreeMap, HashMap};
use std::fmt::{self, Debug, Formatter};
use std::str::from_utf8;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use async_nats::jetstream::consumer::DeliverPolicy;
use bytes::Bytes;
use ruststream::testing::Coordinator;
use ruststream::{HeaderMap, RawMessage};
use tokio::runtime::Handle;
use tokio::sync::mpsc;
use tokio::time::Instant;
use super::delivery::{BusDelivery, ConsumerState, DeliverySender, Feed, Release};
use super::subject::{SubjectPattern, check_publish};
use crate::convert::headers_to_nats;
use crate::error::NatsError;
use crate::jetstream::{
EXPECTED_LAST_MESSAGE_ID, EXPECTED_LAST_SEQUENCE, EXPECTED_LAST_SUBJECT_SEQUENCE,
EXPECTED_STREAM, MESSAGE_ID,
};
const MAX_PAYLOAD: usize = 1024 * 1024;
const DUPLICATE_WINDOW: Duration = Duration::from_mins(2);
#[derive(Debug, Clone)]
pub(crate) struct Settings {
pub(crate) no_echo: bool,
pub(crate) inbox_prefix: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Origin {
Connection,
External,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PublishMode {
Core,
JetStream,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Stored {
pub(crate) stream: String,
pub(crate) sequence: u64,
pub(crate) duplicate: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) enum Group {
Queue(String),
Durable { stream: String, name: String },
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum Kind {
Core,
Inbox,
JetStream { stream: String },
}
struct Subscription {
pattern: SubjectPattern,
group: Option<Group>,
sender: DeliverySender,
kind: Kind,
}
struct StoredMessage {
sequence: u64,
message: async_nats::Message,
at: SystemTime,
}
struct Stream {
name: String,
filters: Vec<SubjectPattern>,
messages: Vec<StoredMessage>,
last_sequence: u64,
last_message_id: Option<String>,
subject_sequences: HashMap<String, u64>,
message_ids: HashMap<String, (u64, Instant)>,
}
impl Stream {
fn new(name: &str) -> Self {
Self {
name: name.to_owned(),
filters: Vec::new(),
messages: Vec::new(),
last_sequence: 0,
last_message_id: None,
subject_sequences: HashMap::new(),
message_ids: HashMap::new(),
}
}
fn captures(&self, subject: &str) -> bool {
self.filters.iter().any(|filter| filter.matches(subject))
}
fn store(
&mut self,
message: &async_nats::Message,
headers: &HeaderMap,
) -> Result<Stored, String> {
let now = Instant::now();
self.message_ids
.retain(|_, (_, at)| now.duration_since(*at) < DUPLICATE_WINDOW);
let message_id = text(headers, MESSAGE_ID);
if let Some(id) = &message_id
&& let Some((sequence, _)) = self.message_ids.get(id)
{
return Ok(Stored {
stream: self.name.clone(),
sequence: *sequence,
duplicate: true,
});
}
if let Some(expected) = number(headers, EXPECTED_LAST_SEQUENCE)?
&& expected != self.last_sequence
{
return Err(format!("wrong last sequence: {}", self.last_sequence));
}
let subject = message.subject.as_str();
if let Some(expected) = number(headers, EXPECTED_LAST_SUBJECT_SEQUENCE)? {
let last = self.subject_sequences.get(subject).copied().unwrap_or(0);
if expected != last {
return Err(format!("wrong last sequence: {last}"));
}
}
if let Some(expected) = text(headers, EXPECTED_LAST_MESSAGE_ID)
&& self.last_message_id.as_deref() != Some(expected.as_str())
{
return Err(format!(
"wrong last msg ID: {}",
self.last_message_id.as_deref().unwrap_or_default()
));
}
self.last_sequence += 1;
let sequence = self.last_sequence;
self.subject_sequences.insert(subject.to_owned(), sequence);
if let Some(id) = message_id {
self.message_ids.insert(id.clone(), (sequence, now));
self.last_message_id = Some(id);
}
self.messages.push(StoredMessage {
sequence,
message: message.clone(),
at: SystemTime::now(),
});
Ok(Stored {
stream: self.name.clone(),
sequence,
duplicate: false,
})
}
fn backlog(&self, filter: &SubjectPattern, policy: DeliverPolicy) -> Vec<&StoredMessage> {
let matching = self
.messages
.iter()
.filter(|stored| filter.matches(stored.message.subject.as_str()));
match policy {
DeliverPolicy::All => matching.collect(),
DeliverPolicy::New => Vec::new(),
DeliverPolicy::Last => matching.last().into_iter().collect(),
DeliverPolicy::LastPerSubject => {
let mut last: BTreeMap<u64, &StoredMessage> = BTreeMap::new();
let mut by_subject: HashMap<&str, u64> = HashMap::new();
for stored in matching {
if let Some(previous) =
by_subject.insert(stored.message.subject.as_str(), stored.sequence)
{
last.remove(&previous);
}
last.insert(stored.sequence, stored);
}
last.into_values().collect()
}
DeliverPolicy::ByStartSequence { start_sequence } => matching
.filter(|stored| stored.sequence >= start_sequence)
.collect(),
DeliverPolicy::ByStartTime { start_time } => {
let start = start_time.unix_timestamp_nanos();
matching
.filter(|stored| {
stored.at.duration_since(UNIX_EPOCH).map_or(0, |since| {
i128::try_from(since.as_nanos()).unwrap_or(i128::MAX)
}) >= start
})
.collect()
}
}
}
}
fn text(headers: &HeaderMap, name: &str) -> Option<String> {
headers
.get(name)
.and_then(|value| from_utf8(value).ok())
.map(str::to_owned)
}
fn number(headers: &HeaderMap, name: &str) -> Result<Option<u64>, String> {
text(headers, name)
.map(|value| {
value
.trim()
.parse()
.map_err(|_| format!("invalid {name}: {value}"))
})
.transpose()
}
#[derive(Default)]
struct State {
subscriptions: BTreeMap<u64, Subscription>,
log: HashMap<String, Vec<RawMessage>>,
turns: HashMap<Group, u64>,
streams: Vec<Stream>,
durables: HashMap<(String, String), Arc<ConsumerState>>,
parked: HashMap<Group, Vec<Parked>>,
}
struct Parked {
message: async_nats::Message,
sequence: u64,
delivered: u64,
}
impl State {
fn stream_mut(&mut self, name: &str) -> &mut Stream {
let index = if let Some(index) = self.streams.iter().position(|s| s.name == name) {
index
} else {
self.streams.push(Stream::new(name));
self.streams.len() - 1
};
&mut self.streams[index]
}
fn knows(&self, stream: &str) -> bool {
self.streams
.iter()
.any(|known| known.name == stream && !known.filters.is_empty())
}
fn capturing(&self, subject: &str) -> Option<usize> {
self.streams
.iter()
.position(|stream| stream.captures(subject))
}
fn recipients(
&mut self,
subject: &str,
stored: Option<&str>,
echo: bool,
) -> Vec<(DeliverySender, Kind)> {
let mut recipients = Vec::new();
let mut competing: BTreeMap<Group, Vec<(DeliverySender, Kind)>> = BTreeMap::new();
for subscription in self.subscriptions.values() {
let reaches = match &subscription.kind {
Kind::Core | Kind::Inbox => echo,
Kind::JetStream { stream } => stored == Some(stream.as_str()),
};
if !reaches || !subscription.pattern.matches(subject) {
continue;
}
let entry = (subscription.sender.clone(), subscription.kind.clone());
match &subscription.group {
None => recipients.push(entry),
Some(group) => competing.entry(group.clone()).or_default().push(entry),
}
}
for (group, mut members) in competing {
let turn = self.turns.entry(group).or_insert(0);
let picked = usize::try_from(*turn % members.len() as u64).unwrap_or(0);
*turn = turn.wrapping_add(1);
recipients.push(members.swap_remove(picked));
}
recipients
}
}
pub(crate) struct ConsumerSpec<'a> {
pub(crate) stream: &'a str,
pub(crate) durable: Option<&'a str>,
pub(crate) filter: SubjectPattern,
pub(crate) deliver_policy: DeliverPolicy,
}
pub(crate) struct Bus {
closed: AtomicBool,
sent: AtomicU64,
received: AtomicU64,
coordinator: OnceLock<Coordinator>,
settings: Settings,
runtime: Handle,
next_id: AtomicU64,
state: Mutex<State>,
}
impl Debug for Bus {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Bus")
.field("closed", &self.is_closed())
.field("settings", &self.settings)
.finish_non_exhaustive()
}
}
impl Bus {
pub(crate) fn new(settings: Settings, runtime: Handle) -> Arc<Self> {
Arc::new(Self {
closed: AtomicBool::new(false),
sent: AtomicU64::new(0),
received: AtomicU64::new(0),
coordinator: OnceLock::new(),
settings,
runtime,
next_id: AtomicU64::new(1),
state: Mutex::new(State::default()),
})
}
fn state(&self) -> MutexGuard<'_, State> {
self.state
.lock()
.expect("in-process NATS state mutex poisoned")
}
fn next_id(&self) -> u64 {
self.next_id.fetch_add(1, Ordering::Relaxed)
}
pub(crate) fn install(&self, coordinator: Coordinator) {
let _ = self.coordinator.set(coordinator);
}
pub(crate) fn coordinator(&self) -> Option<Coordinator> {
self.coordinator.get().cloned()
}
pub(crate) const fn runtime(&self) -> &Handle {
&self.runtime
}
pub(crate) const fn no_echo(&self) -> bool {
self.settings.no_echo
}
pub(crate) fn is_closed(&self) -> bool {
self.closed.load(Ordering::Acquire)
}
pub(crate) fn ensure_live(&self, subject: &str) -> Result<(), NatsError> {
if self.is_closed() {
return Err(NatsError::Closed {
subject: subject.to_owned(),
});
}
Ok(())
}
pub(crate) fn close(&self) -> (u64, u64) {
let mut state = self.state();
self.closed.store(true, Ordering::Release);
state.subscriptions.clear();
state.parked.clear();
drop(state);
(
self.sent.load(Ordering::Relaxed),
self.received.load(Ordering::Relaxed),
)
}
fn register(
self: &Arc<Self>,
state: &mut State,
pattern: SubjectPattern,
group: Option<Group>,
kind: Kind,
) -> (Feed, DeliverySender) {
let (sender, rx) = mpsc::unbounded_channel();
let id = self.next_id();
state.subscriptions.insert(
id,
Subscription {
pattern,
group,
sender: sender.clone(),
kind,
},
);
(Feed::new(rx, id, Arc::clone(self)), sender)
}
pub(crate) fn subscribe_core(
self: &Arc<Self>,
pattern: SubjectPattern,
queue: Option<&str>,
) -> Feed {
let group = queue.map(|name| Group::Queue(name.to_owned()));
let mut state = self.state();
self.register(&mut state, pattern, group, Kind::Core).0
}
pub(crate) fn subscribe_jetstream(
self: &Arc<Self>,
spec: ConsumerSpec<'_>,
) -> (Feed, DeliverySender, Arc<ConsumerState>) {
let mut state = self.state();
let stream = state.stream_mut(spec.stream);
if !stream.filters.contains(&spec.filter) {
stream.filters.push(spec.filter.clone());
}
let (consumer, existed) = if let Some(name) = spec.durable {
let key = (spec.stream.to_owned(), name.to_owned());
if let Some(consumer) = state.durables.get(&key) {
(Arc::clone(consumer), true)
} else {
let consumer = ConsumerState::new(spec.stream, name, true);
state.durables.insert(key, Arc::clone(&consumer));
(consumer, false)
}
} else {
let name = format!("{}_ephemeral_{}", spec.stream, self.next_id());
(ConsumerState::new(spec.stream, &name, false), false)
};
let backlog: Vec<(async_nats::Message, u64)> = if existed {
Vec::new()
} else {
state
.stream_mut(spec.stream)
.backlog(&spec.filter, spec.deliver_policy)
.into_iter()
.map(|stored| (stored.message.clone(), stored.sequence))
.collect()
};
let group = spec.durable.map(|name| Group::Durable {
stream: spec.stream.to_owned(),
name: name.to_owned(),
});
if let Some(group) = group.as_ref().filter(|_| existed) {
for member in state.subscriptions.values_mut() {
if member.group.as_ref() == Some(group) {
member.pattern = spec.filter.clone();
}
}
}
let kind = Kind::JetStream {
stream: spec.stream.to_owned(),
};
let parked = group
.as_ref()
.and_then(|group| state.parked.remove(group))
.unwrap_or_default();
let (feed, sender) = self.register(&mut state, spec.filter, group, kind);
for parked in parked {
let _ = sender.send(BusDelivery {
message: parked.message,
sequence: parked.sequence,
delivered: parked.delivered,
release: Release::counted(self.coordinator()),
});
}
drop(state);
for (message, sequence) in backlog {
let _ = sender.send(BusDelivery {
message,
sequence,
delivered: 1,
release: Release::counted(self.coordinator()),
});
}
(feed, sender, consumer)
}
pub(crate) fn unsubscribe(&self, id: u64) -> Option<Group> {
self.state()
.subscriptions
.remove(&id)
.and_then(|subscription| subscription.group)
}
pub(crate) fn reroute(&self, group: &Group, delivery: BusDelivery) {
let mut state = self.state();
if self.is_closed() {
return;
}
let mut members: Vec<&DeliverySender> = state
.subscriptions
.values()
.filter(|subscription| subscription.group.as_ref() == Some(group))
.map(|subscription| &subscription.sender)
.collect();
let delivery = if members.is_empty() {
Err(delivery)
} else {
let turn = state.turns.get(group).copied().unwrap_or(0);
let picked = usize::try_from(turn % members.len() as u64).unwrap_or(0);
members
.swap_remove(picked)
.send(delivery)
.map_err(|failed| failed.0)
};
match delivery {
Ok(()) => {
let turn = state.turns.entry(group.clone()).or_insert(0);
*turn = turn.wrapping_add(1);
}
Err(delivery) => state.parked.entry(group.clone()).or_default().push(Parked {
message: delivery.message,
sequence: delivery.sequence,
delivered: delivery.delivered,
}),
}
}
fn frame(
subject: &str,
payload: Bytes,
headers: &HeaderMap,
reply: Option<&str>,
) -> Result<async_nats::Message, NatsError> {
check_publish(subject).map_err(|err| NatsError::Publish(Box::new(err)))?;
let wire = headers_to_nats(headers)?;
let header_length = if headers.is_empty() {
0
} else {
10 + headers
.iter()
.map(|(name, value)| name.len() + 2 + value.len() + 2)
.sum::<usize>()
+ 2
};
let length = header_length + payload.len();
if length > MAX_PAYLOAD {
return Err(NatsError::Publish(
format!("Payload size limit of {MAX_PAYLOAD} exceeded by message size of {length}")
.into(),
));
}
Ok(async_nats::Message {
subject: subject.into(),
reply: reply.map(Into::into),
payload,
headers: wire,
status: None,
description: None,
length,
})
}
pub(crate) fn publish(
&self,
subject: &str,
payload: Bytes,
headers: &HeaderMap,
reply: Option<&str>,
mode: PublishMode,
origin: Origin,
) -> Result<Option<Stored>, NatsError> {
self.ensure_live(subject)?;
let message = Self::frame(subject, payload, headers, reply)?;
let echo = origin == Origin::External || !self.settings.no_echo;
let mut state = self.state();
self.ensure_live(subject)?;
let capturing = state.capturing(subject);
let expected = text(headers, EXPECTED_STREAM);
let target = match (&expected, capturing) {
(Some(expected), Some(index)) if state.streams[index].name != *expected => {
Err("expected stream does not match".to_owned())
}
(Some(expected), None) if state.knows(expected) => Ok(None),
(Some(expected), None) => Ok(Some(expected.clone())),
(_, Some(index)) => Ok(Some(state.streams[index].name.clone())),
(None, None) => Ok(None),
};
let stored = match (target, mode) {
(Ok(Some(stream)), _) => state.stream_mut(&stream).store(&message, headers),
(Ok(None), PublishMode::Core) => Err(String::new()),
(Ok(None), PublishMode::JetStream) => {
Err("no stream found for given subject".to_owned())
}
(Err(refusal), _) => Err(refusal),
};
let stored = match (stored, mode) {
(Ok(stored), _) => Some(stored),
(Err(refusal), PublishMode::JetStream) => {
return Err(NatsError::JetStream(refusal.into()));
}
(Err(_), PublishMode::Core) => None,
};
state.log.entry(subject.to_owned()).or_default().push(
RawMessage::new(subject.to_owned(), message.payload.clone())
.with_headers(headers.clone()),
);
let reaching = stored
.as_ref()
.filter(|stored| !stored.duplicate)
.map(|stored| stored.stream.as_str());
let recipients = state.recipients(subject, reaching, echo);
if origin == Origin::Connection {
self.sent.fetch_add(1, Ordering::Relaxed);
}
let sequence = stored.as_ref().map_or(0, |stored| stored.sequence);
for (sender, kind) in recipients {
let (sequence, release) = match kind {
Kind::Core => (0, Release::counted(self.coordinator())),
Kind::Inbox => (0, Release::uncounted()),
Kind::JetStream { .. } => (sequence, Release::counted(self.coordinator())),
};
if sender
.send(BusDelivery {
message: message.clone(),
sequence,
delivered: 1,
release,
})
.is_ok()
{
self.received.fetch_add(1, Ordering::Relaxed);
}
}
drop(state);
Ok(stored)
}
fn open_inbox(
self: &Arc<Self>,
subject: &str,
inbox: SubjectPattern,
) -> Result<Feed, NatsError> {
let mut state = self.state();
let responders = !self.settings.no_echo
&& state.subscriptions.values().any(|subscription| {
subscription.kind == Kind::Core && subscription.pattern.matches(subject)
});
if !responders && state.capturing(subject).is_none() {
return Err(NatsError::Publish("no responders".into()));
}
let feed = self.register(&mut state, inbox, None, Kind::Inbox).0;
drop(state);
Ok(feed)
}
pub(crate) async fn request(
self: &Arc<Self>,
subject: &str,
payload: Bytes,
headers: &HeaderMap,
timeout: Duration,
) -> Result<async_nats::Message, NatsError> {
self.ensure_live(subject)?;
let inbox = format!("{}.{}", self.settings.inbox_prefix, self.next_id());
let pattern =
SubjectPattern::parse(&inbox).map_err(|err| NatsError::Publish(Box::new(err)))?;
let mut feed = self.open_inbox(subject, pattern)?;
let stored = self.publish(
subject,
payload,
headers,
Some(&inbox),
PublishMode::Core,
Origin::Connection,
)?;
if let Some(stored) = stored {
let ack = format!(
r#"{{"stream":"{}","seq":{}{}}}"#,
stored.stream,
stored.sequence,
if stored.duplicate {
r#","duplicate":true"#
} else {
""
},
);
self.publish(
&inbox,
Bytes::from(ack),
&HeaderMap::new(),
None,
PublishMode::Core,
Origin::External,
)?;
}
match tokio::time::timeout(timeout, feed.recv()).await {
Ok(Some(delivery)) => Ok(delivery.message),
Ok(None) => Err(NatsError::Closed {
subject: subject.to_owned(),
}),
Err(_) => Err(NatsError::RequestTimeout),
}
}
pub(crate) fn published(&self, subject: &str) -> Vec<RawMessage> {
self.state().log.get(subject).cloned().unwrap_or_default()
}
}