Skip to content

OutboxEventStore stages SQL event rows and outbox rows using its inner store’s provider transaction. The non-SQL fallback only writes to the inner store; it does not create outbox entries. The outbox table is not created automatically. An SQLite installation using the current relay needs a table with event_id (unique text), event_type, event_data, metadata, timestamp, status, and nullable published_at columns. Provision and migrate the schema before enabling the relay.

A process-local EventBusImpl.publish() acknowledges enqueue, not durable delivery or successful handler execution. OutboxPublisher therefore no longer treats its legacy event_bus argument as an acknowledgement. Its SQLite path requires an explicit acknowledged_sink implementing AcknowledgedEventSink.deliver(event): return None only after a durable handoff, and raise on failure. The caller is responsible for using a sink that actually meets this contract. No such production sink is supplied here. The PostgreSQL relay is currently refused rather than claiming unverified behavior.

Register every SQLite dataclass event type on the inner SqliteEventStore with register_event_type(ClassName, EventClass), or provide a trusted custom event serializer/deserializer. The default relay reconstructs declared types, checks the event class and ID, and rejects payloads that do not round-trip. Unknown or malformed events remain pending; the relay logs failures. A custom serializer is responsible for preserving the complete event and its types.

Relay calls must run outside a database transaction. The relay requires local SQLiteProvider (direct or through DatabaseService) and, if pooled, the local SQLiteConnectionPool; both must advertise the committed-read capability. Older wheels, other providers and third-party pools are refused. These checks prevent uncommitted rows from being delivered on the tested local connection paths. A successful handoff is followed by a conditional update to published; failure to update leaves the row pending or raises. A crash, concurrent relays, or a failed update can produce duplicate handoffs. The sink or downstream consumer must deduplicate by event ID or otherwise be idempotent. Neither exactly-once delivery nor per-stream ordering is promised. Failed entries remain pending and can fill the batch indefinitely, so operators need monitoring and a retry/dead-letter policy outside this component.

The SQLite checks use local providers and sink doubles. They do not prove a production transport, multiple-process behavior, generated-application wiring, PostgreSQL behavior, or end-to-end delivery. Builder event effects remain blocked until their transaction, transport, schema and generated wiring are proven together.