Skip to main content

SubmitPolicy

Trait SubmitPolicy 

Source
pub trait SubmitPolicy: Send {
    // Required method
    fn check(&mut self, sub: &Submission) -> Result<(), String>;

    // Provided methods
    fn accepted(&mut self, _entry: &OpEntry, _hash: &ContentHash) { ... }
    fn subject(&self) -> (Option<String>, Option<String>) { ... }
    fn replicable(&mut self, _entry: &OpEntry) -> Result<(), String> { ... }
}
Expand description

Admission policy run inside the writer thread, before an op is ordered. This is where L8 signature verification and L1 view CAS plug into L2 without the sequencer depending on either layer.

Required Methods§

Source

fn check(&mut self, sub: &Submission) -> Result<(), String>

Accepts or rejects a submission. Runs on the writer thread, so a stateful policy (e.g. one holding a cached materialized view) sees submissions in their final total order.

§Errors

A human-readable rejection reason, returned to the submitter.

Provided Methods§

Source

fn accepted(&mut self, _entry: &OpEntry, _hash: &ContentHash)

Observes an entry that was just appended (for cached-state policies to fold). Default: ignore.

hash is the entry’s content hash, which the sequencer has just computed to answer the submitter. It is passed rather than left to the policy to recompute: a policy that indexes entries by hash would otherwise re-serialize every entry on the write path, which the allocation budget already caught once.

Source

fn subject(&self) -> (Option<String>, Option<String>)

What the last SubmitPolicy::check identified about its submission: (actor_id, op_type), for the journal.

Called by the sequencer immediately after check, on the same thread, so a policy that already derived these while checking hands them over rather than deriving them twice. That matters: actor_id comes from verifying a signature, and re-verifying every op to describe it would double the cost of the one step that is genuinely expensive.

The default answers nothing, which is honest for a policy that never looked. A journal then records the decision without an author, and an entry with a null actor_id says exactly that.

Source

fn replicable(&mut self, _entry: &OpEntry) -> Result<(), String>

Whether an entry another writer already sequenced can be folded here, asked by SequencerHandle::replicate before it appends one. Default: yes.

This is not admission, and must not become it. The writer that sequenced the entry decided whether it was admissible; a replica that re-decided would be a second writer with its own opinion of the order, which is the thing a replica exists not to be. So no signature, grant or quota is consulted here, and SubmitPolicy::check is never called on a replicated entry.

What is asked is narrower and has to be asked before the append: can this replica’s projection take the entry at all. A replica built from an older release may not know an op kind the writer admitted, and appending what SubmitPolicy::accepted then cannot fold would leave a log that no longer replays. Refusing here halts replication at that entry, which is the only honest outcome: a replica that skipped what it did not understand would be a fork.

§Errors

Why the entry cannot be folded, returned to the replicator.

Implementors§