pub trait EventStore: Send + Sync {
Show 16 methods
// Required methods
fn stream_id(&self) -> &str;
fn append<'life0, 'async_trait>(
&'life0 mut self,
event: Map<String, Value>,
) -> Pin<Box<dyn Future<Output = Result<Committed>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn append_if<'life0, 'life1, 'life2, 'async_trait>(
&'life0 mut self,
kinds: Option<&'life1 [&'life2 str]>,
decide: Decision,
) -> Pin<Box<dyn Future<Output = Result<Option<Committed>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait;
fn read_kinds<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
kinds: Option<&'life1 [&'life2 str]>,
from_seq: u64,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Current>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait;
fn head<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<u64>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn len<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
// Provided methods
fn append_many<'life0, 'async_trait>(
&'life0 mut self,
events: Vec<Map<String, Value>>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Committed>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn append_at<'life0, 'async_trait>(
&'life0 mut self,
epoch_ms: u64,
event: Map<String, Value>,
) -> Pin<Box<dyn Future<Output = Result<Committed>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn append_expecting<'life0, 'async_trait>(
&'life0 mut self,
expected: Expected,
event: Map<String, Value>,
) -> Pin<Box<dyn Future<Output = Result<Committed>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn detach_append(&self, event: Map<String, Value>) -> Result<()> { ... }
fn read<'life0, 'async_trait>(
&'life0 self,
from_seq: u64,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Current>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn read_last<'life0, 'async_trait>(
&'life0 self,
n: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Current>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn is_empty<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn database(&self) -> Option<&str> { ... }
fn query<'life0, 'life1, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: Vec<Value>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Map<String, Value>>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait { ... }
fn query_timeout<'life0, 'life1, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: Vec<Value>,
timeout: Duration,
) -> Pin<Box<dyn Future<Output = Result<Vec<Map<String, Value>>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait { ... }
}Required Methods§
Sourcefn append<'life0, 'async_trait>(
&'life0 mut self,
event: Map<String, Value>,
) -> Pin<Box<dyn Future<Output = Result<Committed>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn append<'life0, 'async_trait>(
&'life0 mut self,
event: Map<String, Value>,
) -> Pin<Box<dyn Future<Output = Result<Committed>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Validate, stamp and append an event, returning its coordinates.
A rejected event leaves no trace and consumes no sequence number.
Sourcefn append_if<'life0, 'life1, 'life2, 'async_trait>(
&'life0 mut self,
kinds: Option<&'life1 [&'life2 str]>,
decide: Decision,
) -> Pin<Box<dyn Future<Output = Result<Option<Committed>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn append_if<'life0, 'life1, 'life2, 'async_trait>(
&'life0 mut self,
kinds: Option<&'life1 [&'life2 str]>,
decide: Decision,
) -> Pin<Box<dyn Future<Output = Result<Option<Committed>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Decide inside the store’s serialization: read the stream, ask
decide what to write, and append its answer in the same write.
kinds filters what the decision is shown, exactly as it filters
EventStore::read_kinds (None = the whole stream), and the events
arrive in seq order. What the decision writes is unfiltered — the
event it returns is appended whatever its kind. Returning None
records nothing and leaves the stream untouched.
Sourcefn read_kinds<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
kinds: Option<&'life1 [&'life2 str]>,
from_seq: u64,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Current>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn read_kinds<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
kinds: Option<&'life1 [&'life2 str]>,
from_seq: u64,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Current>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Events of kinds with seq >= from_seq, at most limit, in seq
order and already upcasted.
None reads every kind. Some(kinds) reads only those, and limit
counts what came back rather than what was skipped. An empty slice
selects nothing.
The kind selected on is the stored one, because the selection happens in the backend, before the upcaster chain runs. A step that renames a kind therefore obliges every filtered read of it to name both spellings — which is a cost the chain’s author pays knowingly, and the reason a rename is not free.
Sourcefn head<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<u64>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn head<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<u64>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
The highest seq, or None for an empty stream.
Fallible on purpose: a transient failure must not read as an empty stream, or a caller deciding open-vs-resume takes the wrong branch.
Provided Methods§
Sourcefn append_many<'life0, 'async_trait>(
&'life0 mut self,
events: Vec<Map<String, Value>>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Committed>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn append_many<'life0, 'async_trait>(
&'life0 mut self,
events: Vec<Map<String, Value>>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Committed>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Append events as one write, in the order given.
For facts that are one fact: two records of a single occurrence must not be separable by a reader, because a stream where the first is visible without the second is a stream that never existed.
All or nothing on a backend that can do it. The default below is the most a backend with no transaction can offer — it appends one at a time, so a failure part-way leaves what already landed. Both shipped backends override it.
Sourcefn append_at<'life0, 'async_trait>(
&'life0 mut self,
epoch_ms: u64,
event: Map<String, Value>,
) -> Pin<Box<dyn Future<Output = Result<Committed>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn append_at<'life0, 'async_trait>(
&'life0 mut self,
epoch_ms: u64,
event: Map<String, Value>,
) -> Pin<Box<dyn Future<Output = Result<Committed>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Record an event with a time it already has, rather than the wall clock of this call.
The backfill counterpart to EventStore::append: history from a
system that has its own notion of when, brought in without discarding
that timeline. Identical to append in every other respect — the
envelope is validated, seq and the position are this store’s, the
schema version is the author’s.
Use append for ordinary writes. For moving an eventsdb log,
neither this nor append is the verb — import is, because it also
carries the schema version each event was written under, which this
cannot.
See crate::event for what the time coordinate means and why the
verb rather than a field is what says which moment it is. In short:
positions order the log, the coordinate never does, and this call does
not enforce that the time it is given is at or after the stream’s head.
Backfilling into an empty stream in chronological order keeps that
property by construction.
Sourcefn append_expecting<'life0, 'async_trait>(
&'life0 mut self,
expected: Expected,
event: Map<String, Value>,
) -> Pin<Box<dyn Future<Output = Result<Committed>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn append_expecting<'life0, 'async_trait>(
&'life0 mut self,
expected: Expected,
event: Map<String, Value>,
) -> Pin<Box<dyn Future<Output = Result<Committed>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Append event only if the stream’s head is expected, refusing with
Error::HeadMismatch if it is not.
For a decision taken before this call, somewhere this process
cannot reach: an HTTP client holding an ETag, a form somebody filled
in, a message that sat in a queue. The caller read the stream at some
head, went away, and is now saying “apply this only if nothing moved”.
EventStore::append_if is the other case and the better one wherever
it applies — a decision folded from the stream at the write, which
this cannot express because it compares one number and never looks at
the events. Reach for this only when the decision could not have run
under the lock.
The comparison happens inside the write. The head is read in the
same transaction that inserts, so a writer arriving between them
serializes behind the write lock rather than slipping past the check. A
refused append leaves no trace and consumes no sequence number, exactly
as a rejected EventStore::append does.
The default declines. A backend that cannot make the read and the insert one write has no honest answer here, and an imitation that checked separately would be worse than none — that gap between lookup and save is the documented weakness of the version-only form elsewhere.
Sourcefn detach_append(&self, event: Map<String, Value>) -> Result<()>
fn detach_append(&self, event: Map<String, Value>) -> Result<()>
Queue an append and return without waiting for it to land.
For a fact that has to be recorded from somewhere that cannot await:
a Drop closing a session is the case this exists for, and there
blocking is not allowed either, so neither .await nor
block_on is available. Hence a plain fn — an async fn would need
an executor the caller does not have.
&self, not &mut self. A Drop has whatever reference it has,
and demanding a unique one is the difference between reachable from
there and not. Every other write on this trait takes &mut self
because it returns coordinates the caller is expected to use; this one
returns nothing to hold.
The envelope is checked here; the write is not reported. A malformed event is refused synchronously, before the call returns. After that there is no channel back: a storage failure is dropped and nothing is woken, because the caller has already gone. Use it for the boundary record whose absence a reader would notice — the close of a session that would otherwise read as still open — not for a fact nothing else in the system knows.
Ordering is the backend’s to keep. The queued write must land
before anything submitted after it; a backend that spawns a task which
later calls EventStore::append has left the queue and races every
other writer, which is worse than declining.
The default declines rather than dropping the event quietly. A store with nowhere to queue it has no way to keep that ordering, and silence here would leave a stream looking open for ever with nothing to say why.
Sourcefn read<'life0, 'async_trait>(
&'life0 self,
from_seq: u64,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Current>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn read<'life0, 'async_trait>(
&'life0 self,
from_seq: u64,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Current>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Every event with seq >= from_seq, at most limit.
Sourcefn read_last<'life0, 'async_trait>(
&'life0 self,
n: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Current>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn read_last<'life0, 'async_trait>(
&'life0 self,
n: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Current>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
The last n events, in seq order.
A read from the end, which the range reads cannot express: they
start at a seq and count forward, so “the last five” could only be
asked for by reading the whole stream and throwing the front away. The
default does exactly that, and both shipped backends override it.
Sourcefn is_empty<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn is_empty<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Whether nothing has been recorded yet.
Sourcefn database(&self) -> Option<&str>
fn database(&self) -> Option<&str>
Which database this stream lives in, or None for a backend that is
not one.
Two handles answer with the same string exactly when they are the same database. It is an identity, not a path a caller should take apart.
Sourcefn query<'life0, 'life1, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: Vec<Value>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Map<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn query<'life0, 'life1, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: Vec<Value>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Map<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Answer a caller’s own read-only SQL over the log.
Queries read the stored shape, not the upcasted one: every other read goes through the chain, but SQL runs against the bytes as they were written, because the chain is Rust and the query is SQLite’s. A caller reading across a schema change reads the versions it finds.
The limit is yours, and so is knowing whether it cut. There is no
truncated flag here because the LIMIT is in your text, not in a
parameter this store owns: ask for n + 1 rows and compare, which is
the whole of what such a flag would tell you. Owning the limit instead
would mean wrapping your statement to attach one, and this call
deliberately never rewrites or parses what it is given — the read-only
check above is SQLite’s answer about your text, not a reading of it.
Parameters are positional here, and only here. SQLite binds by name
as well, and crate::Params is how a backend’s own query offers
both — but this trait is used as Box<dyn EventStore>, a dispatchable
method may not have type parameters, and argument-position impl Trait
is one. So the trait takes the Vec and a caller wanting :name calls
the concrete log.
The default refuses, because a store that is not a database has no answer to give.
Sourcefn query_timeout<'life0, 'life1, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: Vec<Value>,
timeout: Duration,
) -> Pin<Box<dyn Future<Output = Result<Vec<Map<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn query_timeout<'life0, 'life1, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: Vec<Value>,
timeout: Duration,
) -> Pin<Box<dyn Future<Output = Result<Vec<Map<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
EventStore::query with a bound on how long the statement may run.
Not the same thing as busy_timeout. That one bounds waiting for
a lock; this bounds a statement that took its lock immediately and is
simply expensive — a recursive CTE with a runaway bound, a join with no
usable index. Nothing else stops one, and a caller that hands SQL to
somebody else (a shell, a script, a user) cannot know in advance which
kind it is getting.
A backend serving SQL from the same place it serves writes has the stronger reason: one expensive statement there stalls every append until it finishes.
The deadline is reported as Error::Timeout — the caller’s own bound
arriving, not the database failing — so retrying a narrower query is
the sensible next move.
The default declines rather than falling back to EventStore::query:
running an unbounded statement for a caller who asked for a bound is
the one answer that is worse than none.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".