pub struct Inbox { /* private fields */ }Expand description
A dedup ledger over the Database port. Construct it with the table the
owning module declares (e.g. "<module>_inbox").
Implementations§
Source§impl Inbox
impl Inbox
pub fn new(table: impl Into<String>) -> Self
Sourcepub fn create_table_sql(&self) -> String
pub fn create_table_sql(&self) -> String
The portable DDL for the inbox table. The owning module ships this as a forward-only migration (it renders identically on SQLite/D1 and Postgres, ADR 0004).
Sourcepub async fn claim(
&self,
db: &dyn Database,
event_key: &str,
seen_at: &str,
) -> Result<bool, DbError>
pub async fn claim( &self, db: &dyn Database, event_key: &str, seen_at: &str, ) -> Result<bool, DbError>
Records event_key, returning true the first time it is seen
(proceed and apply the effect) and false for any later delivery of the
same id (a duplicate/replay/concurrent race — skip).
The insert is a single ON CONFLICT DO NOTHING statement, so it is
atomic on both D1 and Postgres: under concurrent claims of the same key
exactly one insert takes, and only that caller gets true. seen_at is
an RFC 3339 timestamp the caller reads from the Clock port.
The claim commits on its own: if the worker dies after this returns
true but before the effect is applied, the redelivery sees the key and
skips — the event is lost (at-most-once). When the effect is itself
database writes, use claim_with, or put
claim_statement first in the same
db.batch_atomic(..) as the effect, so the two commit together.
§Errors
Returns DbError if the write fails.
Sourcepub fn claim_statement(&self, event_key: &str, seen_at: &str) -> Statement
pub fn claim_statement(&self, event_key: &str, seen_at: &str) -> Statement
The claim as a bare INSERT with no ON CONFLICT clause, to run
as the first statement of the same db.batch_atomic(..) that carries
the effect’s statements (see claim_with for the
ready-made wrapper).
Inside the batch the primary key does the first-writer-wins work: a key that is already claimed fails the insert, and the whole batch — effect included — rolls back, so a duplicate delivery applies nothing a second time. The claim and the effect commit together or not at all: a worker that dies before the batch commits leaves the key unclaimed, and the redelivery re-runs both.
Sourcepub async fn claim_with(
&self,
db: &dyn Database,
event_key: &str,
seen_at: &str,
effect: &[Statement],
) -> Result<bool, DbError>
pub async fn claim_with( &self, db: &dyn Database, event_key: &str, seen_at: &str, effect: &[Statement], ) -> Result<bool, DbError>
Claims event_key and applies effect in one atomic batch, returning
true when this delivery committed them (the convention of
claim) and false when the key was already claimed —
a duplicate, a replay, or a concurrent delivery that won the race — so
the effect did not run again.
The claim_statement goes first, then
effect, all through batch_atomic: either
everything commits or nothing does, and a worker that dies mid-batch
leaves the key unclaimed for the retry.
§Errors
Returns DbError when the batch fails and the key is not claimed
afterwards: nothing committed and the effect did not apply — the caller
(or its queue) retries the whole delivery. A batch that failed because
a concurrent delivery claimed the key first is not an error; it returns
Ok(false), told apart by re-reading the key rather than parsing the
engine’s error text.