Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring Cloud Stream producer failure: a sent message is not an accepted business command

Last updated: 1 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

A producer call can return before broker acknowledgment; an asynchronous send failure needs its own observation and retry decision.

Separate local acceptance from broker acknowledgment

A parcel API that updates a database and then emits an event has two resources. A successful local function call does not prove the broker stored the event. Some binder paths report asynchronous producer failures through an error channel rather than the caller's stack. A producer failure handler can record the failed message, but it cannot retroactively roll back a committed database transaction.

Use a replayable handoff

Write the event to an outbox in the same database transaction as the parcel change, then relay with a stable event ID. Producer completion decides when the relay may acknowledge the outbox row. If acknowledgment is lost after broker acceptance, the relay may resend; consumers still need deduplication.

Test the ambiguous window

Make the broker accept an event and drop the producer acknowledgment, then restart the relay. Verify one business effect after the duplicate event arrives. Also force a definite producer failure and confirm the outbox row remains retryable. A function-level test without a broker cannot cover this window.

Implementation sketch

Java
@Transactional
public void recordParcel(ParcelAccepted command) {
    parcels.insert(command.parcelId(), command.units());
    outbox.insert(command.eventId(), command.parcelId(), "PARCEL_ACCEPTED");
}

Cost and verification

The outbox adds a database write and relay latency. It replaces an unobservable dual-write gap with retained work, retry cost and explicit reconciliation.

Common Mistakes

  • Do not treat a successful producer method return as durable broker acceptance.
  • Do not acknowledge an outbox row before the broker result is known.
  • Do not claim exactly-once delivery from a producer callback alone.

Read next

Spring Kafka producer future: observe broker send completion separately from command success, Spring transactional outbox: commit a receipt and event row together, Spring outbox transaction boundaries: where atomicity ends, Spring Cloud Stream consumer group: scale one logical reader without copying every event.

spring
spring-boot
cloud
cloud-stream-producer-failure
Storage details