A poller can remove a message before a handler commits unless its receive, handler and store use the intended transaction boundary.
Spring Integration poller: include receive and handler in the failure boundary
Identify who owns receive
A PollingConsumer reads from a PollableChannel on a schedule. Adding a transaction advice to the poller can make receive and downstream JDBC work share the same transaction when they use the same transaction manager and database. With a persistent channel store, a handler exception can roll back removal. An in-memory QueueChannel cannot regain a lost process's state. Store choice comes first.
Limit each poll
Set a bounded number of messages per poll and a fixed delay that does not flood the database when no work exists. A handler that blocks on an external API holds the poller thread and possibly a database connection. Move remote delivery to an outbox or use a claim-and-ack protocol with fencing when the write cannot share the message-store transaction.
Test the rollback path
Insert a message, fail the handler after the receive, and assert the message is still available or redelivered according to the configured store. Then restart the JVM. The outcome is store- and binder-dependent; do not infer it from the scheduling annotation alone. This page is an implementation sketch rather than a checked poller fixture.
Implementation sketch
@ServiceActivator(inputChannel = "persistedParcelQueue",
poller = @Poller(fixedDelay = "7000", maxMessagesPerPoll = "23"))
public void handleParcel(Message<ParcelAccepted> message) {
parcelHandler.accept(message.getPayload());
}Cost and verification
Polling adds periodic database or broker reads, even at low traffic. Larger batches improve throughput but enlarge retry scope and the time a transaction holds resources.
Common Mistakes
- Do not assume @Scheduled by itself wraps message receive and handler in one transaction.
- Do not let a poller transaction span an unbounded remote call.
- Do not infer crash recovery from an in-memory queue test.
Read next
Spring Integration queue: bound memory and choose a real message store, Spring outbox claims: lease expiry and token-fenced acknowledgement, Spring TaskScheduler: poll committed outbox rows in the background, Spring Integration idempotent receiver: the metadata key is not the business commit.
