2

There are a lot of questions on this topic. I have searched through stack exchange to find a question which is the same as mine, but haven't found any. Please excuse me if this question has already been asked.

My question is: When using Spring transaction management, what happens when a transactional method with SERIALIZABLE isolation calls another transactional method with REQUIRES_NEW propagation?

As far as I understand, serializable isolation means that any transaction will completely lock the table until it is complete. Meanwhile, a REQUIRES_NEW will make a new transaction and suspend the existing one. This means that since the parent method has not yet completed its transaction, the method that it calls will be instantly deadlocked.

Is my understanding here wrong?

To illustrate, I made an example in Kotlin, which runs without errors in spring, even though it shouldn't, according to my understanding:

open class DummyApplication(val database: Database) {

    @Transactional(isolation = Isolation.SERIALIZABLE)
    open fun doThing() {

        val item = Item("1", "accountId1", "reference1")
        database.saveItemWithoutTransaction(item)

        val item2 = Item("2", "accountId2", "reference2")

        // This call should be instantly deadlocked because it tries to start a new transaction.
        database.saveItemWithTransaction(item2)

    }

}

and the database:

@Repository
class JdbcDatabase(
        private val itemRepository: ItemRepository
) : Database {

    override fun saveItemWithoutTransaction(item: Item) {
        itemRepository.save(item.toItemEntity())
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    override fun saveItemWithTransaction(item: Item) {
        itemRepository.save(item.toItemEntity())
    }
}

The itemRepository is a Spring JPA repository, if that is relevant.

Mdupont
  • 25
  • 3

1 Answers1

1

It depends.

what happens when a transactional method with SERIALIZABLE isolation calls another transactional method with REQUIRES_NEW propagation?

Your question is much more generic than your example. That's why the answer is it depends.

A) If the first transaction has changed anything and the nested transaction attempts to change the same table and the isolation of the nested transaction is READ_COMMITTED or higher (REPEATABLE_READ, SERIALIZABLE), then the nested transaction will be blocked. Normally the blocking (in this case deadlock) happens at the moment when the nested transaction flushes its data.

B) If the first transaction has not changed anything (in the same table) before calling the nested transaction and the isolation of the nested transaction is READ_COMMITTED or SERIALIZABLE, then the nested transaction will not be blocked.

C) If the first transaction has changed anything and the nested transaction attempts to change the same table and the isolation of the nested transaction is READ_UNCOMMITTED, then the nested transaction will not be blocked.

Your example corresponds to the case A. That's why the nested transaction will be blocked and a deadlock will occur.

mentallurg
  • 4,967
  • 5
  • 28
  • 36
  • Thanks for the answer. Sorry it took so long for me to reply, but I don't think that your answer is correct. I tried replicating case "A" from your answer, in a completely fresh spring boot project, using the simplest possible setup, and I still don't encounter any deadlocks – Mdupont Oct 20 '19 at 12:35
  • Here's a link to my i full implementation: https://github.com/MartinDupont/spring-question – Mdupont Oct 20 '19 at 12:36