pub struct PgEventStore { /* private fields */ }Expand description
The PostgreSQL event store.
Implementations§
Source§impl PgEventStore
impl PgEventStore
Sourcepub async fn ensure_genesis(&self, public_revision: &str) -> Result<(), Refusal>
pub async fn ensure_genesis(&self, public_revision: &str) -> Result<(), Refusal>
Append genesis if this log has none. Idempotent: a kernel that already has an epoch is left exactly as it was.
Separate from PgEventStore::open on purpose — opening a store reads,
and a constructor that writes would put an append in the path of every
replay and every read-only inspection of a log.
Source§impl PgEventStore
impl PgEventStore
Sourcepub async fn recover(&self) -> Result<RecoveryReport, Refusal>
pub async fn recover(&self) -> Result<RecoveryReport, Refusal>
Establish what a restart can prove about the projections.
Writes nothing except on the cold path, where the projections are empty
and the log is replayed into them through apply_event — the same
writer that would have filled them originally.
It never appends an event. An attempt that was running when the kernel
died has an outcome the kernel did not observe, and unknown is a
CLAIM about that outcome; minting one here would put a fact into the log
that nothing witnessed, and would bury an attempt whose engine is in
fact still alive. They are reported instead, and the transition stays an
ordinary command with an actor behind it.
Sourcepub async fn rebuild_into(
&self,
scratch: &PgPool,
) -> Result<RebuildReport, Refusal>
pub async fn rebuild_into( &self, scratch: &PgPool, ) -> Result<RebuildReport, Refusal>
Replay the whole log into an EMPTY scratch database and report whether it agrees with the live projections.
This is the only full verification available once the log has moved past
its newest checkpoint, and the only place a rebuild can happen at all,
since the live tables refuse to be reset. It reads the live log, writes
only to scratch, and leaves the decision to replace anything with the
operator.
Source§impl PgEventStore
impl PgEventStore
Sourcepub async fn open(pool: PgPool) -> KernelResult<Self>
pub async fn open(pool: PgPool) -> KernelResult<Self>
Bind to an initialized database and claim this boot’s writer epoch.
Claiming here rather than lazily means a superseded process discovers it at startup, not at the first append.
Sourcepub async fn with_capacity(pool: PgPool, inflight: usize) -> KernelResult<Self>
pub async fn with_capacity(pool: PgPool, inflight: usize) -> KernelResult<Self>
open with an explicit admission bound.
Exists so a test can saturate the queue deterministically — proving the bound refuses rather than queues needs a bound small enough to fill.
Sourcepub fn open_reader(pool: PgPool) -> Self
pub fn open_reader(pool: PgPool) -> Self
A store that can READ the log and cannot write it.
It claims no epoch, and that is the whole point. claim_epoch BUMPS a
durable counter, so a verification path that opened an ordinary store
would fence the daemon it was verifying out of its own log — the
rebuild-into-scratch comparison is meant to run against a live system,
not to take it down.
Epoch 0 is never a live epoch, so the fence every mutating transaction already compares refuses this store’s appends by construction. A reader cannot write even by mistake, and the guarantee costs no new check.
Sourcepub fn with_blobs(self, blobs: PgBlobStore) -> Self
pub fn with_blobs(self, blobs: PgBlobStore) -> Self
Attach the blob store a checkpoint writes its records to.
Without it this store never checkpoints, and that is a real state rather
than a degraded one: admin init and the certifier drive the log with no
filesystem at all, and a snapshot they neither want nor could store is
not something to make them configure around. What must not happen is a
SERVING kernel in that state, which is why the daemon’s own construction
takes the blob store rather than opting into it.
Sourcepub fn blobs(&self) -> Option<&PgBlobStore>
pub fn blobs(&self) -> Option<&PgBlobStore>
The blob store checkpoints write through, if one is attached.
Sourcepub async fn checkpoint_at_watermark(&self) -> Result<Option<Seq>, AppendError>
pub async fn checkpoint_at_watermark(&self) -> Result<Option<Seq>, AppendError>
Snapshot the projections at the watermark, whether or not one is due.
The one snapshot no append triggers. Self::checkpoint_if_due rides an
append’s transaction and is therefore always AT that append’s sequence; a
clean shutdown has no append to ride, so it takes the same writer barrier
itself — without it the hash would be taken over projections another
process is still moving, and a checkpoint that describes no single moment
is worse than none.
This is what makes Verdict::Verified
reachable in practice: it requires a checkpoint exactly at the watermark,
and the event-interval barrier only ever lands on a multiple of
CHECKPOINT_EVENT_INTERVAL. Without a snapshot on the way out, a
cleanly stopped kernel would restart into Unverified — honest, but a
weaker statement than it was entitled to make.
Ok(None) means there was nothing to snapshot: no blob store to put the
records in, or an empty log.
Sourcepub fn boot_epoch(&self) -> i64
pub fn boot_epoch(&self) -> i64
The epoch this process must present for the rest of its life.
pub fn pool(&self) -> &PgPool
Source§impl PgEventStore
impl PgEventStore
Sourcepub async fn submit(&self, envelope: &CommandEnvelope) -> KernelResult
pub async fn submit(&self, envelope: &CommandEnvelope) -> KernelResult
Apply one command, or answer why not.
A refusal is a value here, exactly as the contract says: this returns
KernelResult::Error rather than an Err, so the wire layer has
nothing left to translate.
Trait Implementations§
Source§impl EventStore for PgEventStore
impl EventStore for PgEventStore
Source§async fn append(
&self,
expected_version: u32,
fence: Option<FenceToken>,
events: Vec<EventEnvelope>,
) -> Result<Vec<EventEnvelope>, AppendError>
async fn append( &self, expected_version: u32, fence: Option<FenceToken>, events: Vec<EventEnvelope>, ) -> Result<Vec<EventEnvelope>, AppendError>
Source§async fn read_from(
&self,
cursor: Option<Seq>,
limit: usize,
) -> Result<Vec<EventEnvelope>, StorageError>
async fn read_from( &self, cursor: Option<Seq>, limit: usize, ) -> Result<Vec<EventEnvelope>, StorageError>
global_sequence strictly after cursor
(None = from the beginning), ascending, at most limit (clamped to
MAX_READ_LIMIT). Read moreSource§async fn watermark(&self) -> Result<Option<Seq>, StorageError>
async fn watermark(&self) -> Result<Option<Seq>, StorageError>
global_sequence, if any event exists.Source§async fn grant_fence(&self) -> Result<FenceToken, StorageError>
async fn grant_fence(&self) -> Result<FenceToken, StorageError>
Auto Trait Implementations§
impl !Freeze for PgEventStore
impl !RefUnwindSafe for PgEventStore
impl !UnwindSafe for PgEventStore
impl Send for PgEventStore
impl Sync for PgEventStore
impl Unpin for PgEventStore
impl UnsafeUnpin for PgEventStore
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
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