8

I have a question about how @Transactional annotation alone manages code and transactions execution. Given a correctly setup Spring application and the following code:

@Transactional
public void withdraw(int amount) {
    if(isEnoughFunds(amount)) {
        decreaseFunds(amount);
    }
}

Is it possible for the following scenario to occur:

  • funds == 100; amount == 100
  • thread A enters withdraw / transaction A starts
  • thread A executes isEnoughFunds which evaluates to true
  • thread B enters withdraw / transaction B starts
  • thread B executes isEnoughFunds which evaluates to true
  • thread A executes decreaseFunds / thread A locks db record
  • thread B waits for thread A to commit transaction and release write lock
  • thread A exits withdraw / transaction A commits
  • thread B executes decreaseFunds / thread B locks db record
  • thread B exits withdraw / transaction B commits
  • funds == -100

If this is possible how would you prevent that?

rid
  • 61,078
  • 31
  • 152
  • 193
  • 2
    the annotation just says to wrap this in a transaction. the database isolation level determines how much interleaving occurs. – Nathan Hughes Sep 26 '12 at 15:08

1 Answers1

4

Yes, it is possible, depending on the isolation level. To prevent it, you can explicitly obtain a read lock from the database before calling ifEnoughFunds(). The lock will be released at the end of the transaction. In this scenario, thread B will always wait for thread's A transaction to commit before checking.

Assen Kolov
  • 4,143
  • 2
  • 22
  • 32