Skip to main content

reliar_store_postgres/
lib.rs

1//! `reliar-store-postgres` is Reliar's PostgreSQL provider: the schema, the explicit
2//! [`migrate`] API, and [`PostgresOutboxStore`] — the only crate where an `sqlx`/Postgres type
3//! appears (SRS §20–§26, §35, ADR 0002).
4//!
5//! # MSRV
6//!
7//! This crate declares `rust-version = "1.94"`, six releases above the workspace floor
8//! (`1.88`): `sqlx` 0.9 requires it. Pure crates (`reliar-core`, `reliar-outbox`) stay reachable
9//! on `1.88` for hosts bringing their own store (ADR 0025).
10//!
11//! # Features
12//!
13//! - `json` (**default**) — [`PostgresOutboxStore<JsonSerializer>`]'s default type parameter
14//!   and the [`PostgresOutboxStore::new`]/[`PostgresOutboxStore::with_settings`] convenience
15//!   constructors (forwards `reliar-core/json`). Not hard-enabled: a deployment supplying its
16//!   own [`reliar_core::Serializer`] should not have to pull in `serde_json`. Under
17//!   `--no-default-features`, [`PostgresOutboxStore::connect`] is the only constructor.
18//! - `serde` (off by default) — `Serialize`/`Deserialize` on [`PostgresOutboxSettings`],
19//!   `#[serde(default, deny_unknown_fields)]` so a typo'd config key is a hard error, durations
20//!   as integer milliseconds. `serde` itself is always a dependency regardless of this feature —
21//!   it also drives the crate's private `MetadataRest` JSONB contract (ADR 0012), which is not
22//!   feature-gated.
23//!
24//! # `search_path` setup
25//!
26//! Every Reliar object lives in **one configurable schema, `reliar` by default**, with
27//! unprefixed table names (`outbox`). `sqlx::query!` checks SQL at compile time, so every
28//! identifier in every statement is a static, unqualified literal — the schema is resolved at
29//! connection time through `search_path`, never compiled in (ADR 0017).
30//!
31//! - **The host puts `reliar` first** on the connection URL: `?options=-c%20search_path%3Dreliar,public`.
32//! - **Behind a transaction-mode pooler that drops startup `options`** (some reject the
33//!   parameter outright with `08P01`), use a server-side default instead:
34//!   `ALTER ROLE <app> SET search_path = reliar, public`. This is the portable mechanism —
35//!   verify it against your own pooler build/version rather than assuming: `PgDog`
36//!   (`ghcr.io/pgdogdev/pgdog:v0.1.46`, the pooler this crate's suite runs behind) was found to
37//!   **pass the `options` parameter through** to the upstream server instead of dropping it, so
38//!   the URL-`options` path above works unmodified behind it too, with no `ALTER ROLE` required
39//!   — but a different pooler, or a different `PgDog` configuration, could behave either way
40//!   (§43.A.35).
41//! - [`PostgresOutboxStore::connect`]/[`PostgresOutboxStore::new`] verify **once at
42//!   construction** that the unqualified name `outbox` resolves to the configured schema, and
43//!   fail fast — naming the configured schema, the observed `search_path`, and the `ALTER ROLE`
44//!   remedy — rather than surprise-failing on the first `acquire`.
45//! - [`migrate`] does not depend on the caller's `search_path`: it creates the schema itself and
46//!   qualifies its own bookkeeping table name (ADR 0018).
47//!
48//! # Guarantees
49//!
50//! - **Migrations never run implicitly.** [`migrate`] is the only entry point, and it is
51//!   idempotent and safe under concurrent callers (SRS §35).
52//! - **The claim is one statement.** [`PostgresOutboxStore`]'s `acquire` (via
53//!   [`reliar_outbox::OutboxStore`]) uses a `FOR UPDATE SKIP LOCKED` claim that commits before
54//!   the call returns; no network I/O ever happens while a Reliar transaction is open (ADR 0006).
55//! - **`enqueue` joins the caller's own transaction** — atomicity is visible in the signature —
56//!   and performs no I/O beyond the one `INSERT` (plus, opt-in, a `search_path` wrap).
57
58#![forbid(unsafe_code)]
59#![warn(missing_docs)]
60
61mod duration_serde;
62mod error;
63mod migrate;
64mod records;
65mod settings;
66mod store;
67
68pub use error::{EnqueueError, PostgresStoreError};
69pub use migrate::{MigrateError, MigrateOptions, migrate};
70pub use reliar_core::SettingsError;
71pub use settings::PostgresOutboxSettings;
72pub use store::{EnqueueOptions, PostgresOutboxStore};
73
74#[cfg(feature = "json")]
75pub use reliar_core::JsonSerializer;