pub struct PostgresStore { /* private fields */ }Expand description
Fixture reset is available only in the opt-in test_support module,
never as a method on a normal store handle.
fn reset(store: &PostgresStore) {
let _ = store.truncate_all();
}Query-plan inspection, which refreshes database statistics, is likewise absent from the operational handle:
fn explain(store: &PostgresStore) {
let _ = store.explain_active_lease_sum(AccountId(1));
}The same holds for the two sweep plans (GL-65):
fn explain_sweep(store: &PostgresStore) {
let _ = store.explain_reclaim_due_leases(jiff::Timestamp::UNIX_EPOCH, 256);
}fn explain_rollover(store: &PostgresStore) {
let _ = store.explain_due_periods("daily", 0, 256);
}Implementations§
Source§impl PostgresStore
impl PostgresStore
Sourcepub async fn connect(
url: &str,
policy: GrantPolicy,
) -> Result<Arc<Self>, StoreError>
pub async fn connect( url: &str, policy: GrantPolicy, ) -> Result<Arc<Self>, StoreError>
Connect with default pool bounds and run pending migrations.
Sourcepub async fn connect_with(
url: &str,
policy: GrantPolicy,
pool_config: PoolConfig,
) -> Result<Arc<Self>, StoreError>
pub async fn connect_with( url: &str, policy: GrantPolicy, pool_config: PoolConfig, ) -> Result<Arc<Self>, StoreError>
Connect and run pending migrations (versioned under ./migrations, tracked by sqlx’s _sqlx_migrations table — review finding GL-11).
Note the limits of what a pool bound can promise: acquire_timeout
covers waiting for a connection, including establishing one, but a
query already in flight on a healthy connection is bounded only by a
server-side statement_timeout. Callers must still bound their own
calls (INVARIANTS.md GL-18).
pub async fn balance(&self, account: AccountId) -> Result<CostUnits, StoreError>
pub async fn usage_recorded( &self, account: AccountId, ) -> Result<CostUnits, StoreError>
pub async fn conservation( &self, account: AccountId, ) -> Result<Option<Conservation>, StoreError>
Trait Implementations§
Source§impl AdminStore for PostgresStore
impl AdminStore for PostgresStore
fn create_account<'life0, 'async_trait>(
&'life0 self,
config: AccountConfig,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, CreateAccountError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn deposit<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
units: CostUnits,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, AllocateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn set_budget_schedule<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
schedule: Option<BudgetSchedule>,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, BudgetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn set_budget_schedule<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
schedule: Option<BudgetSchedule>,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, BudgetError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn account_view<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
) -> Pin<Box<dyn Future<Output = Result<Option<AccountView>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn account_view<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
) -> Pin<Box<dyn Future<Output = Result<Option<AccountView>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
None if no such account (GL-121). Read moreSource§fn roll_due_periods<'life0, 'async_trait>(
&'life0 self,
now: Timestamp,
limit: NonZeroUsize,
) -> Pin<Box<dyn Future<Output = Result<RolloverBatch, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn roll_due_periods<'life0, 'async_trait>(
&'life0 self,
now: Timestamp,
limit: NonZeroUsize,
) -> Pin<Box<dyn Future<Output = Result<RolloverBatch, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
limit accounts that are past it:
expire each closed period’s unspent allowance and deposit the next one,
one transaction per batch. Read moreSource§fn set_account_status<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
status: AccountStatus,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<StatusChange>, SetStatusError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn set_account_status<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
status: AccountStatus,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<StatusChange>, SetStatusError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
generation + 1. Read moreSource§fn set_capacity_class<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
class: CapacityClass,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<StatusChange>, SetStatusError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn set_capacity_class<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
class: CapacityClass,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<StatusChange>, SetStatusError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
generation + 1
(GL-99). Read moreSource§fn publish_snapshot<'life0, 'async_trait>(
&'life0 self,
principal: Principal,
snapshot: PublishableSnapshot,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, PublishSnapshotError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn publish_snapshot<'life0, 'async_trait>(
&'life0 self,
principal: Principal,
snapshot: PublishableSnapshot,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, PublishSnapshotError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn remove_snapshot<'life0, 'async_trait>(
&'life0 self,
principal: Principal,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§impl KeyDirectory for PostgresStore
impl KeyDirectory for PostgresStore
Source§fn credential_activity<'life0, 'life1, 'async_trait>(
&'life0 self,
keys: &'life1 [KeyId],
) -> Pin<Box<dyn Future<Output = Result<Vec<CredentialActivity>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn credential_activity<'life0, 'life1, 'async_trait>(
&'life0 self,
keys: &'life1 [KeyId],
) -> Pin<Box<dyn Future<Output = Result<Vec<CredentialActivity>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn insert_key<'life0, 'async_trait>(
&'life0 self,
record: KeyRecord,
) -> Pin<Box<dyn Future<Output = Result<(), KeyError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn insert_key<'life0, 'async_trait>(
&'life0 self,
record: KeyRecord,
) -> Pin<Box<dyn Future<Output = Result<(), KeyError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn revoke_key<'life0, 'async_trait>(
&'life0 self,
key_id: KeyId,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<Revocation, KeyError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn revoke_key<'life0, 'async_trait>(
&'life0 self,
key_id: KeyId,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<Revocation, KeyError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn revoke_key_audited<'life0, 'async_trait>(
&'life0 self,
key_id: KeyId,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<Revocation>, KeyError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn revoke_key_audited<'life0, 'async_trait>(
&'life0 self,
key_id: KeyId,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<Revocation>, KeyError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn publish_key_snapshot<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
key: KeyId,
snapshot: PublishableSnapshot,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, KeySnapshotError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn publish_key_snapshot<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
key: KeyId,
snapshot: PublishableSnapshot,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, KeySnapshotError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
snapshot for the principal of account’s credential key,
resolved inside the store (GL-143). Read moreSource§fn remove_key_snapshot<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
key: KeyId,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, KeySnapshotError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn remove_key_snapshot<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
key: KeyId,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, KeySnapshotError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
account’s credential key, tombstoning it
as AdminStore::remove_snapshot does. Allowed for a revoked
credential: revocation does not withdraw its snapshot, and withdrawal
is the safe direction.Source§fn active_keys<'life0, 'async_trait>(
&'life0 self,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<Vec<KeyRecord>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn active_keys<'life0, 'async_trait>(
&'life0 self,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<Vec<KeyRecord>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
now. Serving
instances use KeySource pages and an owned, bounded drain instead.
Retained for existing direct-store lifecycle tooling; no hidden page cap.Source§fn account_keys<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
after: Option<KeyId>,
limit: NonZeroUsize,
) -> Pin<Box<dyn Future<Output = Result<Vec<KeySummary>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn account_keys<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
after: Option<KeyId>,
limit: NonZeroUsize,
) -> Pin<Box<dyn Future<Output = Result<Vec<KeySummary>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
key_id, for an operator
listing (GL-121). Read moreSource§fn insert_key_within<'life0, 'async_trait>(
&'life0 self,
record: KeyRecord,
max_active: NonZeroUsize,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<(), KeyError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn insert_key_within<'life0, 'async_trait>(
&'life0 self,
record: KeyRecord,
max_active: NonZeroUsize,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<(), KeyError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
max_active
live ones, counting and inserting indivisibly (GL-121). Read moreSource§fn insert_key_within_audited<'life0, 'async_trait>(
&'life0 self,
record: KeyRecord,
max_active: NonZeroUsize,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, KeyError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn insert_key_within_audited<'life0, 'async_trait>(
&'life0 self,
record: KeyRecord,
max_active: NonZeroUsize,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<AdminReceipt<()>, KeyError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§impl KeySource for PostgresStore
impl KeySource for PostgresStore
Source§fn active_keys_page<'life0, 'async_trait>(
&'life0 self,
now: Timestamp,
after: Option<KeyId>,
limit: NonZeroUsize,
) -> Pin<Box<dyn Future<Output = Result<KeyPage, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn active_keys_page<'life0, 'async_trait>(
&'life0 self,
now: Timestamp,
after: Option<KeyId>,
limit: NonZeroUsize,
) -> Pin<Box<dyn Future<Output = Result<KeyPage, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
now; HTTP servers choose their own clock and return it as as_of.
Reads must be coherent with the returned revision, including for an
empty result. Never return a partial successful page after a failure.Source§impl LeaseAllocator for PostgresStore
impl LeaseAllocator for PostgresStore
Source§fn acquire<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
requested: CostUnits,
ttl: SignedDuration,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<Allocation, AllocateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn acquire<'life0, 'async_trait>(
&'life0 self,
account: AccountId,
requested: CostUnits,
ttl: SignedDuration,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<Allocation, AllocateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
GrantPolicy and may be smaller than requested;
the fencing token comes from a strictly increasing per-account
sequence. It remains a capability for this lease only; allocating a
newer token does not invalidate another active lease.Source§fn release<'life0, 'async_trait>(
&'life0 self,
lease_id: LeaseId,
fencing_token: FencingToken,
unspent: CostUnits,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<(), AllocateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn release<'life0, 'async_trait>(
&'life0 self,
lease_id: LeaseId,
fencing_token: FencingToken,
unspent: CostUnits,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<(), AllocateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
(lease_id, fencing_token) pair,
credit unspent back, and close the lease. Callers should flush usage
first when possible; events arriving after release are accepted only
when they fit its provisional settlement loss.Source§fn consolidate<'life0, 'async_trait>(
&'life0 self,
lease_id: LeaseId,
fencing_token: FencingToken,
unspent: CostUnits,
requested: CostUnits,
needed: CostUnits,
ttl: SignedDuration,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<Allocation, AllocateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn consolidate<'life0, 'async_trait>(
&'life0 self,
lease_id: LeaseId,
fencing_token: FencingToken,
unspent: CostUnits,
requested: CostUnits,
needed: CostUnits,
ttl: SignedDuration,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<Allocation, AllocateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn reclaim_expired_batch<'life0, 'async_trait>(
&'life0 self,
now: Timestamp,
limit: NonZeroUsize,
) -> Pin<Box<dyn Future<Output = Result<ReclaimBatch, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn reclaim_expired_batch<'life0, 'async_trait>(
&'life0 self,
now: Timestamp,
limit: NonZeroUsize,
) -> Pin<Box<dyn Future<Output = Result<ReclaimBatch, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
limit active leases whose TTL (plus the policy’s
reclaim grace) has lapsed and whose holder never released them. Read moreSource§fn reclaim_expired<'life0, 'async_trait>(
&'life0 self,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<Vec<ReclaimedLease>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
fn reclaim_expired<'life0, 'async_trait>(
&'life0 self,
now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<Vec<ReclaimedLease>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
Source§impl SnapshotSource for PostgresStore
impl SnapshotSource for PostgresStore
Source§fn principals<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<Vec<Principal>>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn principals<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<Vec<Principal>>, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Tombstones included, for the reason MemoryStore::principals gives:
forgetting a revoked principal is how one gets resurrected.
A primary-key scan, so no new index — the table holds one row per
principal, not per request, and ORDER BY makes the result stable so
a caller diffing two enumerations sees real changes rather than
storage order.
Source§fn snapshot<'life0, 'async_trait>(
&'life0 self,
principal: Principal,
) -> Pin<Box<dyn Future<Output = Result<SnapshotResolution, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn snapshot<'life0, 'async_trait>(
&'life0 self,
principal: Principal,
) -> Pin<Box<dyn Future<Output = Result<SnapshotResolution, StoreError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn subscribe(&self) -> Receiver<SnapshotPush>
fn subscribe(&self) -> Receiver<SnapshotPush>
snapshot() fetch after a lag error
observes at least the newest generation.Source§impl StoreHealth for PostgresStore
impl StoreHealth for PostgresStore
Source§impl UsageSink for PostgresStore
impl UsageSink for PostgresStore
Source§fn ingest<'life0, 'life1, 'async_trait>(
&'life0 self,
events: &'life1 [UsageEvent],
_now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<IngestReport, IngestError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn ingest<'life0, 'life1, 'async_trait>(
&'life0 self,
events: &'life1 [UsageEvent],
_now: Timestamp,
) -> Pin<Box<dyn Future<Output = Result<IngestReport, IngestError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
request_id; every event must match its
stored (lease_id, account_id, fencing_token) capability before lease
state and accounting capacity are checked. Partial acceptance is
normal — the report says what happened. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for PostgresStore
impl !UnwindSafe for PostgresStore
impl Freeze for PostgresStore
impl Send for PostgresStore
impl Sync for PostgresStore
impl Unpin for PostgresStore
impl UnsafeUnpin for PostgresStore
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> 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