pub struct Queue { /* private fields */ }Expand description
The app’s queue: publish here, claim from here.
Cloning is cheap — the DatabaseConnection inside is a pooled handle —
so every worker holds one.
Implementations§
Source§impl Queue
impl Queue
Sourcepub fn new(db: &Db, app: &App) -> Self
pub fn new(db: &Db, app: &App) -> Self
The queue for an app. Never fails and is never optional: publish works
in an app whose main.toml says nothing about queues, because the table
is a built-in and a message with no subscriber is still worth recording.
pub fn config(&self) -> &QueuesConfig
Sourcepub async fn prepare(&self) -> Result<(), QueueError>
pub async fn prepare(&self) -> Result<(), QueueError>
Add the index the claim query needs, if it isn’t there.
Not left to the migrator: the migrator’s job is to make columns match
the resource declarations, and this index is not a property of the
schema but of one query — the (status, available_at) lookup every
subscriber runs on every sweep, which is a sequential scan of the whole
ledger without it. Retention keeps that table small in a healthy app and
large in exactly the app that is having a bad day.
Sourcepub async fn publish(
&self,
topic: &str,
message: &Value,
published_by: &str,
) -> Result<Publication, QueueError>
pub async fn publish( &self, topic: &str, message: &Value, published_by: &str, ) -> Result<Publication, QueueError>
Publish a message: one row per subscriber, then one notification.
The order matters and is not an accident. The rows are committed first, so a subscriber woken by the notification always finds them; notifying first would race, and the loser would be a wakeup for work that isn’t visible yet — which looks exactly like a queue that randomly adds 30 seconds of latency.
Sourcepub async fn claim(&self, worker: &str) -> Result<Vec<Delivery>, QueueError>
pub async fn claim(&self, worker: &str) -> Result<Vec<Delivery>, QueueError>
Take up to [queues] batch messages for this app’s topics.
One statement, and that is the point: the SELECT … FOR UPDATE SKIP LOCKED runs inside the UPDATE’s own transaction, so the rows are
claimed and the locks released in a single commit. Holding a transaction
open across the handler instead would mean one database connection tied
up per in-flight message, and a long handler blocking VACUUM on the
whole table.
The running rows a dead worker left behind are swept back in by
Queue::reclaim rather than here, so a stuck message costs a lease
rather than being invisible.
Sourcepub async fn next_due(&self) -> Result<Option<u64>, QueueError>
pub async fn next_due(&self) -> Result<Option<u64>, QueueError>
Seconds until the next scheduled message becomes claimable, if there is one waiting.
This is what makes a retry honour the backoff it was given rather than
the poll interval. A failure schedules itself for now() + 10s, but
nothing publishes when a backoff expires — there is no NOTIFY for “a
timer went off” — so a subscriber that always waited the full
poll_secs would round every retry up to the next 30-second boundary.
Asking the database when to come back costs one indexed query per cycle
and makes the configured number mean what it says.
None means nothing is scheduled, and the caller should wait its full
interval. Some(0) means something is due now.
Sourcepub async fn fail(
&self,
delivery: &Delivery,
error: &str,
) -> Result<bool, QueueError>
pub async fn fail( &self, delivery: &Delivery, error: &str, ) -> Result<bool, QueueError>
Record a failed attempt: schedule the retry, or give up.
Giving up leaves the row failed with its error rather than deleting
it. A dead-letter you have to go and look at is the point — a queue that
quietly discards what it could not handle is a queue that loses orders.
Sourcepub async fn reclaim(&self) -> Result<u64, QueueError>
pub async fn reclaim(&self) -> Result<u64, QueueError>
Offer up messages whose handler never came back.
Returns how many were taken back. The attempt is not undone: a handler that reliably kills its process — the classic out-of-memory loop — has spent an attempt, and will run out of them and land in the dead-letter instead of retrying until somebody notices the restart count.
Sourcepub async fn prune(&self) -> Result<u64, QueueError>
pub async fn prune(&self) -> Result<u64, QueueError>
Delete handled messages older than [queues] retain_hours. 0 keeps
them forever.
Only done rows. A failed row is the whole reason the ledger exists
and is never swept — if it were, the dead-letter would empty itself
overnight and the evidence would go with it.
Sourcepub async fn execute(
&self,
request: &str,
published_by: &str,
) -> Result<Value, QueueError>
pub async fn execute( &self, request: &str, published_by: &str, ) -> Result<Value, QueueError>
Run one operation on behalf of a function. The JSON surface behind
HostApi::publish.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for Queue
impl !UnwindSafe for Queue
impl Freeze for Queue
impl Send for Queue
impl Sync for Queue
impl Unpin for Queue
impl UnsafeUnpin for Queue
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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