ngunha02
ngunha02

Reputation: 1729

JPA Optimistic Lock(READ) explanation

I am reading an article about jpa locking. Link: https://blogs.oracle.com/carolmcdonald/entry/jpa_2_0_concurrency_and

On section about Optimistic Lock(READ), author has code snippet which says transaction 2 get rolled back because of an exception. Base on code snippet, transaction 2 acquires the lock on dep before transaction 1 modifies dep's name. If I understand lecture from school correctly, from the time a lock is acquired (em.lock in the article) until a lock is released (tx2.commit in the article), the action (raise the employee by ten percent in the article) is atomic which means the dep's name cannot be set to "MarketEng" and therefore, the employee get 10 percent raised. After tx2 commits, it releases the lock and dep's name finally sets to "MarketEng". Therefore, no transaction gets rolled back and it is a thread safe.

Do I understand it correctly? If not, please explain it to me what author means by "e1 gets the raise he does not deserve".

Upvotes: 1

Views: 345

Answers (1)

wrschneider
wrschneider

Reputation: 18780

Optimistic locking is kind of a misnomer - there is no actual lock. The behavior you are describing, where em.lock in tx2 would block updates in tx1, is a pessimistic lock. In JPA a pessimistic lock translates to a row-level lock with select .. for update in the DB. An optimistic lock does not.

Optimistic locking instead uses a counter to communicate when an object has been updated, or to verify whether an update has happened. Writes always increment the counter. Reads with LockMode.OPTIMISTIC will check the counter on commit and fail with an exception or rollback if the value has changed.

The point of optimistic locking is that you're "optimistic" that such collisions on the same object instance are rare in practice, so it's better to risk failing on commit than to slow everything down with actual row-level locks and have to worry about deadlock.

In this specific example there are two transactions running concurrently:

  • tx1 changes department name from "Eng" to "MarketEng"
  • tx2 'locks' the department (which is not really a lock, but a promise to check the version counter on the department on tx2.commit)
  • tx2 gives a raise to an employee only if deparment name = "Eng".
    • tx1 changed it already but has not committed yet
    • tx2 still has department name = 'Eng' so employee may get raise
  • tx1 commits change - this increments the counter on the Department
  • tx2 checks the version counter, sees that it changed, and rolls back with

This example is somewhat flawed, though, because the impact from a business or user perspective doesn't match intuition - if tx1 had simply started after tx2 committed, the department name changes immediately after the employee gets a raise. Why does it matter if the name changes a split second earlier?

The analogy I like to use is with version control systems. Most version control systems like svn and git use some form of optimistic locking. Two people are allowed to start work on the same file at the same time but you get an error when you try to commit a file that someone else committed first. Other version control systems like Visual SourceSafe (at least the last time I used it) used pessimistic locking, where you had to check out a file for editing, and the checkout was an exclusive lock - no one else could check out the same file for editing until you committed.

Upvotes: 1

Related Questions