Reputation: 9540
I’m using Postgres and have a transactional method defined as follows:
@Entity
public class SomeEntity {
// …
}
@Transactional(isolation = READ_COMMITED)
public void persistUniqueAndSendEmail() {
SomeEntity e = // …
// Persis the entity to Postgres with a unique constraint that may fail
// Once the entity is persisted send the notification email
}
With READ_COMMITED isolation level is it possible that the unique constraint violation is thrown when the transaction is actually committed, not the actual sql statement is executed?
If so that would mean that the email can be sent, but the relevant changes are not persisted.
Upvotes: 2
Views: 146
Reputation: 96454
The transaction doesnt finish until the end of the method. The save doesnt flush until the transaction finishing prompts it to. So yes, you could get a constraint violation that rolls back the save after the email is sent.
You could try calling saveAndFlush instead of save (see Difference between save and saveAndFlush in Spring data jpa). The flush forces the database call to occur, which will trigger any constraint violation (even while the transaction is still in progress).
But you don't want the transaction to fail for some other reason and have the email sent erroneously, and there are plenty of ways that can happen. The safe thing to do (well, a relatively safer way) is make sure the transaction completes successfully, then send the email either in another service calling the transactional service, or in an AOP interceptor.
That way is safe for the database work, but is not safe for the email-sending, because as this answer points out thoroughly, you do have a 2-system problem. It's worth thinking about what happens in your posted code when your transaction rolls back, and the email still gets sent. If you want your transaction to be repeated you have to send the email again. Mixing non-transactional stuff like email in with transactional database updates is fraught with peril.
Check your requirements, find out the consequences of failing to send an email, or of sending duplicate emails, and use that information to decide how much trouble to go to coordinating these two systems.
Upvotes: 2
Reputation: 103893
You have a few problems here.
Yes, it can. Specifically, constraint checks can be done on the spot or when you commit and it depends on the DDL, not the setup of the connection. In PSQL, such constraint checks are triggers and you can configure them: Don't run (bad idea, but can be useful when you're bulk importing), Run on the spot, and defer until commit. We have no idea how you configured your DB (what SQL you wrote to set up these constraints). By default constraints are checked as the statement is executed.
Use SERIALIZABLE instead. Your database's integrity will thank you.
Regardless of the situation of where constraints are checked, a commit is never guaranteed. What if you send out the email and then someone trips over the power cable? If you reverse the situation and commit, then send the email, what happens if someone trips over a power cable after the commit but before the email is sent?
You're stuck here in a 2-system commit cascade. You have 2 things that both aren't atomic and you want either both to occur (an email is sent and the db row is updated), or neither to occur. But that is not possible - 2 disparate systems can't do that.
You have many ways out of the dilemma and they're all bad. Some general aspects:
If the last entry is 'about to send an email' then one of two things has happened:
You can't solve the dilemma without a separate API or other system that lets you check what happened. For example, if you're sending via gmail y ou could use its API to check the sent box. Worst comes to worst, on boot you just hard crash and demand that a human operator update the entry to explain precisely what the fate is of that email. Or, pick some default (when in doubt, send twice - for example. i.e. assume the email did not go out, so to restore state, send it again).
This is quite complicated but the only way to atomicize 2 disparate systems.
Upvotes: 2