Skip to main content

Reducer

Trait Reducer 

Source
pub trait Reducer: Send {
    type Op: Send + Sync;
    type ReadState;
    type ApplyState: Send;
    type Event: Send + Clone;
    type Error;

    // Required methods
    fn prepare<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 mut self,
        db: &'life1 dyn Db,
        op: &'life2 Self::Op,
    ) -> Pin<Box<dyn Future<Output = Result<Self::ReadState, Self::Error>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait;
    fn apply(
        &self,
        batch: &mut dyn DbBatch,
        timestamp: Timestamp,
        op: &Self::Op,
        read: Self::ReadState,
    ) -> Result<Self::ApplyState, Self::Error>;
    fn post_apply(
        &self,
        apply_state: Self::ApplyState,
        batch_result: &[DbStatementResult],
    ) -> Result<Vec<Self::Event>, Self::Error>;
}
Expand description

Translates a single op into the SQL writes that materialize it, in three phases so the work maps onto every backend — including ones with no interactive transaction (e.g. D1’s batch()):

  1. prepare runs before the batch. It is the only phase allowed to read or issue DDL, and it returns the ReadState apply needs — so every read is hoisted out of the batch.
  2. apply emits the op’s mutation statements into the open batch. It is pure and read-free (it consumes the ReadState), so the batch stays a flat, declarative statement list.
  3. post_apply runs after the batch commits, when RETURNING rows finally exist, and turns the results into zero or more events — empty when the op changed nothing observable.

§Idempotency

Whether a reducer must apply idempotently is not its own choice — it depends on the LogTracker it is paired with. Behind a tracker that rejects a repeated (peer_id, entry_idx) (e.g. LogIndexTracker) a redundant apply rolls back, so the reducer need not be idempotent; behind one that does not reject, it must be. See the tracker’s duplicate-rejection contract.

Required Associated Types§

Source

type Op: Send + Sync

The op vocabulary this reducer materializes (e.g. the table op enum).

Source

type ReadState

Data read in prepare and consumed by apply: e.g. a card’s prior FSRS state, or its full review history when an out-of-order op forces a recompute. () when apply needs nothing read.

Source

type ApplyState: Send

Carried from apply to post_apply: the StmtIds of the emitted statements plus any op-derived data needed to build the event.

Source

type Event: Send + Clone

The change event produced for an applied op, for downstream observers.

Source

type Error

Error surfaced from any phase.

Required Methods§

Source

fn prepare<'life0, 'life1, 'life2, 'async_trait>( &'life0 mut self, db: &'life1 dyn Db, op: &'life2 Self::Op, ) -> Pin<Box<dyn Future<Output = Result<Self::ReadState, Self::Error>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Reconcile the schema needed by op (create/alter tables, refresh any cache) and read whatever apply will need, returning it as a ReadState. Runs outside the batch — DDL is additive and safe to commit on its own, and hoisting reads here is what keeps apply pure.

Source

fn apply( &self, batch: &mut dyn DbBatch, timestamp: Timestamp, op: &Self::Op, read: Self::ReadState, ) -> Result<Self::ApplyState, Self::Error>

Emit the statements that materialize op at timestamp into batch, using only op, the cached schema, and read. Read-free, so it stays expressible as a declarative batch. The returned ApplyState is provisional until batch commits.

Source

fn post_apply( &self, apply_state: Self::ApplyState, batch_result: &[DbStatementResult], ) -> Result<Vec<Self::Event>, Self::Error>

Build the events once the batch has committed — empty when the op changed nothing observable (e.g. an upsert that lost every column’s LWW, or a write to a table with no observer-facing name), usually one, or several when a single op has multiple logical effects. batch_result holds the whole batch’s per-statement results in add order; locate this op’s RETURNING rows via the StmtIds stored in apply_state.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§