Перейти к основному содержимому
Эта страница ещё не переведена

Ниже показан английский оригинал.

Startup

A subscription builds and prepares its statements when it opens, so a missing table or column, a retry the table cannot carry or a second reader stops startup.

A struct that cannot drive a queue does not compile at all; the derive lists those errors. What only the database can tell is checked at startup. Each subscription builds its statements with the broker's dialect and prepares each one on the server. A table or a column the description names and the database lacks fails startup with SqlxBrokerError::Schema, which names the subscription, the table and the statement. Preparing checks names, not column types: a row whose columns do not decode reaches the decode policy instead.

The other refusals at startup:

ErrorWhen
Declarationthe mount declares what the table cannot carry: max_attempts(n) without dead_letter(..) or the reverse, a cap on a table without an attempt column, or transactional mode in the lease form at a strict isolation level on Postgres
Dialectthe dialect cannot build a statement the table needs
ServerTooOlda MySQL older than 8.0.1 or a MariaDB older than 10.6, which lack SKIP LOCKED
AlreadySubscribeda second subscription to one queue on the same broker; a table without a group column is one queue
Connect, Backendthe broker could not take a connection, or an AnyPool reached a database whose dialect feature is off

The service runs its migrations before the app starts, and the checks then see the schema the release reads.

Example​

A release adds an attempt column to the queue; on_startup applies the schema before the subscriptions open, and a table without the column stops startup with SqlxBrokerError::Schema.

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

#[derive(Inbox, sqlx::FromRow)]
#[inbox(table = "invoice_jobs")]
struct SendInvoice {
#[field(id, generated)]
id: i64,
// The column this release adds; a table without it stops the subscription at startup.
#[field(attempt, generated)]
attempt: i16,
#[field(locked_until)]
locked_until: Option<DateTime<Utc>>,
#[field(payload)]
payload: Vec<u8>,
}

#[subscriber(InboxQueue::<SendInvoice>::new("invoices"))]
async fn send(invoice: &Invoice, Ctx(attempt): Ctx<keys::Attempt>) -> HandlerOutcome {
tracing::info!(invoice.number, ?attempt, "sending an invoice");
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("billing", "0.1.0"))
// The schema this release reads, applied before the subscriptions check 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::<SendInvoice>("invoices"), |b| {
b.include(send);
})
}

#[ruststream::app]
fn app() -> impl App {
service("sqlite:billing.db?mode=rwc")
}