Transaction and sending an email
Asked Answered
H

5

7

Considering the common use case of a user creating a new account on a web application and the application sending a confirmation email to the user's address. From what I've seen, this is typically implemented in one of 3 ways:

  1. The web controller calls a service method, which creates the user account and sends the email, both within a single transaction.
  2. The web controller calls a service method (with tx propagation = never), which invokes a 1st method on itself to create the user account within a transaction, and then invokes a 2nd method on itself to send the email.
  3. The web controller calls a 1st service method, which creates the user account within a transaction, then a 2nd service method that sends the email.

The 1st approach is simple and straightforward, but there is a risk that the transaction is rolled back after the email was sent, thereby making the email invalid. The 2nd approach is more complicated, but it guarantees that the email is sent only if user creation has actually succeeded. The 3rd approach is simple but burdens the web layer with business logic that it shouldn't need to know.

Isn't there a simpler approach, maybe AOP-driven, that guarantees that the email will be sent only if the user creation transaction actually succeeded? Am I paranoid in thinking that the 1st approach could fail?

We're using a Java EE + Spring stack and are willing to integrate additional APIs (AOP? Spring Integration?) to achieve this.

Cheers!

Haggi answered 2/12, 2010 at 3:30 Comment(0)
W
6

To send emails, it is recommanded to use a Queue and schedule the emails to be send like every 5 or 15 minutes. The queue will be stored in the database, therefor within the transaction. Then schedule a procedure to send email from that Queue at regular interval.

Its the only way i found to make sure the email is sent only when the transaction is coompleted and commited, since emails by definition are not tied to any kind of database transactions.

Wallacewallach answered 2/12, 2010 at 9:7 Comment(2)
Seems like a logical thing to do.Haggi
It's good for performance too: your controller doesn't have to wait for the e-mail to actually be sent, but it can return to the user immediately.Warplane
D
6

Another option I am currently using to solve this problem:

http://download.oracle.com/javaee/6/api/javax/transaction/Synchronization.html

Derma answered 10/12, 2010 at 9:58 Comment(2)
Interesting, I didn't know about that part of the API. Unfortunately we're not using JTA in our environment (only resource local transactions) so this approach isn't a viable option.Haggi
I am using local tx too with Spring managed tx which lets me register Synch objects: public void sendMailAfterCommit(final MailManager mail) { TransactionSynchronizationManager.registerSynchronization( new TransactionSynchronizationAdapter() { @Override public void afterCommit() { mailService.sendMail(mail); } }); }Derma
H
0

I would add a lightweight JMS layer like ActiveMQ for the email, it is pretty easy to setup and integrates (and even embeds) with Spring. Then you have

1) User creation transaction & send of JMS message happening in 1 transaction. If either fails you're still in a good state (both commit or both rollback and you present an error to the user)

2) If the JMS consumer fails to send the email you can setup the JMS queue to retry a few times and you'll have a better solution for managing transient problems with your email system.

Hadden answered 6/12, 2010 at 15:21 Comment(1)
This seems like a logical thing to do also. I'm not familiar with JMS, so I'm wondering: how can sending the JMS message and updating the database be enrolled in the same transaction? Do we need to setup XA transactions to achieve this, since the JMS persistent store won't necessarily be the application database?Haggi
L
0

Queuing with database table and run scheduling program with Quartz or something like whould be reasonable and easy to implement.

It's also good idea to use RabbitMQ for those feature development. RabbitMQ is quite easy to configure and can be used for the most of the case for publish/subscribe interworking betwen systems even though you may need to implement application level ack and small apps that subscribes messages from RabbitMQ and send email through SMTP.

It may sounds overkill, but you can use this #2 solution for every system that requires email send in the future. You can still use database queue based model for this, but will use expensive database cpu/resources for to send emmail.

Loralorain answered 20/2, 2013 at 0:16 Comment(0)
K
0

You can use event-based approach.

  1. Publish event via spring event published from transaction
  2. Create @TransactionalEventListener to handle this event and send email here @TransactionalEventListener supports different transaction phase (AFTER_COMMIT by default) In this case you could be sure that email will be sent only if transaction is commited
Kratzer answered 2/8, 2023 at 13:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.