Skip to main content

Postgres

Struct Postgres 

Source
pub struct Postgres { /* private fields */ }

Implementations§

Source§

impl Postgres

Source

pub async fn tenant_status( &self, tenant: &str, ) -> Result<Option<TenantStatus>, DbError>

The status the registry records for tenant, or None when there is no row.

A status this binary does not know reads as TenantStatus::Degraded here: TenantStatus::parse is fail-closed on purpose, and this read inherits that — an unknown word is a refusal at request time, not a guess.

§Errors

DbError when the read fails (including when the registry has not been bootstrapped yet).

Source§

impl Postgres

Source

pub async fn apply_migrations( &self, module: &str, migrations: &[SqlMigration], ) -> Result<(), DbError>

Applies a module’s unapplied migrations in id order, tracked under <module>/<id> in a harness_migrations table; each migration runs in its own transaction so a failure rolls back atomically and re-running is idempotent. Mirrors SqliteDatabase::apply_migrations exactly.

§Errors

DbError::Batch when the tracking DDL or a migration’s SQL fails, or when the bookkeeping insert cannot be rendered.

Source

pub async fn apply_harness_migrations( &self, harness: &Harness, ) -> Result<(), DbError>

Applies every module’s migrations for a whole harness, in lock order: modules in harness (config) order, each set by zero-padded migration id — the order fz migrations collect pins in .harness-lock.json. For each module the postgres set applies when shipped, else the sqlite set when it passes the portable lint (select_set).

§Errors

DbError::Batch when a module’s sqlite set fails the lint or any migration fails to apply.

Source§

impl Postgres

Source

pub async fn bootstrap_registry(&self) -> Result<(), DbError>

Creates the harness_tenants registry on the control database, idempotently. Boot calls this first; a failure here aborts the boot (there is no registry to read, no DSN to resolve).

§Errors

DbError when the DDL fails.

Source

pub async fn register_tenant( &self, tenant: &str, dsn: &str, ) -> Result<(), DbError>

Registers (or re-points) a tenant: upsert the DSN and reset the status to provisioning — the next boot’s reconciliation is what promotes it to active.

An archived tenant is never re-registered. Archiving means the database was dropped and the data keys destroyed (docs/TENANT-ONBOARDING.md §2); re-registering the same id would mint a tenant that the registry says is the old one and that no export can reconstitute. The guard is the WHERE on the conflict branch, so it is decided by the database and not by a read the caller might race.

§Errors

DbError when the write fails, and DbError::Execute naming the tenant when the row is archived.

Source

pub async fn tenants(&self) -> Result<Vec<TenantRecord>, DbError>

Every tenant in the registry, newest DSN wins. The values come from the control database and include the DSN: callers log or serve with them, never print them.

§Errors

DbError when the read fails.

Source

pub async fn set_tenant_status( &self, tenant: &str, status: TenantStatus, ) -> bool

Records a tenant’s status, if the lifecycle allows the move.

Answers whether the row changed. Best-effort by design (RECONCILIATION.md §6): a write that fails mid-boot still leaves the tenant refused at request time, because its pool was never registered. The answer is for the caller that wants to say so — false covers both “the database would not take it” and “the lifecycle does not allow it”, which are the same thing to a caller: the tenant is not in the status it just asked for.

The legal previous statuses are TenantStatus::admits, so archived stays terminal (it admits nothing, itself included) and an offboarding tenant cannot be flipped back to active by a reconciler while its keys are being shredded.

Source

pub async fn reconcile_tenant( &self, record: &TenantRecord, harness: &Harness, ) -> Result<TenantReport, DbError>

Reconciles one tenant: take the advisory lock, then apply every module’s migrations to the tenant’s database in depends_on order. On any failure the tenant is marked degraded in the control database (best-effort) and the error is returned; the caller decides whether that aborts the boot (strict).

The tenant DSN is never part of any error message: connection strings carry credentials.

§Errors

DbError when the tenant’s database is unreachable or a migration fails.

Source

pub async fn reconcile_fleet( &self, harness: &Harness, parallelism: usize, ) -> Result<Vec<TenantReport>, DbError>

Reconciles every tenant in the registry whose status is active or provisioning, at most parallelism at a time (default 8; #30 §10 keeps the right number a measured question). A degraded tenant is left for the retry timer, not re-flown this boot; an offboarding or archived one is never flown again at all.

This is what boot calls. The control database drives everything: if it cannot be bootstrapped or read, the error returns and the boot aborts — there is nothing useful to serve without a registry. A tenant that fails is marked degraded and every other tenant serves; the same with strict semantics is the caller’s decision (any Degraded in the reports).

§Errors

DbError when the control database cannot be bootstrapped or read. Per-tenant failures never abort the fleet.

Source

pub async fn reconcile_plan( &self, record: &TenantRecord, harness: &Harness, ) -> Result<TenantPlan, DbError>

What would be applied, per module, for one tenant. Takes the same advisory lock, so the answer is not a guess about a moving target (RECONCILIATION.md §8), and applies nothing.

§Errors

DbError when the tenant’s database is unreachable.

Source§

impl Postgres

Source

pub async fn connect(url: &str) -> Result<Self, DbError>

Opens a pool and establishes at least one connection, failing fast on an unreachable server or bad credentials.

Uses PoolLimits::default. A multi-tenant deployment wants Postgres::connect_with instead: one pool per tenant with no cap is how one busy tenant takes every connection on a shared cluster.

§Errors

DbError::Execute when the pool cannot connect.

Source

pub async fn connect_with( url: &str, limits: PoolLimits, ) -> Result<Self, DbError>

Opens a pool with explicit limits (TENANT-ROUTING.md §4).

sqlx caps and times out per pool, which is exactly the per-tenant boundary: a pool belongs to one tenant. The total across pools is not something sqlx can express, so it is not pretended to here — it needs a semaphore held by whatever owns the pools, acquired around every execute, query and batch. A config key that silently did nothing would be worse than its absence.

§Errors

DbError::Execute when the pool cannot connect.

Source

pub async fn close(&self) -> Result<(), DbError>

Closes the pool: waits for in-flight queries, then closes every connection. Dropping the adapter eventually closes the pool too; close makes shutdown deterministic for the parity kit and the native runtime.

§Errors

DbError::Execute when the pool cannot be closed.

Trait Implementations§

Source§

impl Clone for Postgres

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Database for Postgres

Source§

fn batch_atomic<'life0, 'life1, 'async_trait>( &'life0 self, stmts: &'life1 [Statement], ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Runs all statements in one transaction (atomic on Postgres).

Source§

fn execute<'life0, 'life1, 'async_trait>( &'life0 self, stmt: &'life1 Statement, ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn query<'life0, 'life1, 'async_trait>( &'life0 self, stmt: &'life1 Statement, ) -> Pin<Box<dyn Future<Output = Result<Rows, DbError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

impl TenantLifecycle for Postgres

Source§

fn create<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, tenant: &'life1 str, dsn: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<(), TenantLifecycleError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Registers a tenant: the row is inserted, or an existing non-archived row is re-pointed at dsn, and the status lands on TenantStatus::Provisioning — the next reconciliation is what promotes it. Read more
Source§

fn status<'life0, 'life1, 'async_trait>( &'life0 self, tenant: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<TenantStatus, TenantLifecycleError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

The status the registry records for tenant. Read more
Source§

fn tenants<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Vec<TenantSummary>, TenantLifecycleError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Every row of the registry, as TenantSummarys — no DSNs. Read more
Source§

fn set_status<'life0, 'life1, 'async_trait>( &'life0 self, tenant: &'life1 str, next: TenantStatus, ) -> Pin<Box<dyn Future<Output = Result<(), TenantLifecycleError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Moves the tenant’s status, if TenantStatus::can_become allows the move. Read more
Source§

fn begin_erasure<'life0, 'life1, 'async_trait>( &'life0 self, tenant: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<Vec<ErasureStep>, TenantLifecycleError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Begins an erasure: moves the tenant to TenantStatus::Offboarding and returns the steps of docs/TENANT-ONBOARDING.md §2 still outstanding, in order, so the caller can work down the list without keeping its own copy of the table. Read more
Source§

fn complete_erasure<'life0, 'life1, 'async_trait>( &'life0 self, tenant: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<(), TenantLifecycleError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Completes an erasure: records TenantStatus::Archived. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more