Expand description
reliar-store-postgres is Reliar’s PostgreSQL provider: the schema, the explicit
migrate API, and PostgresOutboxStore — the only crate where an sqlx/Postgres type
appears (ADR 0002).
§Quickstart
use reliar_store_postgres::{PostgresOutboxStore, migrate};
use reliar_outbox::OutboxEnqueue;
use reliar_core::Message;
use sqlx::postgres::PgPoolOptions;
#[derive(serde::Serialize, serde::Deserialize)]
struct OrderPlaced {
order_id: String,
}
impl Message for OrderPlaced {
const TYPE: &'static str = "orders.placed";
const VERSION: u16 = 1;
}
let database_url = std::env::var("DATABASE_URL")?;
let pool = PgPoolOptions::new().connect(&database_url).await?;
// Run once, out of band — never implicitly at startup.
migrate(&pool, Default::default()).await?;
let store = PostgresOutboxStore::new(pool.clone()).await?;
let mut tx = pool.begin().await?;
store.enqueue(&mut tx, OrderPlaced { order_id: "ord_1".into() }).await?;
tx.commit().await?;
// Hand `store` to an `OutboxDispatcher` to publish what was just enqueued.§MSRV
This crate declares rust-version = "1.94", six releases above the workspace floor
(1.88): sqlx 0.9 requires it. Pure crates (reliar-core, reliar-outbox) stay reachable
on 1.88 for hosts bringing their own store (ADR 0025).
§Features
json(default) —PostgresOutboxStore<JsonSerializer>’s default type parameter and thePostgresOutboxStore::new/PostgresOutboxStore::with_settingsconvenience constructors (forwardsreliar-core/json). Not hard-enabled: a deployment supplying its ownreliar_core::Serializershould not have to pull inserde_json. Under--no-default-features,PostgresOutboxStore::connectis the only constructor.serde(off by default) —Serialize/DeserializeonPostgresOutboxSettings,#[serde(default, deny_unknown_fields)]so a typo’d config key is a hard error, durations as integer milliseconds.serdeitself is always a dependency regardless of this feature — it also drives the crate’s privateMetadataRestJSONB contract (ADR 0012), which is not feature-gated.
§search_path setup
Every Reliar object lives in one configurable schema, reliar by default, with
unprefixed table names (outbox). sqlx::query! checks SQL at compile time, so every
identifier in every statement is a static, unqualified literal — the schema is resolved at
connection time through search_path, never compiled in (ADR 0017).
- The host puts
reliarfirst on the connection URL:?options=-c%20search_path%3Dreliar,public. - Behind a transaction-mode pooler that drops startup
options(some reject the parameter outright with08P01), use a server-side default instead:ALTER ROLE <app> SET search_path = reliar, public. This is the portable mechanism — verify it against your own pooler build/version rather than assuming:PgDog(ghcr.io/pgdogdev/pgdog:v0.1.46, the pooler this crate’s suite runs behind) was found to pass theoptionsparameter through to the upstream server instead of dropping it, so the URL-optionspath above works unmodified behind it too, with noALTER ROLErequired — but a different pooler, or a differentPgDogconfiguration, could behave either way. PostgresOutboxStore::connect/PostgresOutboxStore::newverify once at construction that the unqualified nameoutboxresolves to the configured schema, and fail fast — naming the configured schema, the observedsearch_path, and theALTER ROLEremedy — rather than surprise-failing on the firstacquire.migratedoes not depend on the caller’ssearch_path: it creates the schema itself and qualifies its own bookkeeping table name (ADR 0018).
§PostgreSQL version floor
PostgreSQL 18 or later is a hard requirement, with no older-version fallback (ADR 0015,
amended by ADR 0041 / human decision #47). PostgresOutboxStore::connect and migrate
each check the connected server’s server_version_num against MIN_SERVER_VERSION_NUM —
once per entry point, never per pooled connection — and fail with
PostgresStoreError::UnsupportedServerVersion / MigrateError::UnsupportedServerVersion
below it, naming the required and detected version. There is no conditional DDL, no
uuidv7() substitute, and no degraded mode.
§Guarantees
- Migrations never run implicitly.
migrateis the only entry point, and it is idempotent and safe under concurrent callers. - The claim is one statement.
PostgresOutboxStore’sacquire(viareliar_outbox::OutboxStore) uses aFOR UPDATE SKIP LOCKEDclaim that commits before the call returns; no network I/O ever happens while a Reliar transaction is open (ADR 0006). - Enqueuing joins the caller’s own transaction —
PostgresOutboxStoreimplementsreliar_outbox::OutboxEnqueuedirectly, no facade type in between, and atomicity is visible in the signature — and performs no I/O beyond the oneINSERT(plus, opt-in, asearch_pathwrap).
Structs§
- Json
Serializer json - The default
Serializer: JSON viaserde_json. Ships behind the defaultjsonfeature; disable it to supply a different wire format (ADR 0010). - Migrate
Options - Where
migratecreates Reliar’s schema and its bookkeeping table. - Postgres
Outbox Settings - What is provider-specific about the outbox. Everything portable lives in
reliar_outbox::OutboxSettings. - Postgres
Outbox Store - Reliar’s PostgreSQL outbox provider. Cheap to clone into an
AppState— it wraps aPgPool; no outerArcrequired. The connection pool stays the host’s: Reliar never owns or reads aDATABASE_URL.
Enums§
- Enqueue
Error crate::PostgresOutboxStore’sreliar_outbox::OutboxEnqueue::enqueue_envelopefailures. Enqueuing runs on the host’s write path, where the host decides whether to retry its own transaction, so this implementsClassifyon the same rules asPostgresStoreErrorrather than making the host re-derive which SQLSTATEs are worth retrying.- Migrate
Error migrate’s failure. Provider-owned, not a re-export ofsqlx::migrate::MigrateError: a rejected schema identifier has no variant insqlx’s own type to report it as, since that check happens before anysqlx::migratecode runs at all.- Postgres
Store Error - A failure of a
crate::PostgresOutboxStoreOutboxStore/OutboxDeadLetterscall — never a property of one row’s content. Row-content problems surface asreliar_outbox::PoisonedRows instead (ADR 0008). - Settings
Error - Why a
*Settings::from_envcall failed.OutboxSettings::from_env(reliar-outbox) was the first caller; every provider’s ownfrom_envreturns this same type, so a host wiring severalfrom_envcalls handles one error type for the whole family.
Constants§
- MIN_
SERVER_ VERSION_ NUM - The floor this crate enforces, in PostgreSQL’s own
server_version_numencoding (major * 10_000 + minor) —180_000for PostgreSQL 18.0. Public so a host can state the same requirement in its own preflight without hard-coding the number (ADR 0041). There is no older-version fallback: a server below this reportscrate::PostgresStoreError::UnsupportedServerVersionorcrate::MigrateError::UnsupportedServerVersion, never a degraded mode.
Functions§
- migrate
- Applies Reliar’s migrations. Never invoked implicitly.
poolmust reach a PostgreSQL 18 or later server — a hard requirement, with no older-version fallback, checked here as the first statement on this function’s dedicated connection (ADR 0041 / human decision #47): a server below the floor returnsMigrateError::UnsupportedServerVersionbefore the schema is created or any migration file runs.