We have a large application where emails are sent as part of all the Service
s' @Transaction
s. A typical Service
method is
@Transactional(readOnly = false, rollbackFor = {Exception.class})
public String operation() {
//...
sendEmail(..); // will call JavaMailSender.send()
//...
if (condition) {
sendEmail(..); // will call JavaMailSender.send()
}
}
The mail server often has issues which cause the whole transaction to get rolled back. We need to take the emailing out of the transactions. It's not feasible to move these lines one-by-one out of all the Service
s, or change the rollbackFor
annotation for each, due to the size of the existing app. I was thinking of adding @Async
to the custom sendEmail
method but doing that inside a transaction is not recommended and can still impact the transaction.
Some options under consideration are:
- Use a message queue e.g.
RabbitMQ
in Spring. The methodsendMail
can be tweaked to enqueue a serialized mail object with params, and a listener will consume. But this requires opening a new port for MQ - A microservice: also requires opening a new URL and port
- A cron that runs every minute and processes incomplete rows entered by a tweaked
sendMail
that just adds table rows.
But is there a simpler, centralized robust solution that doesn't require all this new architecture? The actual emailing simply has to be outside all transactions.