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§
Sourcefn check(&mut self, sub: &Submission) -> Result<(), String>
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§
Sourcefn accepted(&mut self, _entry: &OpEntry, _hash: &ContentHash)
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.
Sourcefn subject(&self) -> (Option<String>, Option<String>)
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.
Sourcefn replicable(&mut self, _entry: &OpEntry) -> Result<(), String>
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.