Skip to content

feat(messaging): a deferred announcement survives a broker outage - the durable publish - #6874

Merged
delchev merged 1 commit into
masterfrom
feat/durable-event-enqueue
Aug 20, 2026
Merged

feat(messaging): a deferred announcement survives a broker outage - the durable publish#6874
delchev merged 1 commit into
masterfrom
feat/durable-event-enqueue

Conversation

@delchev

@delchev delchev commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Part two of #6867, completing it (part one is #6869 — merge that first; this PR touches adjacent IntentEngineIT regions).

The write-attached outbox (#6854) covers an event that belongs to one write. Five generated announcements are deliberately decoupled from any single write and remained bare publishes — each a live event-loss window:

Site Why it cannot ride a write
SetField, Writer, Numbering deferred via Process.executeAfterCommit so consumers observe the whole synchronous Flowable chain's writes — write-attachment would regress that ordering fix
StepEvent a process moment with no entity write of its own
Generate completion announce ordered after the target and its items exist, across several transactions

They now publish through Producer.sendToTopicDurable: recorded in the tenant's event outbox in its own short transaction, dispatched immediately, retried by EventOutboxRelayJob when the broker refuses — the same at-least-once contract the write-attached events have, with ordering untouched. Recording failure falls back to the direct publish (the caller's work has already committed).

Plumbing: DurableMessagePublisher SPI in api-messaging (visible to both sides without a dependency cycle), implemented by EventOutbox with an own-connection EventOutboxStore.insert, resolved by the SDK Producer via BeanProvider. The engine-java guide's doctrine line now names both rules: one-write announcements ride the write; decoupled ones use the durable publish.

Verified: IntentEngineIT 56/56 (anchors updated to the durable call), IntentEmissionCoverageIT (workflow setters, step moments, stamped numbers and completion announces all flow through the new path at runtime), JavaEventOutboxIT, release-profile javadoc gate on the three modules.

Closes #6867 (together with #6869).

🤖 Generated with Claude Code

…he durable publish (#6867)

The write-attached outbox (#6854) covers an event that belongs to ONE
write. Five generated announcements are deliberately DECOUPLED from any
single write and stayed bare Producer.sendToTopic calls - each a live
instance of #6816's loss class, where a briefly-down broker swallows the
notice and every downstream reaction keyed on it silently never runs:

- SetField, Writer and Numbering defer their "-transitioned"/"-updated"
  publish past the synchronous Flowable chain's commit, so consumers
  observe the whole chain's writes rather than a mid-chain snapshot -
  attaching the event to the write would regress exactly that;
- a StepEvent moment has no entity write of its own at all;
- the create-from's completion announce is ordered after the target and
  its items exist, across several transactions.

They now publish through Producer.sendToTopicDurable: the message is
recorded in the tenant's event outbox in its own short transaction and
handed to the broker immediately - a healthy system publishes exactly as
promptly as before - and whatever the broker refuses stays for
EventOutboxRelayJob, the same at-least-once contract the write-attached
events have. Ordering is untouched: the deferral points stay where they
are; only the loss window closes. If even the recording fails, the
message falls back to the direct publish - the caller's work has already
committed and an at-most-once attempt beats none.

The plumbing: DurableMessagePublisher is a small SPI in api-messaging
(the module both sides already see - the SDK cannot depend on the
outbox's module and the outbox already depends on messaging), EventOutbox
implements it with an own-connection insert on the store, and the SDK
Producer resolves it through BeanProvider. The javadoc states the rule:
an announcement that belongs to one write rides that write; this is for
the decoupled ones.

Emission is pinned by the existing IntentEngineIT anchors, updated to the
durable call; runtime by IntentEmissionCoverageIT (the workflow setters,
step moments, stamped numbers and completion announces all flow through
the new path) and JavaEventOutboxIT.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@delchev
delchev merged commit b3cdd04 into master Aug 20, 2026
10 checks passed
@delchev
delchev deleted the feat/durable-event-enqueue branch August 20, 2026 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

templates: generated glue still publishes beside the write - the bare Producer.sendToTopic sites contradict the outbox and stay event-loss windows

1 participant