Package com.onec.mail.outbox
Class MailOutbox
java.lang.Object
com.onec.mail.outbox.MailOutbox
Side-effect queue for outbound mail. Lives in its own table
onec_mail_outbox
rather than the framework's domain-event outbox: mail is a command-to-execute, not an event-to-broadcast,
and giving it a separate table avoids racing with the Kafka relay over the shared onec_outbox.
Works on both supported engines: PostgreSQL gets the lock-free fast paths
(ON CONFLICT, UPDATE ... RETURNING with SKIP LOCKED); H2 — used by
single-node dev/demo apps — gets portable equivalents selected via SqlDialect.
-
Nested Class Summary
Nested Classes -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionclaimBatch(int limit, Duration leaseTimeout) Atomically claims up tolimitdue messages for this worker, flipping themNEW -> SENDING.Enqueues a message.voidvoidmarkDispatched(UUID id) voidrecordFailure(UUID id, int attempts, String error, LocalDateTime nextAttempt, boolean exhausted)
-
Constructor Details
-
MailOutbox
public MailOutbox(org.jdbi.v3.core.Jdbi jdbi)
-
-
Method Details
-
initSchema
public void initSchema() -
enqueue
-
enqueue
Enqueues a message. WhenidempotencyKeyis non-blank and a row with that key already exists, the existing id is returned and no new row is inserted, so retried application logic can't double-send. -
claimBatch
Atomically claims up tolimitdue messages for this worker, flipping themNEW -> SENDING. On PostgreSQL this is a single statement;FOR UPDATE SKIP LOCKEDlets concurrent relays (multiple app instances) grab disjoint batches instead of all selecting the same rows and sending duplicates. On H2 — which has neitherUPDATE ... RETURNINGnorSKIP LOCKED— the claim is a transactionalSELECT ... FOR UPDATEfollowed by the status flip: concurrent claimers serialise on the row locks rather than skipping, which is fine for the single-node setups H2 backs. Rows stuck inSENDINGlonger thanleaseTimeout— a worker that crashed mid-send — are reclaimed on either engine. -
markDispatched
-
recordFailure
public void recordFailure(UUID id, int attempts, String error, LocalDateTime nextAttempt, boolean exhausted)
-