Skip to main content

RabbitQuorumQueue

Struct RabbitQuorumQueue 

Source
pub struct RabbitQuorumQueue { /* private fields */ }
Expand description

Describes one quorum-queue subscription: the queue, its expected settings, and its bindings.

A quorum queue counts the deliveries a message has spent and ends a poison loop itself: with an x-delivery-limit and a dead-letter route it carries a message away once its deliveries run out, whether or not a service is running to count them. So the retries of a registration mounted here are the broker’s, and out_retry(policy) does not compile on it: the server moves the spent message, and the service publishes no copy. A registration declares the pair at the mount site:

use ruststream_lapin::prelude::*;
use serde::Deserialize;

#[derive(Deserialize)]
struct Payout {
    id: u64,
}

#[subscriber(RabbitQuorumQueue::new("payouts"))]
async fn on_payout(payout: &Payout) -> HandlerOutcome {
    tracing::warn!(payout.id, "the bank refused, trying again");
    HandlerOutcome::retry()
}

fn app() -> RustStream {
    let broker = LapinBroker::new("amqp://localhost:5672").declare_topology(true);
    RustStream::new(AppInfo::new("payouts", "0.1.0")).with_broker(broker, |b| {
        // The server applies this cap: it becomes the queue's delivery limit and dead-letter
        // route.
        b.include(on_payout)
            .max_attempts(nonzero!(5u32))
            .dead_letter("payouts.dead");
    })
}

The pair becomes this queue’s x-delivery-limit and dead-letter route when the broker opts into declare_topology(true). A queue declared elsewhere carries its own arguments, so subscribing then refuses the declaration rather than promising a cap nothing applies; configure the queue on the broker and mount it without the pair. The crate reads the queue’s count either way.

A quorum queue is durable, shared and permanent by definition, so those three settings are not here to be set wrong. For the single-node implementation take RabbitQueue.

§Examples

use ruststream_lapin::prelude::*;
use serde::Deserialize;

#[derive(Deserialize)]
struct Charge {
    id: u64,
}

// A charge the payment provider keeps refusing is moved to `dead-letters` by the server after
// its fifth delivery: the limit counts the four returns it survives.
#[subscriber(RabbitQuorumQueue::new("charges")
    .bind(RabbitExchange::topic("events"), "charge.*")
    .dead_letter_exchange("dead-letters")
    .delivery_limit(4))]
async fn take_payment(charge: &Charge) -> HandlerOutcome {
    tracing::info!(charge.id, "charging");
    HandlerOutcome::retry()
}

fn app() -> RustStream {
    let broker = LapinBroker::new("amqp://localhost:5672").declare_topology(true);
    RustStream::new(AppInfo::new("payments", "0.1.0")).with_broker(broker, |b| {
        b.include(take_payment);
    })
}

Implementations§

Source§

impl RabbitQuorumQueue

Source

pub fn new(name: impl Into<String>) -> Self

Describes the quorum queue name.

Source

pub fn delivery_limit(self, returns: u32) -> Self

How often the queue returns a message before it carries it away (x-delivery-limit).

The argument counts the returns a message survives, so it is one less than the number of deliveries the queue allows: delivery_limit(2) hands a message out three times. Where it goes then is dead_letter_exchange; a limit with no dead-letter route drops the spent message instead.

This is the queue’s own policy, for a queue whose retries are not one registration’s business. A mount site that declares max_attempts(n).dead_letter(..) is the more specific statement and is written over this.

Source§

impl RabbitQuorumQueue

Source

pub fn bind( self, exchange: RabbitExchange, routing_key: impl Into<String>, ) -> Self

Binds the queue to exchange under routing_key.

Call repeatedly for multiple bindings. Without any binding the queue only receives messages published to the default exchange under the queue name.

The binding carries no arguments, which is what every exchange that routes on the routing key wants. A headers exchange routes on arguments instead, and an empty table there matches every message it gets rather than none, so bind to one with bind_with.

The generated AsyncAPI document reports every binding in the crate’s own x-ruststream-amqp extension, in declaration order; the specification’s binding has no field for one.

Source

pub fn bind_with( self, exchange: RabbitExchange, routing_key: impl Into<String>, arguments: FieldTable, ) -> Self

Binds the queue to exchange under routing_key, with arguments for the exchange to match on.

A headers exchange routes by these rather than by the routing key: x-match decides whether a message has to carry all of the remaining entries or any one of them, and the routing key is ignored (pass ""). Binding to one without arguments quietly takes the whole exchange - all of no headers is true of every message - so the plain bind belongs to the exchanges that route on the key.

The crate overview has the worked all and any example; AMQPValue and FieldTable are re-exported for building the table. The generated AsyncAPI document reports the arguments with the binding, in the crate’s own x-ruststream-amqp extension.

Source

pub fn dead_letter_exchange(self, exchange: impl Into<String>) -> Self

Dead-letters rejected messages to exchange (the x-dead-letter-exchange argument).

A handler returning drop settles with basic.reject(requeue = false), which routes the message there. This is the queue’s own topology, and it applies to every rejection, whoever caused it.

Source

pub fn dead_letter_routing_key(self, routing_key: impl Into<String>) -> Self

Overrides the routing key dead-lettered messages carry (x-dead-letter-routing-key).

Source

pub fn argument(self, name: impl Into<String>, value: AMQPValue) -> Self

Sets one raw declaration argument (x-...), passed through verbatim.

§Panics

Panics if name exceeds 255 bytes (the AMQP short-string limit); argument names are compile-time constants in practice.

Source

pub fn arguments(self, arguments: FieldTable) -> Self

Replaces the whole raw declaration argument table (x-... passthrough).

Source

pub fn prefetch(self, prefetch: NonZeroU16) -> Self

Caps unacknowledged deliveries in flight for this subscription (basic.qos), overriding the broker-wide prefetch.

This is the back-pressure window for the subscriber stream. When neither is set the server imposes no prefetch limit.

The count is a NonZeroU16 because AMQP reads basic.qos(0) as “no limit”, the exact opposite of a cap: the zero sentinel is unrepresentable here, and leaving the prefetch unset is how “unlimited” is spelled.

Source

pub fn batch_wait(self, batch_wait: Duration) -> Self

Caps how long a partial batch waits for more deliveries before it goes to the handler. Defaults to 50 ms.

AMQP delivers one message at a time, so a batch handler’s batch(n) is honoured by collecting deliveries on the client; how big a batch may be is the registration’s word, and this is the other half of the trade-off - the ceiling on how long a batch that never fills keeps its deliveries. Raise it on a slow link or a sparse queue where fuller batches are worth the wait; lower it where a batch arriving late costs more than a batch arriving short. It has no effect on a subscription without a batch handler.

Source

pub fn delay(self, delay: Delay) -> Self

Makes retry_after / nack_after native, routing delayed redeliveries through a broker waiting queue instead of the core in-process fallback.

Without this the runtime handles a delay with its broker-agnostic deferred re-publish (at-most-once over the delay window, held in the service). With it, a delayed message is re-published to the Delay waiting queue with a per-message TTL and dead-lettered back to this queue when it fires, so the delayed copy lives on the broker.

The copy the waiting queue releases is a new message, which the server counts from zero; the framework’s own retry count is what survives the round.

The waiting queue is infrastructure: it is declared only when the broker opts into declare_topology; otherwise provision it yourself. Without declaration the subscription checks that it exists when it opens, and refuses to open when it does not, because a delayed copy sent to a missing waiting queue would be dropped.

Source

pub fn name(&self) -> &str

The queue name.

Trait Implementations§

Source§

impl Clone for RabbitQuorumQueue

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RabbitQuorumQueue

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl IntoSource for RabbitQuorumQueue

The descriptor is its own source, so the manual path’s subscriber(source, body) takes a RabbitQuorumQueue where the attribute path writes it in the decorator.

Source§

type Source = RabbitQuorumQueue

The source the constructor stores.
Source§

fn into_source(self) -> Self

Builds the source.
Source§

impl PartialEq for RabbitQuorumQueue

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl QueueDescriptor for RabbitQuorumQueue

Source§

impl StructuralPartialEq for RabbitQuorumQueue

Source§

impl SubscriptionSource<ConnectedLapinBroker> for RabbitQuorumQueue

Source§

type Copies = BrokerMoves

The queue moves a spent delivery itself: its x-delivery-limit counts the deliveries and its dead-letter route takes the message that runs out of them, with nothing published by this process. That is what makes out_retry(policy) a compile error here, and what keeps the cap holding while the service is down.

Source§

fn declare_retry(self, declaration: &RetryDeclaration) -> Self

Takes the registration’s cap and dead-letter destination into the descriptor, so the queue this broker declares carries them as topology.

Source§

fn channel_bindings(&self) -> Bindings

The queue this subscription consumes, as the amqp binding names its settings.

Source§

fn operation_bindings(&self) -> Bindings

The consumer’s own half: this crate acknowledges by hand on every subscription.

Source§

type Subscriber = LapinSubscriber

The subscriber type this source opens.
Source§

fn name(&self) -> &str

The name (subject / channel) this subscription binds to. Read more
Source§

async fn subscribe( self, connected: &ConnectedLapinBroker, ) -> Result<Self::Subscriber, AmqpError>

Opens the subscription against the connected broker. Called once at startup. Read more
Source§

fn declare_retry_on( &self, connected: &C, declaration: &RetryDeclaration, ) -> Result<(), DeclareRetryError>

Carries the registration’s declaration to the broker, at the same point and before the subscription opens. Read more
Source§

fn message_bindings(&self) -> Bindings

What this subscription adds to the messages that arrive on it. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> CompatExt for T

Source§

fn compat(self) -> Compat<T> ⓘ
where T: Sized,

Applies the Compat adapter by value. Read more
Source§

fn compat_ref(&self) -> Compat<&T> ⓘ

Applies the Compat adapter by shared reference. Read more
Source§

fn compat_mut(&mut self) -> Compat<&mut T> ⓘ

Applies the Compat adapter by mutable reference. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> PublishedThrough<DefaultSlot> for T

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more