跳到主要内容
本页尚未翻译

下面显示的是英文原文。

Lease

#[field(locked_until)] selects the lease form: the claim writes an expiry and commits, the handler holds no connection, and the lease is extended.

A claim writes the lease's expiry into locked_until, counts the attempt and commits at once, so no transaction stays open while the handler runs. The claim reads "now" once: the rows it finds due, the leases it finds ended and the expiry it writes start from that instant. The expiry is the delivery's ownership token: a settlement takes effect only while the row still holds it. While the handler runs, the subscription extends the lease of every delivery in work each half lease, and each extension moves the token forward, so a handler may run longer than its lease. A lease is how long a crashed process's row stays out of the queue: thirty seconds unless SqlxBroker::lease sets another for the broker or InboxQueue::lease for one subscription. It is whole seconds, at least one, and a shorter one is rounded up. A lease runs out under a running handler only when its extensions stop: the database out of reach, the subscription closed, the broker shut down. The next claim then takes the row, and the late holder's settlement fails with SqlxBrokerError::LeaseLost. A delivery dropped unsettled releases its row at once; a settlement whose statement fails keeps the row until its lease runs out, as a crash does. The claim counts the attempt, so a crash spends one too, and a delivery reports the attempt as it stood before its claim. After the broker shuts down, a delivery in work keeps its lease and settles as before, and the lease is no longer extended. The crate computes the expiry on the host, so the form reads the host's clock: locked_until beside clock = DatabaseClock does not compile. The column holds a time type, Option<DateTime<Utc>> here. By hand, .lease(..) names the column, and Lease<Time> in the description's type gives the lease's time type (LeaseRow).

Example​

A report takes minutes to render under a one-minute lease, which the subscription extends while the handler runs.

use ruststream_sqlx::prelude::*;
use sqlx::Connection;
use sqlx::sqlite::SqlitePoolOptions;

/// One row of `report_jobs`: `locked_until` makes the table a lease table.
#[derive(Inbox, sqlx::FromRow)]
#[inbox(table = "report_jobs")]
struct RenderReport {
#[field(id, generated)]
id: i64,
#[field(attempt, generated)]
attempt: i16,
#[field(locked_until)]
locked_until: Option<DateTime<Utc>>,
#[field(payload)]
payload: Vec<u8>,
}

/// A report takes minutes to render. The subscription extends the minute's lease every thirty
/// seconds while the handler runs; after a crash the row waits out its minute, then returns.
#[subscriber(InboxQueue::<RenderReport>::new("reports").lease(Duration::from_mins(1)))]
async fn render(report: &Report) -> HandlerOutcome {
draw(report).await;
tracing::info!(report.id, "report rendered");
HandlerOutcome::ack()
}

/// The service on the database `url` names.
fn service(url: &str) -> RustStream {
let schema_url = url.to_owned();
// Nothing connects here: the broker builds its pool when the service starts.
let broker = SqlxBroker::from_url(url, SqlitePoolOptions::new());
RustStream::new(AppInfo::new("reports", "0.1.0"))
// The table, created before the subscription checks it.
.on_startup(async move |()| {
let mut conn = SqliteConnection::connect(&schema_url).await?;
sqlx::raw_sql(SCHEMA).execute(&mut conn).await?;
conn.close().await
})
.with_broker(broker.route::<RenderReport>("reports"), |b| {
b.include(render);
})
}