pub struct Postgres { /* private fields */ }Implementations§
Source§impl Postgres
impl Postgres
Sourcepub async fn tenant_status(
&self,
tenant: &str,
) -> Result<Option<TenantStatus>, DbError>
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
impl Postgres
Sourcepub async fn apply_migrations(
&self,
module: &str,
migrations: &[SqlMigration],
) -> Result<(), DbError>
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.
Sourcepub async fn apply_harness_migrations(
&self,
harness: &Harness,
) -> Result<(), DbError>
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
impl Postgres
Sourcepub async fn bootstrap_registry(&self) -> Result<(), DbError>
pub async fn bootstrap_registry(&self) -> Result<(), DbError>
Sourcepub async fn register_tenant(
&self,
tenant: &str,
dsn: &str,
) -> Result<(), DbError>
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.
Sourcepub async fn set_tenant_status(
&self,
tenant: &str,
status: TenantStatus,
) -> bool
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.
Sourcepub async fn reconcile_tenant(
&self,
record: &TenantRecord,
harness: &Harness,
) -> Result<TenantReport, DbError>
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.
Sourcepub async fn reconcile_fleet(
&self,
harness: &Harness,
parallelism: usize,
) -> Result<Vec<TenantReport>, DbError>
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.
Sourcepub async fn reconcile_plan(
&self,
record: &TenantRecord,
harness: &Harness,
) -> Result<TenantPlan, DbError>
pub async fn reconcile_plan( &self, record: &TenantRecord, harness: &Harness, ) -> Result<TenantPlan, DbError>
Source§impl Postgres
impl Postgres
Sourcepub async fn connect(url: &str) -> Result<Self, DbError>
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.
Sourcepub async fn connect_with(
url: &str,
limits: PoolLimits,
) -> Result<Self, DbError>
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.
Sourcepub async fn close(&self) -> Result<(), DbError>
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 Database for Postgres
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,
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).
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,
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
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,
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,
dsn, and the status lands on
TenantStatus::Provisioning — the next reconciliation is what
promotes it. Read moreSource§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,
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,
tenant. Read moreSource§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,
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,
TenantSummarys — no DSNs. Read moreSource§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,
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,
TenantStatus::can_become
allows the move. Read moreSource§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,
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,
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 moreSource§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,
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,
TenantStatus::Archived. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for Postgres
impl !UnwindSafe for Postgres
impl Freeze for Postgres
impl Send for Postgres
impl Sync for Postgres
impl Unpin for Postgres
impl UnsafeUnpin for Postgres
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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