mod capability;
mod log;
mod pattern;
pub mod prelude;
use capability::SeekControl;
pub use capability::{
MemoryBatchContext, MemoryContext, MemoryPosition, MemoryRequester, MemorySeeker,
MemoryTransaction, PARTITION_KEY_HEADER, Position, RequestError, SeekHandle,
};
use log::LogState;
pub use log::{Discarding, LogMode, Retaining, Retention};
pub use pattern::{MemoryPattern, PatternError, Routing};
use pattern::{Pattern, PatternGroup, PatternReach, has_wildcard};
use std::{
borrow::Cow,
collections::HashMap,
convert::Infallible,
fmt,
future::{Future, ready},
marker::PhantomData,
num::NonZeroU64,
sync::{
Arc, Mutex, OnceLock,
atomic::{AtomicBool, AtomicU64, Ordering},
},
task::Poll,
time::Duration,
};
#[cfg(feature = "testing")]
use crate::testing::Backlog;
#[cfg(feature = "testing")]
use crate::testing::coordinator::Coordinator;
use crate::{
AckError, AddressedCopies, Broker, BytesMut, ConnectedBroker, DefaultPublish, DescribeServer,
FromName, HeaderMap, IncomingMessage, OutgoingMessage, PairError, PublishPolicy, Publisher,
RawMessage, RedeliveryAddress, RedeliveryAddressed, ServerSpec, Subscribe, Subscriber,
SubscriptionSource, Take,
};
use bytes::Bytes;
use futures::Stream;
use futures::future::lazy;
use thiserror::Error;
use tokio::runtime::Handle;
use tokio::sync::{Notify, mpsc};
use tokio::time::sleep;
type Sender = mpsc::UnboundedSender<MemoryDelivery>;
#[derive(Clone)]
struct MemoryOutbound {
name: String,
payload: Bytes,
headers: HeaderMap,
}
impl From<OutgoingMessage<'_, BytesMut>> for MemoryOutbound {
fn from(msg: OutgoingMessage<'_, BytesMut>) -> Self {
let (name, payload, headers) = msg.into_parts();
Self {
name: name.to_owned(),
payload: payload.freeze(),
headers,
}
}
}
struct DeliveryInner {
name: Arc<str>,
payload: Bytes,
headers: HeaderMap,
}
#[derive(Clone)]
struct MemoryDelivery {
shared: Arc<DeliveryInner>,
seq: usize,
deliveries: NonZeroU64,
}
impl MemoryDelivery {
fn redelivered(mut self) -> Self {
self.deliveries = self.deliveries.saturating_add(1);
self
}
}
enum Bus {
Live(Registry),
ShutDown(Routing),
}
impl Default for Bus {
fn default() -> Self {
Self::Live(Registry::new(Routing::default()))
}
}
struct Registry {
names: HashMap<Arc<str>, Vec<Sender>>,
patterns: Vec<PatternGroup>,
routing: Routing,
}
impl Registry {
fn new(routing: Routing) -> Self {
Self {
names: HashMap::new(),
patterns: Vec::new(),
routing,
}
}
fn len(&self) -> usize {
self.names.values().map(Vec::len).sum::<usize>()
+ self
.patterns
.iter()
.map(|group| group.senders.len())
.sum::<usize>()
}
}
struct MemoryState {
subscribers: Mutex<Bus>,
log: Mutex<LogState>,
recording: AtomicBool,
notify: Notify,
inbox_seq: AtomicU64,
#[cfg(feature = "testing")]
coordinator: OnceLock<Coordinator>,
}
impl MemoryState {
fn discarding() -> Self {
Self::with_log(LogState::Discarding, false)
}
fn retaining(retention: Retention) -> Self {
Self::with_log(LogState::recording(retention), true)
}
fn with_log(log: LogState, recording: bool) -> Self {
Self {
subscribers: Mutex::new(Bus::default()),
log: Mutex::new(log),
recording: AtomicBool::new(recording),
notify: Notify::new(),
inbox_seq: AtomicU64::new(0),
#[cfg(feature = "testing")]
coordinator: OnceLock::new(),
}
}
fn register(&self, name: &str, tx: Sender) -> Result<(), MemoryError> {
match &mut *self
.subscribers
.lock()
.expect("memory broker mutex poisoned")
{
Bus::Live(registry) => {
registry.names.entry(Arc::from(name)).or_default().push(tx);
Ok(())
}
Bus::ShutDown(_) => Err(MemoryError::ShutDown),
}
}
fn register_pattern(
&self,
text: &str,
pattern: Pattern,
tx: Sender,
) -> Result<(), MemoryError> {
match &mut *self
.subscribers
.lock()
.expect("memory broker mutex poisoned")
{
Bus::Live(registry) => {
match registry
.patterns
.iter_mut()
.find(|group| &*group.text == text)
{
Some(group) => group.senders.push(tx),
None => registry.patterns.push(PatternGroup {
text: text.into(),
pattern,
senders: vec![tx],
}),
}
Ok(())
}
Bus::ShutDown(_) => Err(MemoryError::ShutDown),
}
}
fn set_routing(&self, routing: Routing) {
match &mut *self
.subscribers
.lock()
.expect("memory broker mutex poisoned")
{
Bus::Live(registry) => registry.routing = routing,
Bus::ShutDown(kept) => *kept = routing,
}
}
#[cfg(any(test, feature = "testing"))]
fn routing(&self) -> Routing {
match &*self
.subscribers
.lock()
.expect("memory broker mutex poisoned")
{
Bus::Live(registry) => registry.routing,
Bus::ShutDown(routing) => *routing,
}
}
fn unregister(&self, name: &str) {
if let Bus::Live(registry) = &mut *self
.subscribers
.lock()
.expect("memory broker mutex poisoned")
{
registry.names.remove(name);
}
if self.recording.load(Ordering::Acquire) {
self.log
.lock()
.expect("memory broker mutex poisoned")
.forget(name);
}
}
#[allow(clippy::significant_drop_tightening)]
fn fanout(&self, name: &str, payload: Bytes, headers: HeaderMap) -> Result<(), MemoryError> {
{
let bus = self
.subscribers
.lock()
.expect("memory broker mutex poisoned");
let Bus::Live(registry) = &*bus else {
return Err(MemoryError::ShutDown);
};
let mut log = self
.recording
.load(Ordering::Acquire)
.then(|| self.log.lock().expect("memory broker mutex poisoned"));
if registry.patterns.is_empty() {
let registered = registry.names.get_key_value(name);
let shared = match registered {
Some((registered, _)) => Some(Arc::clone(registered)),
None => log.is_some().then(|| Arc::from(name)),
};
if let Some(name) = shared {
let shared = Arc::new(DeliveryInner {
name,
payload,
headers,
});
let seq = log.as_deref_mut().map_or(0, |log| log.append(&shared));
let delivery = MemoryDelivery {
shared,
seq,
deliveries: NonZeroU64::MIN,
};
if let Some((_, senders)) = registered {
self.send_to(senders, &delivery);
}
}
} else {
self.fanout_with_patterns(registry, log.as_deref_mut(), name, payload, headers);
}
}
self.notify.notify_waiters();
Ok(())
}
fn send_to(&self, senders: &[Sender], delivery: &MemoryDelivery) {
for tx in senders {
let sent = tx.send(delivery.clone());
#[cfg(feature = "testing")]
if sent.is_ok()
&& !delivery.shared.name.starts_with("_inbox.")
&& let Some(coordinator) = self.coordinator.get()
{
coordinator.enqueued();
}
#[cfg(not(feature = "testing"))]
let _ = sent;
}
}
#[cold]
#[inline(never)]
fn fanout_with_patterns(
&self,
registry: &Registry,
log: Option<&mut LogState>,
name: &str,
payload: Bytes,
headers: HeaderMap,
) {
let registered = registry.names.get_key_value(name);
let exact_live =
registered.is_some_and(|(_, senders)| senders.iter().any(|sender| !sender.is_closed()));
let reach = registry.routing.reach(®istry.patterns, name, exact_live);
let shared = match registered {
Some((registered, _)) => Arc::clone(registered),
None if log.is_some() || !matches!(reach, PatternReach::Nobody) => Arc::from(name),
None => return,
};
let shared = Arc::new(DeliveryInner {
name: shared,
payload,
headers,
});
let seq = log.map_or(0, |log| log.append(&shared));
let delivery = MemoryDelivery {
shared,
seq,
deliveries: NonZeroU64::MIN,
};
if let Some((_, senders)) = registered {
self.send_to(senders, &delivery);
}
match reach {
PatternReach::Nobody => {}
PatternReach::EveryFrom(first) => {
for group in registry.patterns[first..]
.iter()
.filter(|group| group.pattern.matches(name))
{
self.send_to_pattern(&group.senders, &delivery);
}
}
PatternReach::Only(index) => {
self.send_to_pattern(®istry.patterns[index].senders, &delivery);
}
}
}
fn send_to_pattern(&self, senders: &[Sender], delivery: &MemoryDelivery) {
for tx in senders {
let sent = tx.send(delivery.clone());
#[cfg(feature = "testing")]
if sent.is_ok()
&& let Some(coordinator) = self.coordinator.get()
{
coordinator.enqueued();
}
#[cfg(not(feature = "testing"))]
let _ = sent;
}
}
#[cfg(feature = "testing")]
fn install_coordinator(&self, coordinator: Coordinator) {
if self.coordinator.set(coordinator).is_ok()
&& self
.log
.lock()
.expect("memory broker mutex poisoned")
.record_for_harness()
{
self.recording.store(true, Ordering::Release);
}
}
#[cfg(feature = "testing")]
fn coordinator(&self) -> Option<Coordinator> {
self.coordinator.get().cloned()
}
}
pub struct MemoryBroker<Log = Discarding> {
state: Arc<MemoryState>,
mode: PhantomData<Log>,
}
impl<Log> Clone for MemoryBroker<Log> {
fn clone(&self) -> Self {
Self {
state: Arc::clone(&self.state),
mode: PhantomData,
}
}
}
impl Default for MemoryBroker<Discarding> {
fn default() -> Self {
Self::new()
}
}
impl MemoryBroker<Discarding> {
#[must_use]
pub fn new() -> Self {
Self {
state: Arc::new(MemoryState::discarding()),
mode: PhantomData,
}
}
}
impl MemoryBroker<Retaining> {
#[must_use]
pub fn retaining(retention: Retention) -> Self {
Self {
state: Arc::new(MemoryState::retaining(retention)),
mode: PhantomData,
}
}
}
impl<Log: LogMode> MemoryBroker<Log> {
#[must_use]
pub fn routing(self, routing: Routing) -> Self {
self.state.set_routing(routing);
self
}
#[must_use]
pub fn subscribe(&self, name: impl Into<String>) -> MemorySubscriber<Log> {
let (tx, mut rx) = mpsc::unbounded_channel();
let name = name.into();
if has_wildcard(&name) {
rx.close();
} else {
let _ = self.state.register(&name, tx.clone());
}
MemorySubscriber::new(name, rx, tx, &self.state, None)
}
#[must_use]
pub fn publisher(&self) -> MemoryPublisher {
MemoryPublisher {
state: Arc::clone(&self.state),
txn: Mutex::new(None),
}
}
#[must_use]
pub fn requester(&self) -> MemoryRequester {
MemoryRequester::new(Arc::clone(&self.state))
}
}
impl<Log> fmt::Debug for MemoryBroker<Log> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MemoryBroker").finish_non_exhaustive()
}
}
impl<Log: LogMode> Broker for MemoryBroker<Log> {
type Error = MemoryError;
type Connected = ConnectedMemoryBroker<Log>;
fn connect(self) -> impl Future<Output = Result<Self::Connected, Self::Error>> {
lazy(move |_| {
{
let mut bus = self
.state
.subscribers
.lock()
.expect("memory broker mutex poisoned");
if let Bus::ShutDown(routing) = *bus {
*bus = Bus::Live(Registry::new(routing));
}
}
Ok(ConnectedMemoryBroker {
state: self.state,
runtime: Handle::current(),
mode: PhantomData,
})
})
}
}
pub struct ConnectedMemoryBroker<Log = Discarding> {
state: Arc<MemoryState>,
runtime: Handle,
mode: PhantomData<Log>,
}
impl<Log> Clone for ConnectedMemoryBroker<Log> {
fn clone(&self) -> Self {
Self {
state: Arc::clone(&self.state),
runtime: self.runtime.clone(),
mode: PhantomData,
}
}
}
impl<Log> fmt::Debug for ConnectedMemoryBroker<Log> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ConnectedMemoryBroker")
.finish_non_exhaustive()
}
}
impl<Log: LogMode> ConnectedMemoryBroker<Log> {
#[must_use]
pub fn publisher(&self) -> MemoryPublisher {
MemoryPublisher {
state: Arc::clone(&self.state),
txn: Mutex::new(None),
}
}
#[must_use]
pub fn requester(&self) -> MemoryRequester {
MemoryRequester::new(Arc::clone(&self.state))
}
}
impl<Log: LogMode> ConnectedBroker for ConnectedMemoryBroker<Log> {
type Error = MemoryError;
type Closed = ClosedMemoryBroker;
fn shutdown(self) -> impl Future<Output = Result<Self::Closed, Self::Error>> {
let dropped = {
let mut bus = self
.state
.subscribers
.lock()
.expect("memory broker mutex poisoned");
let routing = match &*bus {
Bus::Live(registry) => registry.routing,
Bus::ShutDown(routing) => *routing,
};
match std::mem::replace(&mut *bus, Bus::ShutDown(routing)) {
Bus::Live(registry) => registry.len(),
Bus::ShutDown(_) => 0,
}
};
ready(Ok(ClosedMemoryBroker {
subscribers_dropped: dropped,
}))
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[must_use]
pub struct MemoryPublish;
impl<Log: LogMode> PublishPolicy<ConnectedMemoryBroker<Log>> for MemoryPublish {
type Live = MemoryPublisher;
fn pair(
self,
connected: &ConnectedMemoryBroker<Log>,
) -> impl Future<Output = Result<Self::Live, PairError>> {
ready(Ok(connected.publisher()))
}
}
impl<Log: LogMode> DefaultPublish for ConnectedMemoryBroker<Log> {
type Policy = MemoryPublish;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[must_use]
pub struct MemoryRequest;
impl<Log: LogMode> PublishPolicy<ConnectedMemoryBroker<Log>> for MemoryRequest {
type Live = MemoryRequester;
fn pair(
self,
connected: &ConnectedMemoryBroker<Log>,
) -> impl Future<Output = Result<Self::Live, PairError>> {
ready(Ok(connected.requester()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ClosedMemoryBroker {
subscribers_dropped: usize,
}
impl ClosedMemoryBroker {
#[must_use]
pub fn subscribers_dropped(&self) -> usize {
self.subscribers_dropped
}
}
impl<Log: LogMode> DescribeServer for MemoryBroker<Log> {
fn describe_server(&self) -> ServerSpec {
ServerSpec::in_process("memory")
}
}
#[cfg(feature = "testing")]
impl<Log: LogMode> crate::testing::InProcess for MemoryBroker<Log> {
fn connect_in_process(
self,
) -> impl Future<Output = Result<Self::Connected, Self::Error>> + Send {
self.connect()
}
}
#[cfg(feature = "testing")]
impl<Log: LogMode> crate::testing::TestableBroker for ConnectedMemoryBroker<Log> {
fn install_coordinator(&self, coordinator: Coordinator) {
self.state.install_coordinator(coordinator);
}
fn inject(&self, message: OutgoingMessage<'_>) {
let (name, payload, headers) = message.into_parts();
self.state
.fanout(name, Bytes::copy_from_slice(payload), headers)
.expect("inject on a shut-down broker: drive the harness before shutdown");
}
fn published(&self, name: &str) -> Vec<RawMessage> {
self.state
.log
.lock()
.expect("memory broker mutex poisoned")
.name(name)
.map(|log| log.messages(name))
.unwrap_or_default()
}
fn routes(&self, destination: &str, subscriptions: &[&str]) -> Vec<usize> {
self.state.routing().routes(destination, subscriptions)
}
fn backlog(&self) -> Backlog {
Backlog::Missed
}
}
#[cfg(feature = "testing")]
crate::register_testable_broker!(MemoryBroker<Discarding>);
#[cfg(feature = "testing")]
crate::register_testable_broker!(MemoryBroker<Retaining>);
impl<Log: LogMode> Subscribe for ConnectedMemoryBroker<Log> {
type Subscriber = MemorySubscriber<Log>;
type Copies = AddressedCopies;
fn subscribe(&self, name: &str) -> impl Future<Output = Result<Self::Subscriber, Self::Error>> {
if has_wildcard(name) {
return ready(Err(MemoryError::WildcardName {
name: name.to_owned(),
}));
}
let (tx, rx) = mpsc::unbounded_channel();
let name = name.to_owned();
if let Err(err) = self.state.register(&name, tx.clone()) {
return ready(Err(err));
}
ready(Ok(MemorySubscriber::new(
name,
rx,
tx,
&self.state,
Some(self.runtime.clone()),
)))
}
}
#[derive(Debug, Clone)]
pub struct MemorySource {
name: String,
}
impl MemorySource {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self { name: name.into() }
}
}
impl FromName for MemorySource {
fn from_name(name: impl Into<Cow<'static, str>>) -> Self {
Self::new(name.into().into_owned())
}
}
impl<Log: LogMode> SubscriptionSource<ConnectedMemoryBroker<Log>> for MemorySource {
type Subscriber = MemorySubscriber<Log>;
type Copies = AddressedCopies;
fn name(&self) -> &str {
&self.name
}
async fn subscribe(
self,
connected: &ConnectedMemoryBroker<Log>,
) -> Result<Self::Subscriber, MemoryError> {
Subscribe::subscribe(connected, &self.name).await
}
}
impl<Log: LogMode> RedeliveryAddressed<ConnectedMemoryBroker<Log>> for MemorySource {
fn redelivery_address(
&self,
_connected: &ConnectedMemoryBroker<Log>,
) -> impl Future<Output = Result<RedeliveryAddress, MemoryError>> + Send {
ready(Ok(RedeliveryAddress::new(self.name.clone())))
}
}
pub struct MemorySubscriber<Log = Discarding> {
rx: mpsc::UnboundedReceiver<MemoryDelivery>,
subscription: Arc<SubscriptionShared>,
mode: PhantomData<Log>,
}
struct SubscriptionShared {
name: String,
state: Arc<MemoryState>,
requeue: Sender,
runtime: Option<Handle>,
seek: SeekControl,
}
impl<Log> MemorySubscriber<Log> {
fn new(
name: String,
rx: mpsc::UnboundedReceiver<MemoryDelivery>,
requeue: Sender,
state: &Arc<MemoryState>,
runtime: Option<Handle>,
) -> Self {
Self {
rx,
subscription: Arc::new(SubscriptionShared {
name,
state: Arc::clone(state),
requeue,
runtime,
seek: SeekControl::default(),
}),
mode: PhantomData,
}
}
#[cfg(feature = "testing")]
pub(crate) fn coordinator(&self) -> Option<Coordinator> {
self.subscription.state.coordinator()
}
}
impl<Log> fmt::Debug for MemorySubscriber<Log> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MemorySubscriber")
.field("name", &self.subscription.name)
.finish_non_exhaustive()
}
}
impl<Log: LogMode> Subscriber for MemorySubscriber<Log> {
type Message = MemoryMessage<Log>;
type Error = Infallible;
fn stream(&mut self) -> impl Stream<Item = Result<Self::Message, Self::Error>> + Send + '_ {
#[cfg(feature = "testing")]
let coordinator = self.coordinator();
futures::stream::poll_fn(move |cx| {
if log::retains::<Log>() {
self.subscription.seek.waker.register(cx.waker());
}
self.apply_pending_seek();
loop {
match self.rx.poll_recv(cx) {
Poll::Ready(Some(delivery)) => {
if log::retains::<Log>()
&& delivery.seq < self.subscription.seek.watermark()
{
#[cfg(feature = "testing")]
if let Some(coordinator) = &coordinator {
coordinator.consumed();
}
continue;
}
return Poll::Ready(Some(Ok(MemoryMessage {
delivery: Some(delivery),
subscription: Some(Arc::clone(&self.subscription)),
#[cfg(feature = "testing")]
coordinator: coordinator.clone(),
mode: PhantomData,
})));
}
Poll::Ready(None) => return Poll::Ready(None),
Poll::Pending => return Poll::Pending,
}
}
})
}
}
pub struct MemoryPublisher {
state: Arc<MemoryState>,
txn: Mutex<Option<Vec<MemoryOutbound>>>,
}
impl Clone for MemoryPublisher {
fn clone(&self) -> Self {
Self {
state: Arc::clone(&self.state),
txn: Mutex::new(None),
}
}
}
impl fmt::Debug for MemoryPublisher {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MemoryPublisher").finish_non_exhaustive()
}
}
#[derive(Debug, Error, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum MemoryError {
#[error("a transaction is already open on this publisher handle")]
TransactionBusy,
#[error("no transaction is open on this publisher handle")]
NoTransaction,
#[error("the memory broker is shut down")]
ShutDown,
#[error("position {requested} is no longer retained; the log starts at {oldest}")]
PositionEvicted {
requested: usize,
oldest: usize,
},
#[error("subscription pattern {pattern:?} is invalid: {reason}")]
InvalidPattern {
pattern: String,
reason: PatternError,
},
#[error(
"subscription {name:?} has a wildcard token and reads one name only; subscribe to a \
pattern with `MemoryPattern::new({name:?})`"
)]
WildcardName {
name: String,
},
}
impl Publisher for MemoryPublisher {
type Payload = Take;
type Error = MemoryError;
type Options = ();
fn publish(
&self,
msg: OutgoingMessage<'_, BytesMut>,
_options: Option<&Self::Options>,
) -> impl Future<Output = Result<(), Self::Error>> {
{
let mut txn = self.txn.lock().expect("memory broker mutex poisoned");
if let Some(buffered) = txn.as_mut() {
buffered.push(MemoryOutbound::from(msg));
return ready(Ok(()));
}
}
let (name, payload, headers) = msg.into_parts();
ready(self.state.fanout(name, payload.freeze(), headers))
}
}
pub struct MemoryMessage<Log = Discarding> {
delivery: Option<MemoryDelivery>,
subscription: Option<Arc<SubscriptionShared>>,
#[cfg(feature = "testing")]
coordinator: Option<Coordinator>,
mode: PhantomData<Log>,
}
#[cfg(feature = "testing")]
impl<Log> Drop for MemoryMessage<Log> {
fn drop(&mut self) {
if let Some(coordinator) = &self.coordinator {
coordinator.consumed();
}
}
}
impl<Log> fmt::Debug for MemoryMessage<Log> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MemoryMessage")
.field("name", &self.delivery.as_ref().map(|d| &*d.shared.name))
.finish_non_exhaustive()
}
}
impl<Log> MemoryMessage<Log> {
#[must_use]
pub fn name(&self) -> &str {
self.delivery
.as_ref()
.map(|d| &*d.shared.name)
.unwrap_or_default()
}
#[must_use]
pub fn into_raw(mut self) -> RawMessage {
let delivery = self.delivery.take().expect("delivery already consumed");
RawMessage::new(&*delivery.shared.name, delivery.shared.payload.clone())
.with_headers(delivery.shared.headers.clone())
}
}
impl<Log> MemoryMessage<Log> {
fn requeue_now(&self, delivery: MemoryDelivery) {
let Some(subscription) = &self.subscription else {
return;
};
let sent = subscription.requeue.send(delivery);
#[cfg(feature = "testing")]
if sent.is_ok()
&& let Some(coordinator) = &self.coordinator
{
coordinator.enqueued();
}
#[cfg(not(feature = "testing"))]
let _ = sent;
}
}
impl<Log: LogMode> IncomingMessage for MemoryMessage<Log> {
fn payload(&self) -> &[u8] {
self.delivery
.as_ref()
.map(|d| d.shared.payload.as_ref())
.unwrap_or_default()
}
fn partition_key(&self) -> Option<&[u8]> {
crate::Partitioned::partition_key(self)
}
fn headers(&self) -> &HeaderMap {
static EMPTY: OnceLock<HeaderMap> = OnceLock::new();
self.delivery
.as_ref()
.map_or_else(|| EMPTY.get_or_init(HeaderMap::new), |d| &d.shared.headers)
}
fn redelivery_count(&self) -> Option<u64> {
self.delivery.as_ref().map(|d| d.deliveries.get())
}
fn ack(mut self) -> impl Future<Output = Result<(), AckError>> {
self.delivery.take();
ready(Ok(()))
}
fn nack(mut self, requeue: bool) -> impl Future<Output = Result<(), AckError>> {
let delivery = self.delivery.take().expect("delivery already consumed");
if requeue {
self.requeue_now(delivery.redelivered());
}
ready(Ok(()))
}
fn supports_nack_after(&self) -> bool {
true
}
fn nack_after(mut self, delay: Duration) -> impl Future<Output = Result<(), AckError>> {
let delivery = self
.delivery
.take()
.expect("delivery already consumed")
.redelivered();
if delay.is_zero() {
self.requeue_now(delivery);
return ready(Ok(()));
}
let Some(subscription) = self.subscription.take() else {
return ready(Ok(()));
};
let back = subscription.requeue.clone();
#[cfg(feature = "testing")]
if let Some(coordinator) = self.coordinator.clone() {
drop(subscription);
let counter = coordinator.clone();
coordinator.schedule_redelivery(delay, move || {
if back.send(delivery).is_ok() {
counter.enqueued();
}
});
return ready(Ok(()));
}
let redeliver = async move {
sleep(delay).await;
let _ = back.send(delivery);
};
match &subscription.runtime {
Some(runtime) => drop(runtime.spawn(redeliver)),
None => drop(tokio::spawn(redeliver)),
}
ready(Ok(()))
}
}
#[cfg(test)]
mod tests;