Code review

Transaction propagation or 100% deadlock with JPA/Hibernate.

Transaction propagation deadlock

In continuation of the topic about deadlocks I want to tell you about another case of getting deadlock with JPA/Hibernate. This deadlock I’ve already met two times and it is related to transaction propagation.

How often do you manipulate transaction propagation except using default one – REQUIRED (luckily same name for Hibernate and JPA)?

If you ask me, not much. So when I see propagation=REQUIRES_NEW in code I always suspect that something might smell.

@Transactional(propagation=REQUIRES_NEW)

What does REQUIRES_NEW means? Spring documentation says:

In other words:

  • if transaction doesn’t exist, it will create one
  • if transaction exists it will be suspended and new one will be created

There are not much use cases for using REQUIRES_NEW. In most cases it is saving some auditing information. For example: you are creating an order, and you want to save some auditing information about an attempt to create an order. And you don’t care if order saving fails or not (due to some errors or validations).

How to get a deadlock with transaction propagation REQUIRES_NEW?

Very easy! Consider next example:

  • facade begins transaction and creates an object
  • service does something on the same object but with Propagation.REQUIRES_NEW

In code it looks like this:

To run this code I created a spring boot test:

When you run it, you will not get an exception from your database like in this case but the thread of your application will be stuck. And this will be your last logs before deadlock:

What happens in database:

  • the first transaction(started in DocumentFacade) is holding write lock on KEY
  • the second transaction (started in DocumentService) wants to get shared lock on the same KEY to do merge

If you try to follow advice from the previous article and change setting READ_COMMITTED_SNAPSHOT ON it will not help. Your merge will pass but save in DocumentService will deadlock when acquiring write lock to save document.

Solution

I could only come up with a small guideline because the actual solution might vary. As the root cause is insert/update operation on the object with the same id – do not use the same object. For simple cases just removing propagation=REQUIRES_NEW would be enough. For other – make sequential transaction and not suspend first one which holds a lock.

* Source code can be found here

admin