You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@openjpa.apache.org by Philip Aston <ph...@oracle.com> on 2009/02/05 01:11:01 UTC

Is the implementation of lock(LockModeType.READ) correct?

Hello OpenJPA people,

I don't think the OpenJPA implementation of lock(LockModeType.READ) is
correct. Please tell me where I'm mistaken.


JPA 1.0:

    If transaction T1 calls lock(entity, |LockModeType.READ|
    <http://java.sun.com/javaee/5/docs/api/javax/persistence/LockModeType.html#READ>)
    on a versioned object, the entity manager must ensure that neither
    of the following phenomena can occur:

        ...

          *P2 (Non-repeatable read): Transaction T1 reads a row. Another
          transaction T2 then modifies or deletes that row, before T1
          has committed. Both transactions eventually commit
          successfully. * 

    *Lock modes must always prevent the phenomena ... P2.*


An application should be able to read lock a row from which it only
reads (say R1), and be sure that it will be consistent with a row it
both reads and writes (say R2). That is, T1 will commit and R2 will be
updated if and only if R1 has not been changed.

I would expect an implementation to remember the version of R1 at the
time the lock is acquired, then perform an additional version check
(additional SELECT) on R1 after doing the UPDATE to R2, rolling back the
transaction if the R1 version has changed.

This is not how I observe OpenJPA 1.1 or 1.2 behaving. When the
application calls commit() on T1, OpenJPA does the additional check, but
does so before the UPDATE. This presents a window between the SELECT and
the UPDATE where evil T2 can overwrite R1 and commit, before T1 commits
and changes the value of R2. Although T1 committed last, its read lock
has been ignored and R1 is now inconsistent with R2.

Test case attached. This fails pretty quickly (usually sub-second,
occasionally a minute or two) on my dual core machine against a local
Oracle database. A couple of further observations:

   1. There are no problems with LockModeType.WRITE. If you change the
      test case to use that, it will run and run. I contend that
      LockModeType.WRITE should not be required for correct behaviour
      for this test case.
   2. It "works" if I run with non-enhanced classes, since then there is
      no change detection and all rows get written and version checked.


- Phil



Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.
Thanks for the attention so far Pinaki. I'm convinced the read lock
behaviour is wrong, and I've said more than enough about it here if you
come back to it after painting those go-faster stripes.

Regards,

- Phil

Pinaki Poddar (via Nabble) wrote:
> Hi Philip,
> The flag exists and its documentation/naming indicates that it should do
> what you are asking for. And you are right that it is set to true for
> VersionLockManager -- which is the default for optimistic transactions.
> But I see no other part of the code is making use of this flag. More
> investigation is required. But as I am busy with some performance
> enhancement drug (oops, work;) for OpenJPA I am not being able to pay
> enough attention to this thread.
>  
> 
> Thanks Pinaki,
> 
> Are you sure? Setting:
> 
>       <property name="openjpa.LockManager"
> value="version(VersionCheckOnReadLock=true)"/>
> 
> doesn't change the behavior of my case, and AFAICS from the code "true"
> is the default for VersionCheckOnReadLock with the version lock manager.
> 
> - Phil
> 
> 
> Philip,
>>  do the optimistic lock checks for read locks after the INSERT,
> DELETE, and UPDATES for the transaction.
>   This is currently possible with setting VersionCheckOnReadLock
> property on LockManager.
> I think this will ensure that all three Flags become part of commit set
> and thus the transactional consistent extends to consistency of the
> entire dataset that the test case is demanding.
> 
> Pinaki
>  
> 
>     Philip Aston wrote:
>     Hi David,
> 
>     EL does an update for each read lock at commit time, setting its
>     version column to the same value. See my other post - I agree that
>     SELECT FOR UPDATE is not appropriate for read locks, and I instead
>     suggest a third approach; that is to do the optimistic lock checks
>     for read locks after the INSERT, DELETE, and UPDATES for the
>     transaction.
> 
>     - Phil
> 
> 
>         dezzio wrote:
>         Hi Philip,
> 
>         But why does EclipseLink pass the test?
> 
>         Clearly the implementation could use SELECT FOR UPDATE when it
>         checks
>         the READ lock's state prior to commit.  If it did that, then both
>         transactions would not succeed, and in fact, due to the
>         likelihood of
>         deadlock in our example case, they both might fail.
> 
>         These kind of issues do not get sorted by argument.  They are
>         settled by
>         test cases in the TCK.  For this issue, there is no TCK test
>         case, and
>         thus, ipso facto, OpenJPA complies with the spec.  You can still
>         argue
>         that it doesn't comply with the spec in the best way, and you
>         may be
>         right.  I could be persuaded by understanding the behavior of
>         competing
>         implementations.
> 
>         Cheers,
> 
>         David
> 
> 
> 
> 
> ------------------------------------------------------------------------
> This email is a reply to your post @
> http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2334993.html
> You can reply by email or by visting the link above.
> 


-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2350307.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Pinaki Poddar <pp...@apache.org>.
Hi Philip,
The flag exists and its documentation/naming indicates that it should do
what you are asking for. And you are right that it is set to true for
VersionLockManager -- which is the default for optimistic transactions. But
I see no other part of the code is making use of this flag. More
investigation is required. But as I am busy with some performance
enhancement drug (oops, work;) for OpenJPA I am not being able to pay enough
attention to this thread.
 

Philip Aston wrote:
> 
> Thanks Pinaki,
> 
> Are you sure? Setting:
> 
>       <property name="openjpa.LockManager"
> value="version(VersionCheckOnReadLock=true)"/>
> 
> doesn't change the behavior of my case, and AFAICS from the code "true" is
> the default for VersionCheckOnReadLock with the version lock manager.
> 
> - Phil
> 
> 
> Pinaki Poddar wrote:
>> 
>> Philip,
>>>  do the optimistic lock checks for read locks after the INSERT, DELETE,
>>> and UPDATES for the transaction.
>>   This is currently possible with setting VersionCheckOnReadLock property
>> on LockManager.
>> I think this will ensure that all three Flags become part of commit set
>> and thus the transactional consistent extends to consistency of the
>> entire dataset that the test case is demanding.
>> 
>> Pinaki
>>  
>> 
>> Philip Aston wrote:
>>> 
>>> Hi David,
>>> 
>>> EL does an update for each read lock at commit time, setting its version
>>> column to the same value. See my other post - I agree that SELECT FOR
>>> UPDATE is not appropriate for read locks, and I instead suggest a third
>>> approach; that is to do the optimistic lock checks for read locks after
>>> the INSERT, DELETE, and UPDATES for the transaction.
>>> 
>>> - Phil
>>> 
>>> 
>>> 
>>> dezzio wrote:
>>>> 
>>>> Hi Philip,
>>>> 
>>>> But why does EclipseLink pass the test?
>>>> 
>>>> Clearly the implementation could use SELECT FOR UPDATE when it checks 
>>>> the READ lock's state prior to commit.  If it did that, then both 
>>>> transactions would not succeed, and in fact, due to the likelihood of 
>>>> deadlock in our example case, they both might fail.
>>>> 
>>>> These kind of issues do not get sorted by argument.  They are settled
>>>> by 
>>>> test cases in the TCK.  For this issue, there is no TCK test case, and 
>>>> thus, ipso facto, OpenJPA complies with the spec.  You can still argue 
>>>> that it doesn't comply with the spec in the best way, and you may be 
>>>> right.  I could be persuaded by understanding the behavior of competing 
>>>> implementations.
>>>> 
>>>> Cheers,
>>>> 
>>>> David
>>>> 
>>>> 
>>> 
>>> 
>> 
>> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2334993.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.
Thanks Pinaki,

Are you sure? Setting:

      <property name="openjpa.LockManager"
value="version(VersionCheckOnReadLock=true)"/>

doesn't change the behavior of my case, and AFAICS from the code "true" is
the default for VersionCheckOnReadLock with the version lock manager.

- Phil


Pinaki Poddar wrote:
> 
> Philip,
>>  do the optimistic lock checks for read locks after the INSERT, DELETE,
>> and UPDATES for the transaction.
>   This is currently possible with setting VersionCheckOnReadLock property
> on LockManager.
> I think this will ensure that all three Flags become part of commit set
> and thus the transactional consistent extends to consistency of the entire
> dataset that the test case is demanding.
> 
> Pinaki
>  
> 
> Philip Aston wrote:
>> 
>> Hi David,
>> 
>> EL does an update for each read lock at commit time, setting its version
>> column to the same value. See my other post - I agree that SELECT FOR
>> UPDATE is not appropriate for read locks, and I instead suggest a third
>> approach; that is to do the optimistic lock checks for read locks after
>> the INSERT, DELETE, and UPDATES for the transaction.
>> 
>> - Phil
>> 
>> 
>> 
>> dezzio wrote:
>>> 
>>> Hi Philip,
>>> 
>>> But why does EclipseLink pass the test?
>>> 
>>> Clearly the implementation could use SELECT FOR UPDATE when it checks 
>>> the READ lock's state prior to commit.  If it did that, then both 
>>> transactions would not succeed, and in fact, due to the likelihood of 
>>> deadlock in our example case, they both might fail.
>>> 
>>> These kind of issues do not get sorted by argument.  They are settled by 
>>> test cases in the TCK.  For this issue, there is no TCK test case, and 
>>> thus, ipso facto, OpenJPA complies with the spec.  You can still argue 
>>> that it doesn't comply with the spec in the best way, and you may be 
>>> right.  I could be persuaded by understanding the behavior of competing 
>>> implementations.
>>> 
>>> Cheers,
>>> 
>>> David
>>> 
>>> 
>> 
>> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2333637.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Pinaki Poddar <pp...@apache.org>.
Philip,
>  do the optimistic lock checks for read locks after the INSERT, DELETE,
> and UPDATES for the transaction.
  This is currently possible with setting VersionCheckOnReadLock property on
LockManager.
I think this will ensure that all three Flags become part of commit set and
thus the transactional consistent extends to consistency of the entire
dataset that the test case is demanding.

Pinaki
 

Philip Aston wrote:
> 
> Hi David,
> 
> EL does an update for each read lock at commit time, setting its version
> column to the same value. See my other post - I agree that SELECT FOR
> UPDATE is not appropriate for read locks, and I instead suggest a third
> approach; that is to do the optimistic lock checks for read locks after
> the INSERT, DELETE, and UPDATES for the transaction.
> 
> - Phil
> 
> 
> 
> dezzio wrote:
>> 
>> Hi Philip,
>> 
>> But why does EclipseLink pass the test?
>> 
>> Clearly the implementation could use SELECT FOR UPDATE when it checks 
>> the READ lock's state prior to commit.  If it did that, then both 
>> transactions would not succeed, and in fact, due to the likelihood of 
>> deadlock in our example case, they both might fail.
>> 
>> These kind of issues do not get sorted by argument.  They are settled by 
>> test cases in the TCK.  For this issue, there is no TCK test case, and 
>> thus, ipso facto, OpenJPA complies with the spec.  You can still argue 
>> that it doesn't comply with the spec in the best way, and you may be 
>> right.  I could be persuaded by understanding the behavior of competing 
>> implementations.
>> 
>> Cheers,
>> 
>> David
>> 
>> Philip Aston wrote:
>>> Hi David,
>>> 
>>> Thanks for confirming this. So to summarise where we are, we have:
>>> 
>>>  1. A reasonable use case that can fail with some unlucky timing.
>>> 
>>>  2. A technical test case demonstrating the problem that does not rely
>>> on unlucky timing.
>>> 
>>>  3. A disagreement in our readings of whether 1 and 2 are spec.
>>> compliant. Personally, I don't share your reading of the spec. In my
>>> reading, read locks are safe and provide a concrete guarantee that if
>>> locked entity is changed by another transaction, the locking transaction
>>> will not complete.
>>> 
>>> (This is a different QoS compared to a write lock - if a write lock is
>>> obtained and the pc flushed, the transaction knows that it will not fail
>>> due to another transaction updating the locked entity. Read locks are
>>> "more optimistic" and can support higher concurrency if there is minimal
>>> contention - many transactions can hold read locks, only one can hold
>>> right locks.).
>>> 
>>> 
>>> How can I convince you to change your interpretation of the spec? Anyone
>>> else have an opinion?
>>> 
>>> FWIW, EclipseLink passes the test case.
>>> 
>>> - Phil
>>> 
>>> dezzio (via Nabble) wrote:
>>>> Hi Philip,
>>>>
>>>> Let's take a closer look.
>>>>
>>>> We have two bank accounts, Account[1] and Account[2], shared
>>>> jointly by customers Innocent[1] and Innocent[2]. The bank's
>>>> business rule is that no withdrawal can be made the draws
>>>> the combined total of the accounts below zero. This rule is
>>>> enforced in the server side Java application that customer's
>>>> use.
>>>>
>>>> At the start of the banking day, the accounts stand at:
>>>>
>>>>      Account[1] balance 100.
>>>>      Account[2] balance 50.
>>>>
>>>> Innocent[1] wants to draw out all the money, and asks the
>>>> application to take 150 from Account[1]. Innocent[2] also
>>>> wants to draw out all the money, and asks the application to
>>>> take 150 from Account[2]. By itself, either transaction
>>>> would conform to the bank's business rule.
>>>>
>>>> The application implements the withdrawal logic by doing the
>>>> following for each transaction.
>>>>
>>>> For Innocent[1], read Account[1] and Account[2]. Obtain a
>>>> read lock on Account[2]. Refresh Account[2]. Deduct 150 from
>>>> Account[1]. Verify business rule, result, sum of balances =
>>>> 0. Call JPA commit.
>>>>
>>>> For Innocent[2], read Account[1] and Account[2]. Obtain a
>>>> read lock on Account[1]. Refresh Account[1]. Deduct 150 from
>>>> Account[2]. Verify business rule, result, sum of balances =
>>>> 0. Call JPA commit.
>>>>
>>>> Within JPA commit, as seen over the JDBC connections, the
>>>> following time sequence occurs. (Other time sequences can
>>>> yield the same result.)
>>>>
>>>> Innocent[1]: Check version of Account[2]: passes.
>>>>
>>>> Innocent[2]: Check version of Account[1]: passes.
>>>> Innocent[2]: Update balance of Account[2], withdraw 150,
>>>>                  setting balance to -100: does not block.
>>>> Innocent[2]: commit: successful
>>>> Innocent[2]: Receives 150.
>>>>
>>>> Innocent[1]: Update balance of Account[1], withdraw 150,
>>>>                  setting balance to -50: does not block.
>>>> Innocent[1]: commit: successful.
>>>> Innocent[1]: Receives 150.
>>>>
>>>> After the two transactions:
>>>>
>>>> Account[1]: balance -50
>>>> Account[2]: balance -100
>>>>
>>>> Clearly the bank would not be happy. What's a developer to
>>>> do?
>>>>
>>>> I think the developer needs an education about what is meant
>>>> by the JPA spec. What JPA is guaranteeing is that when JPA
>>>> commit is called, the objects with read locks will have
>>>> their versions checked. The objects with write locks will
>>>> have their versions checked and changed. The objects that
>>>> have been modified will have their versions checked, their
>>>> information updated, and their versions changed. Clearly all
>>>> of these rules were enforced in the above example.
>>>>
>>>> If the developer had used write locks, both transactions
>>>> would not have succeeded. In fact, for the above example and
>>>> a similar time sequence, if write locks had been used in
>>>> place of read locks, there would have been deadlock.
>>>>
>>>> Now, if in fact, I'm wrong about my interpretation of the
>>>> JPA spec (and it wouldn't be the first time) then you have a
>>>> case. I'd be curious to know whether other JPA
>>>> implementations pass your elegant test case, and what they
>>>> are doing differently that makes it so.
>>>>
>>>> Also, if I am wrong about my interpretation, then the JPA
>>>> TCK needs a test case that will snag this failure, because
>>>> OpenJPA passes the current JPA TCK.
>>>>
>>>> Cheers,
>>>>
>>>> David
>>>>
>>>> Philip Aston wrote:
>>>>
>>>>> Oh yeah - my bad. Try this one instead:
>>>>>
>>>>> Suppose there are a set of Accounts, and a business rule that says
>>>>> that the net balance must be positive.
>>>>>
>>>>> Innocent wants to draw down on Account 1 as far as possible. It read
>>>>> locks the of Accounts, sums up the value, and and subtracts the
>>>>> positive total from Account 1. Innocent begins its commit, and its
>>>>> read locks are validated.
>>>>>
>>>>> Meanwhile InnocentToo does the same for Account 2, and commits.
>>>>>
>>>>> Innocent updates Account 1 and finishes its commit.
>>>>>
>>>>> The total in account summary is now negative, violating the business
>>>>> rule. If read locks worked as I think they should, Innocent would have
>>>>> received an OptimisticLockException.
>>>>>
>>>>>
>>>>>
>>>>> dezzio wrote:
>>>>>> Hi Philip,
>>>>>>
>>>>>> When two transactions read the same version of AccountSummary, both
>>>>>> cannot successfully update its sum.  Only one will successfully
>>>>>> commit.
>>>>>>
>>>>>> David
>>> 
>>> 
>> 
>> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2330901.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by David Ezzio <de...@apache.org>.
Hi Philip,

Yup, I agree with your argument, and it seems to me the conclusion shows 
that our example (either mine or the original one that you proposed and 
I expanded) doesn't really mirror what a bank would do.  If it dispensed 
175, it would likely zero out all three balances meaning that not one of 
our examples (original without reordering, original with reordering, or 
my latest one) would allow both transactions to succeed.

Since this is the reasonable conclusion, that leads me back to the need 
for a use case that actually requires the functionality that you seek 
(an optimistic read lock that holds for the entire transaction.)

Cheers,

David


Philip Aston wrote:
> No, I don't agree.
> 
> You could re-order these transactions so they didn't overlap with the
> same result. That is, I1 could complete Tx1, before I2 begins Tx2.
> 
> The Bank's rules on handling the joint account is flawed, and it
> essentially lets A&B overdraw the account by its current positive
> balance. For example, I1 could move all of the money to account B, then
> withdraw 175 from account A. Then I2 could withdraw the 175 from account B.
> 
> - Phil
> 
> 
> dezzio (via Nabble) wrote:
>> Hi Philip,
>>
>> Actually, I was not thinking of proof by implementation, but
>> rather a mathematical proof. As it turns out, I think there
>> is a counter-example that disproves the viability of your
>> suggestion.
>>
>> It's an example that is only slightly different from the one
>> that we have discussed.
>>
>> For the same bank, assume that customer Innocent1 has three
>> accounts, A, B, and C, with balances of 100, 50, and 25.
>> Assume that Innocent2 shares account B jointly with Innocent1.
>>
>> Scenario: Innocent1 draws down account A by withdrawing 175.
>> At the same time Innocent2 draws down Account B by
>> withdrawing 50. Either transaction, by itself, satisfies the
>> bank's business rule that the total of a customer's accounts
>> cannot be less than zero.
>>
>> When the backend server executes the separate transactions,
>> the following time sequence of actions occurs on the JDBC
>> connections.
>>
>> Tx1 (for Innocent1)
>>      begin tx
>>      update A by setting balance to -75
>>      verify that B has not been modified (to satisfy read
>>          lock held)
>>
>> Tx2 (for Innocent2)
>>      begin tx
>>      update B by setting balance to 0
>>         (does not block since the verify in Tx1 did not
>>          obtain a pessimistic lock.)
>>      commit tx
>>      dispense 50
>>
>> Tx1
>>      verify that C has not been modified (to satisfy read
>>          lock held)
>>      commit tx
>>      dispense 175
>>
>> Both succeed, and the read lock on B has not held until the
>> end of Tx1.
>>
>> Do you agree that this counter-example disproves your suggestion?
>>
>> Cheers,
>>
>> David
>>
>>
>> Philip Aston wrote:
>>
>>> Hi David,
>>>
>>> That would require studying openjpa internals in great depth, and I'm too
>>> time poor I'm afraid.
>>>
>>>
>>> dezzio wrote:
>>>> Hi Philip,
>>>>
>>>> Your suggestion might work.  Would you like to create the proof?
>>>>
>>>> Cheers,
>>>>
>>>> David
>>>>
>>>>
>>>> Philip Aston wrote:
>>>>> Hi David,
>>>>>
>>>>> EL does an update for each read lock at commit time, setting its
>> version
>>>>> column to the same value. See my other post - I agree that SELECT FOR
>>>>> UPDATE
>>>>> is not appropriate for read locks, and I instead suggest a third
>>>>> approach;
>>>>> that is to do the optimistic lock checks for read locks after the
>> INSERT,
>>>>> DELETE, and UPDATES for the transaction.
>>>>>
>>>>> - Phil
>>
>> ------------------------------------------------------------------------
>> This email is a reply to your post @
>> http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2364648.html
>> You can reply by email or by visting the link above.
>>
> 
> 

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.
No, I don't agree.

You could re-order these transactions so they didn't overlap with the
same result. That is, I1 could complete Tx1, before I2 begins Tx2.

The Bank's rules on handling the joint account is flawed, and it
essentially lets A&B overdraw the account by its current positive
balance. For example, I1 could move all of the money to account B, then
withdraw 175 from account A. Then I2 could withdraw the 175 from account B.

- Phil


dezzio (via Nabble) wrote:
> Hi Philip,
> 
> Actually, I was not thinking of proof by implementation, but
> rather a mathematical proof. As it turns out, I think there
> is a counter-example that disproves the viability of your
> suggestion.
> 
> It's an example that is only slightly different from the one
> that we have discussed.
> 
> For the same bank, assume that customer Innocent1 has three
> accounts, A, B, and C, with balances of 100, 50, and 25.
> Assume that Innocent2 shares account B jointly with Innocent1.
> 
> Scenario: Innocent1 draws down account A by withdrawing 175.
> At the same time Innocent2 draws down Account B by
> withdrawing 50. Either transaction, by itself, satisfies the
> bank's business rule that the total of a customer's accounts
> cannot be less than zero.
> 
> When the backend server executes the separate transactions,
> the following time sequence of actions occurs on the JDBC
> connections.
> 
> Tx1 (for Innocent1)
>      begin tx
>      update A by setting balance to -75
>      verify that B has not been modified (to satisfy read
>          lock held)
> 
> Tx2 (for Innocent2)
>      begin tx
>      update B by setting balance to 0
>         (does not block since the verify in Tx1 did not
>          obtain a pessimistic lock.)
>      commit tx
>      dispense 50
> 
> Tx1
>      verify that C has not been modified (to satisfy read
>          lock held)
>      commit tx
>      dispense 175
> 
> Both succeed, and the read lock on B has not held until the
> end of Tx1.
> 
> Do you agree that this counter-example disproves your suggestion?
> 
> Cheers,
> 
> David
> 
> 
> Philip Aston wrote:
> 
>> Hi David,
>>
>> That would require studying openjpa internals in great depth, and I'm too
>> time poor I'm afraid.
>>
>>
>> dezzio wrote:
>>> Hi Philip,
>>>
>>> Your suggestion might work.  Would you like to create the proof?
>>>
>>> Cheers,
>>>
>>> David
>>>
>>>
>>> Philip Aston wrote:
>>>> Hi David,
>>>>
>>>> EL does an update for each read lock at commit time, setting its
> version
>>>> column to the same value. See my other post - I agree that SELECT FOR
>>>> UPDATE
>>>> is not appropriate for read locks, and I instead suggest a third
>>>> approach;
>>>> that is to do the optimistic lock checks for read locks after the
> INSERT,
>>>> DELETE, and UPDATES for the transaction.
>>>>
>>>> - Phil
>>
> 
> 
> ------------------------------------------------------------------------
> This email is a reply to your post @
> http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2364648.html
> You can reply by email or by visting the link above.
> 


-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2364702.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by David Ezzio <de...@apache.org>.
Hi Philip,

Actually, I was not thinking of proof by implementation, but
rather a mathematical proof. As it turns out, I think there
is a counter-example that disproves the viability of your
suggestion.

It's an example that is only slightly different from the one
that we have discussed.

For the same bank, assume that customer Innocent1 has three
accounts, A, B, and C, with balances of 100, 50, and 25.
Assume that Innocent2 shares account B jointly with Innocent1.

Scenario: Innocent1 draws down account A by withdrawing 175.
At the same time Innocent2 draws down Account B by
withdrawing 50. Either transaction, by itself, satisfies the
bank's business rule that the total of a customer's accounts
cannot be less than zero.

When the backend server executes the separate transactions,
the following time sequence of actions occurs on the JDBC
connections.

Tx1 (for Innocent1)
     begin tx
     update A by setting balance to -75
     verify that B has not been modified (to satisfy read
         lock held)

Tx2 (for Innocent2)
     begin tx
     update B by setting balance to 0
	(does not block since the verify in Tx1 did not
         obtain a pessimistic lock.)
     commit tx
     dispense 50

Tx1
     verify that C has not been modified (to satisfy read
         lock held)
     commit tx
     dispense 175

Both succeed, and the read lock on B has not held until the
end of Tx1.

Do you agree that this counter-example disproves your suggestion?

Cheers,

David


Philip Aston wrote:
> Hi David,
> 
> That would require studying openjpa internals in great depth, and I'm too
> time poor I'm afraid. 
> 
> 
> dezzio wrote:
>> Hi Philip,
>>
>> Your suggestion might work.  Would you like to create the proof?
>>
>> Cheers,
>>
>> David
>>
>>
>> Philip Aston wrote:
>>> Hi David,
>>>
>>> EL does an update for each read lock at commit time, setting its version
>>> column to the same value. See my other post - I agree that SELECT FOR
>>> UPDATE
>>> is not appropriate for read locks, and I instead suggest a third
>>> approach;
>>> that is to do the optimistic lock checks for read locks after the INSERT,
>>> DELETE, and UPDATES for the transaction.
>>>
>>> - Phil
> 

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.
Hi David,

That would require studying openjpa internals in great depth, and I'm too
time poor I'm afraid. 


dezzio wrote:
> 
> Hi Philip,
> 
> Your suggestion might work.  Would you like to create the proof?
> 
> Cheers,
> 
> David
> 
> 
> Philip Aston wrote:
>> Hi David,
>> 
>> EL does an update for each read lock at commit time, setting its version
>> column to the same value. See my other post - I agree that SELECT FOR
>> UPDATE
>> is not appropriate for read locks, and I instead suggest a third
>> approach;
>> that is to do the optimistic lock checks for read locks after the INSERT,
>> DELETE, and UPDATES for the transaction.
>> 
>> - Phil
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2350288.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by David Ezzio <de...@apache.org>.
Hi Philip,

Your suggestion might work.  Would you like to create the proof?

Cheers,

David


Philip Aston wrote:
> Hi David,
> 
> EL does an update for each read lock at commit time, setting its version
> column to the same value. See my other post - I agree that SELECT FOR UPDATE
> is not appropriate for read locks, and I instead suggest a third approach;
> that is to do the optimistic lock checks for read locks after the INSERT,
> DELETE, and UPDATES for the transaction.
> 
> - Phil
> 
> 
> 
> dezzio wrote:
>> Hi Philip,
>>
>> But why does EclipseLink pass the test?
>>
>> Clearly the implementation could use SELECT FOR UPDATE when it checks 
>> the READ lock's state prior to commit.  If it did that, then both 
>> transactions would not succeed, and in fact, due to the likelihood of 
>> deadlock in our example case, they both might fail.
>>
>> These kind of issues do not get sorted by argument.  They are settled by 
>> test cases in the TCK.  For this issue, there is no TCK test case, and 
>> thus, ipso facto, OpenJPA complies with the spec.  You can still argue 
>> that it doesn't comply with the spec in the best way, and you may be 
>> right.  I could be persuaded by understanding the behavior of competing 
>> implementations.
>>
>> Cheers,
>>
>> David
>>
>> Philip Aston wrote:
>>> Hi David,
>>>
>>> Thanks for confirming this. So to summarise where we are, we have:
>>>
>>>  1. A reasonable use case that can fail with some unlucky timing.
>>>
>>>  2. A technical test case demonstrating the problem that does not rely
>>> on unlucky timing.
>>>
>>>  3. A disagreement in our readings of whether 1 and 2 are spec.
>>> compliant. Personally, I don't share your reading of the spec. In my
>>> reading, read locks are safe and provide a concrete guarantee that if
>>> locked entity is changed by another transaction, the locking transaction
>>> will not complete.
>>>
>>> (This is a different QoS compared to a write lock - if a write lock is
>>> obtained and the pc flushed, the transaction knows that it will not fail
>>> due to another transaction updating the locked entity. Read locks are
>>> "more optimistic" and can support higher concurrency if there is minimal
>>> contention - many transactions can hold read locks, only one can hold
>>> right locks.).
>>>
>>>
>>> How can I convince you to change your interpretation of the spec? Anyone
>>> else have an opinion?
>>>
>>> FWIW, EclipseLink passes the test case.
>>>
>>> - Phil
>>>
>>> dezzio (via Nabble) wrote:
>>>> Hi Philip,
>>>>
>>>> Let's take a closer look.
>>>>
>>>> We have two bank accounts, Account[1] and Account[2], shared
>>>> jointly by customers Innocent[1] and Innocent[2]. The bank's
>>>> business rule is that no withdrawal can be made the draws
>>>> the combined total of the accounts below zero. This rule is
>>>> enforced in the server side Java application that customer's
>>>> use.
>>>>
>>>> At the start of the banking day, the accounts stand at:
>>>>
>>>>      Account[1] balance 100.
>>>>      Account[2] balance 50.
>>>>
>>>> Innocent[1] wants to draw out all the money, and asks the
>>>> application to take 150 from Account[1]. Innocent[2] also
>>>> wants to draw out all the money, and asks the application to
>>>> take 150 from Account[2]. By itself, either transaction
>>>> would conform to the bank's business rule.
>>>>
>>>> The application implements the withdrawal logic by doing the
>>>> following for each transaction.
>>>>
>>>> For Innocent[1], read Account[1] and Account[2]. Obtain a
>>>> read lock on Account[2]. Refresh Account[2]. Deduct 150 from
>>>> Account[1]. Verify business rule, result, sum of balances =
>>>> 0. Call JPA commit.
>>>>
>>>> For Innocent[2], read Account[1] and Account[2]. Obtain a
>>>> read lock on Account[1]. Refresh Account[1]. Deduct 150 from
>>>> Account[2]. Verify business rule, result, sum of balances =
>>>> 0. Call JPA commit.
>>>>
>>>> Within JPA commit, as seen over the JDBC connections, the
>>>> following time sequence occurs. (Other time sequences can
>>>> yield the same result.)
>>>>
>>>> Innocent[1]: Check version of Account[2]: passes.
>>>>
>>>> Innocent[2]: Check version of Account[1]: passes.
>>>> Innocent[2]: Update balance of Account[2], withdraw 150,
>>>>                  setting balance to -100: does not block.
>>>> Innocent[2]: commit: successful
>>>> Innocent[2]: Receives 150.
>>>>
>>>> Innocent[1]: Update balance of Account[1], withdraw 150,
>>>>                  setting balance to -50: does not block.
>>>> Innocent[1]: commit: successful.
>>>> Innocent[1]: Receives 150.
>>>>
>>>> After the two transactions:
>>>>
>>>> Account[1]: balance -50
>>>> Account[2]: balance -100
>>>>
>>>> Clearly the bank would not be happy. What's a developer to
>>>> do?
>>>>
>>>> I think the developer needs an education about what is meant
>>>> by the JPA spec. What JPA is guaranteeing is that when JPA
>>>> commit is called, the objects with read locks will have
>>>> their versions checked. The objects with write locks will
>>>> have their versions checked and changed. The objects that
>>>> have been modified will have their versions checked, their
>>>> information updated, and their versions changed. Clearly all
>>>> of these rules were enforced in the above example.
>>>>
>>>> If the developer had used write locks, both transactions
>>>> would not have succeeded. In fact, for the above example and
>>>> a similar time sequence, if write locks had been used in
>>>> place of read locks, there would have been deadlock.
>>>>
>>>> Now, if in fact, I'm wrong about my interpretation of the
>>>> JPA spec (and it wouldn't be the first time) then you have a
>>>> case. I'd be curious to know whether other JPA
>>>> implementations pass your elegant test case, and what they
>>>> are doing differently that makes it so.
>>>>
>>>> Also, if I am wrong about my interpretation, then the JPA
>>>> TCK needs a test case that will snag this failure, because
>>>> OpenJPA passes the current JPA TCK.
>>>>
>>>> Cheers,
>>>>
>>>> David
>>>>
>>>> Philip Aston wrote:
>>>>
>>>>> Oh yeah - my bad. Try this one instead:
>>>>>
>>>>> Suppose there are a set of Accounts, and a business rule that says
>>>>> that the net balance must be positive.
>>>>>
>>>>> Innocent wants to draw down on Account 1 as far as possible. It read
>>>>> locks the of Accounts, sums up the value, and and subtracts the
>>>>> positive total from Account 1. Innocent begins its commit, and its
>>>>> read locks are validated.
>>>>>
>>>>> Meanwhile InnocentToo does the same for Account 2, and commits.
>>>>>
>>>>> Innocent updates Account 1 and finishes its commit.
>>>>>
>>>>> The total in account summary is now negative, violating the business
>>>>> rule. If read locks worked as I think they should, Innocent would have
>>>>> received an OptimisticLockException.
>>>>>
>>>>>
>>>>>
>>>>> dezzio wrote:
>>>>>> Hi Philip,
>>>>>>
>>>>>> When two transactions read the same version of AccountSummary, both
>>>>>> cannot successfully update its sum.  Only one will successfully
>>>>>> commit.
>>>>>>
>>>>>> David
>>>
>>
> 

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.
Hi David,

EL does an update for each read lock at commit time, setting its version
column to the same value. See my other post - I agree that SELECT FOR UPDATE
is not appropriate for read locks, and I instead suggest a third approach;
that is to do the optimistic lock checks for read locks after the INSERT,
DELETE, and UPDATES for the transaction.

- Phil



dezzio wrote:
> 
> Hi Philip,
> 
> But why does EclipseLink pass the test?
> 
> Clearly the implementation could use SELECT FOR UPDATE when it checks 
> the READ lock's state prior to commit.  If it did that, then both 
> transactions would not succeed, and in fact, due to the likelihood of 
> deadlock in our example case, they both might fail.
> 
> These kind of issues do not get sorted by argument.  They are settled by 
> test cases in the TCK.  For this issue, there is no TCK test case, and 
> thus, ipso facto, OpenJPA complies with the spec.  You can still argue 
> that it doesn't comply with the spec in the best way, and you may be 
> right.  I could be persuaded by understanding the behavior of competing 
> implementations.
> 
> Cheers,
> 
> David
> 
> Philip Aston wrote:
>> Hi David,
>> 
>> Thanks for confirming this. So to summarise where we are, we have:
>> 
>>  1. A reasonable use case that can fail with some unlucky timing.
>> 
>>  2. A technical test case demonstrating the problem that does not rely
>> on unlucky timing.
>> 
>>  3. A disagreement in our readings of whether 1 and 2 are spec.
>> compliant. Personally, I don't share your reading of the spec. In my
>> reading, read locks are safe and provide a concrete guarantee that if
>> locked entity is changed by another transaction, the locking transaction
>> will not complete.
>> 
>> (This is a different QoS compared to a write lock - if a write lock is
>> obtained and the pc flushed, the transaction knows that it will not fail
>> due to another transaction updating the locked entity. Read locks are
>> "more optimistic" and can support higher concurrency if there is minimal
>> contention - many transactions can hold read locks, only one can hold
>> right locks.).
>> 
>> 
>> How can I convince you to change your interpretation of the spec? Anyone
>> else have an opinion?
>> 
>> FWIW, EclipseLink passes the test case.
>> 
>> - Phil
>> 
>> dezzio (via Nabble) wrote:
>>> Hi Philip,
>>>
>>> Let's take a closer look.
>>>
>>> We have two bank accounts, Account[1] and Account[2], shared
>>> jointly by customers Innocent[1] and Innocent[2]. The bank's
>>> business rule is that no withdrawal can be made the draws
>>> the combined total of the accounts below zero. This rule is
>>> enforced in the server side Java application that customer's
>>> use.
>>>
>>> At the start of the banking day, the accounts stand at:
>>>
>>>      Account[1] balance 100.
>>>      Account[2] balance 50.
>>>
>>> Innocent[1] wants to draw out all the money, and asks the
>>> application to take 150 from Account[1]. Innocent[2] also
>>> wants to draw out all the money, and asks the application to
>>> take 150 from Account[2]. By itself, either transaction
>>> would conform to the bank's business rule.
>>>
>>> The application implements the withdrawal logic by doing the
>>> following for each transaction.
>>>
>>> For Innocent[1], read Account[1] and Account[2]. Obtain a
>>> read lock on Account[2]. Refresh Account[2]. Deduct 150 from
>>> Account[1]. Verify business rule, result, sum of balances =
>>> 0. Call JPA commit.
>>>
>>> For Innocent[2], read Account[1] and Account[2]. Obtain a
>>> read lock on Account[1]. Refresh Account[1]. Deduct 150 from
>>> Account[2]. Verify business rule, result, sum of balances =
>>> 0. Call JPA commit.
>>>
>>> Within JPA commit, as seen over the JDBC connections, the
>>> following time sequence occurs. (Other time sequences can
>>> yield the same result.)
>>>
>>> Innocent[1]: Check version of Account[2]: passes.
>>>
>>> Innocent[2]: Check version of Account[1]: passes.
>>> Innocent[2]: Update balance of Account[2], withdraw 150,
>>>                  setting balance to -100: does not block.
>>> Innocent[2]: commit: successful
>>> Innocent[2]: Receives 150.
>>>
>>> Innocent[1]: Update balance of Account[1], withdraw 150,
>>>                  setting balance to -50: does not block.
>>> Innocent[1]: commit: successful.
>>> Innocent[1]: Receives 150.
>>>
>>> After the two transactions:
>>>
>>> Account[1]: balance -50
>>> Account[2]: balance -100
>>>
>>> Clearly the bank would not be happy. What's a developer to
>>> do?
>>>
>>> I think the developer needs an education about what is meant
>>> by the JPA spec. What JPA is guaranteeing is that when JPA
>>> commit is called, the objects with read locks will have
>>> their versions checked. The objects with write locks will
>>> have their versions checked and changed. The objects that
>>> have been modified will have their versions checked, their
>>> information updated, and their versions changed. Clearly all
>>> of these rules were enforced in the above example.
>>>
>>> If the developer had used write locks, both transactions
>>> would not have succeeded. In fact, for the above example and
>>> a similar time sequence, if write locks had been used in
>>> place of read locks, there would have been deadlock.
>>>
>>> Now, if in fact, I'm wrong about my interpretation of the
>>> JPA spec (and it wouldn't be the first time) then you have a
>>> case. I'd be curious to know whether other JPA
>>> implementations pass your elegant test case, and what they
>>> are doing differently that makes it so.
>>>
>>> Also, if I am wrong about my interpretation, then the JPA
>>> TCK needs a test case that will snag this failure, because
>>> OpenJPA passes the current JPA TCK.
>>>
>>> Cheers,
>>>
>>> David
>>>
>>> Philip Aston wrote:
>>>
>>>> Oh yeah - my bad. Try this one instead:
>>>>
>>>> Suppose there are a set of Accounts, and a business rule that says
>>>> that the net balance must be positive.
>>>>
>>>> Innocent wants to draw down on Account 1 as far as possible. It read
>>>> locks the of Accounts, sums up the value, and and subtracts the
>>>> positive total from Account 1. Innocent begins its commit, and its
>>>> read locks are validated.
>>>>
>>>> Meanwhile InnocentToo does the same for Account 2, and commits.
>>>>
>>>> Innocent updates Account 1 and finishes its commit.
>>>>
>>>> The total in account summary is now negative, violating the business
>>>> rule. If read locks worked as I think they should, Innocent would have
>>>> received an OptimisticLockException.
>>>>
>>>>
>>>>
>>>> dezzio wrote:
>>>>> Hi Philip,
>>>>>
>>>>> When two transactions read the same version of AccountSummary, both
>>>>> cannot successfully update its sum.  Only one will successfully
>>>>> commit.
>>>>>
>>>>> David
>> 
>> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2329205.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by David Ezzio <de...@apache.org>.
Hi Philip,

But why does EclipseLink pass the test?

Clearly the implementation could use SELECT FOR UPDATE when it checks 
the READ lock's state prior to commit.  If it did that, then both 
transactions would not succeed, and in fact, due to the likelihood of 
deadlock in our example case, they both might fail.

These kind of issues do not get sorted by argument.  They are settled by 
test cases in the TCK.  For this issue, there is no TCK test case, and 
thus, ipso facto, OpenJPA complies with the spec.  You can still argue 
that it doesn't comply with the spec in the best way, and you may be 
right.  I could be persuaded by understanding the behavior of competing 
implementations.

Cheers,

David

Philip Aston wrote:
> Hi David,
> 
> Thanks for confirming this. So to summarise where we are, we have:
> 
>  1. A reasonable use case that can fail with some unlucky timing.
> 
>  2. A technical test case demonstrating the problem that does not rely
> on unlucky timing.
> 
>  3. A disagreement in our readings of whether 1 and 2 are spec.
> compliant. Personally, I don't share your reading of the spec. In my
> reading, read locks are safe and provide a concrete guarantee that if
> locked entity is changed by another transaction, the locking transaction
> will not complete.
> 
> (This is a different QoS compared to a write lock - if a write lock is
> obtained and the pc flushed, the transaction knows that it will not fail
> due to another transaction updating the locked entity. Read locks are
> "more optimistic" and can support higher concurrency if there is minimal
> contention - many transactions can hold read locks, only one can hold
> right locks.).
> 
> 
> How can I convince you to change your interpretation of the spec? Anyone
> else have an opinion?
> 
> FWIW, EclipseLink passes the test case.
> 
> - Phil
> 
> dezzio (via Nabble) wrote:
>> Hi Philip,
>>
>> Let's take a closer look.
>>
>> We have two bank accounts, Account[1] and Account[2], shared
>> jointly by customers Innocent[1] and Innocent[2]. The bank's
>> business rule is that no withdrawal can be made the draws
>> the combined total of the accounts below zero. This rule is
>> enforced in the server side Java application that customer's
>> use.
>>
>> At the start of the banking day, the accounts stand at:
>>
>>      Account[1] balance 100.
>>      Account[2] balance 50.
>>
>> Innocent[1] wants to draw out all the money, and asks the
>> application to take 150 from Account[1]. Innocent[2] also
>> wants to draw out all the money, and asks the application to
>> take 150 from Account[2]. By itself, either transaction
>> would conform to the bank's business rule.
>>
>> The application implements the withdrawal logic by doing the
>> following for each transaction.
>>
>> For Innocent[1], read Account[1] and Account[2]. Obtain a
>> read lock on Account[2]. Refresh Account[2]. Deduct 150 from
>> Account[1]. Verify business rule, result, sum of balances =
>> 0. Call JPA commit.
>>
>> For Innocent[2], read Account[1] and Account[2]. Obtain a
>> read lock on Account[1]. Refresh Account[1]. Deduct 150 from
>> Account[2]. Verify business rule, result, sum of balances =
>> 0. Call JPA commit.
>>
>> Within JPA commit, as seen over the JDBC connections, the
>> following time sequence occurs. (Other time sequences can
>> yield the same result.)
>>
>> Innocent[1]: Check version of Account[2]: passes.
>>
>> Innocent[2]: Check version of Account[1]: passes.
>> Innocent[2]: Update balance of Account[2], withdraw 150,
>>                  setting balance to -100: does not block.
>> Innocent[2]: commit: successful
>> Innocent[2]: Receives 150.
>>
>> Innocent[1]: Update balance of Account[1], withdraw 150,
>>                  setting balance to -50: does not block.
>> Innocent[1]: commit: successful.
>> Innocent[1]: Receives 150.
>>
>> After the two transactions:
>>
>> Account[1]: balance -50
>> Account[2]: balance -100
>>
>> Clearly the bank would not be happy. What's a developer to
>> do?
>>
>> I think the developer needs an education about what is meant
>> by the JPA spec. What JPA is guaranteeing is that when JPA
>> commit is called, the objects with read locks will have
>> their versions checked. The objects with write locks will
>> have their versions checked and changed. The objects that
>> have been modified will have their versions checked, their
>> information updated, and their versions changed. Clearly all
>> of these rules were enforced in the above example.
>>
>> If the developer had used write locks, both transactions
>> would not have succeeded. In fact, for the above example and
>> a similar time sequence, if write locks had been used in
>> place of read locks, there would have been deadlock.
>>
>> Now, if in fact, I'm wrong about my interpretation of the
>> JPA spec (and it wouldn't be the first time) then you have a
>> case. I'd be curious to know whether other JPA
>> implementations pass your elegant test case, and what they
>> are doing differently that makes it so.
>>
>> Also, if I am wrong about my interpretation, then the JPA
>> TCK needs a test case that will snag this failure, because
>> OpenJPA passes the current JPA TCK.
>>
>> Cheers,
>>
>> David
>>
>> Philip Aston wrote:
>>
>>> Oh yeah - my bad. Try this one instead:
>>>
>>> Suppose there are a set of Accounts, and a business rule that says
>>> that the net balance must be positive.
>>>
>>> Innocent wants to draw down on Account 1 as far as possible. It read
>>> locks the of Accounts, sums up the value, and and subtracts the
>>> positive total from Account 1. Innocent begins its commit, and its
>>> read locks are validated.
>>>
>>> Meanwhile InnocentToo does the same for Account 2, and commits.
>>>
>>> Innocent updates Account 1 and finishes its commit.
>>>
>>> The total in account summary is now negative, violating the business
>>> rule. If read locks worked as I think they should, Innocent would have
>>> received an OptimisticLockException.
>>>
>>>
>>>
>>> dezzio wrote:
>>>> Hi Philip,
>>>>
>>>> When two transactions read the same version of AccountSummary, both
>>>> cannot successfully update its sum.  Only one will successfully commit.
>>>>
>>>> David
> 
> 

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.
Pinaki - I agree with David that you're not addressing the purpose of read
locks.

Here's my opinion:

1. Any locking mechanism that is prone to race conditions (no matter
how rare) is useless.

2. Given this, for read locks to add distinct value to that provided
by write locks, I think you have to interpret the specification as I
have above. Of course, the specification does allow an implementation
to wimp out and use write locks when read locks are requested.
Personally, I don't see the ambiguity in the specification, but we're
only going to resolve our disagreements about its intention by
conferring with the specification authors.

3. Implementing read locks using SELECT FOR UPDATE is not appropriate
due to the risk of deadlock that David identifies. A better approach
would be to re-order the optimistic lock checks that OpenJPA currently
does to be done after any INSERT or UPDATE statements for the
transaction. This fulfils the goal of ensuring that the effects of
the transaction were calculated based on a consistent view. Or you
could do what EclipseLink does - issue an UPDATE to the read locked
row to set the version column to itself - I prefer reordering locks.

4. Read locks *do* provide different, valuable behaviour compared to
write locks. Firstly, with write locks you have to flush in order for
them to become effective (--performance); secondly only one
transaction can hold a write lock - the rest are blocked. Many
transactions can hold read locks, so the difference can be seen as
"read locks are optimistic, write locks are pessimistic".

- Phil


dezzio wrote:
> 
> Hi Pinaki,
> 
> I think the issue is whether a LockModeType.READ holds for the entire 
> transaction (the subject tx), from the moment that the lock is obtained 
> until the moment when the transaction has successfully committed.  By 
> "hold", I mean that either another tx cannot successfully commit a 
> change to an object that the subject tx has locked until the subject's 
> tx ends, or that the subject tx will fail if another transaction has 
> successfully committed a change prior to the subject tx's end.
> 
> In the case of the OpenJPA implementation and the time sequence under 
> discussion, the lock would hold if the implementation obtained a 
> database row level lock (SELECT FOR UPDATE) when it checked the locked 
> object's version.
> 
> A peripheral question is whether the spec requires that a read lock hold 
> for the entire tx (as defined above.)  If it does, it certainly doesn't 
> test for that compliance, and OpenJPA is not in compliance.
> 
> A clear downside to locking the row when checking the version for a read 
> lock is that two or more transactions with no incompatible changes but a 
> variety of read locks for unchanged objects could end up in deadlock.
> 
> My take is that the tradeoff is worthwhile, especially since 
> LockModeType.WRITE will give the consistency desired.  SFAIK, there are 
> not a lot of implementation options to make a read lock hold as defined. 
>   However, the expert group, currently discussing lock mode types, 
> should make clear exactly what can be expected for all lock modes, and 
> have TCK tests to ensure compliance.  Intentional ambiguity in a spec is 
> like infidelity in a marriage: it's a knife in the heart of reasonable 
> expectations.
> 
> Cheers,
> 
> David
> 
> 
> 
> Pinaki Poddar wrote:
>> The expressed view relates to Philip's use case by his own observation:
>>> It "works" if I run with non-enhanced classes, since then there is no
>>> change detection and all rows get > written and version checked.
>> 
>> The point is the way OpenJPA decides what is flushed in a commit is not
>> the
>> entire set {A,B,C} but only {B,C} that are dirty. So 'transaction
>> consistency' is ensured but not 'database consistency' because another
>> transaction may have committed {A,B}. And that breaks the parity
>> invariance
>> of the entire set {A,B,C}. 
>> 
>> 
>> 
>> dezzio wrote:
>>> Hi Pinaki,
>>>
>>> Actually, much as I like your concepts, I don't yet see how they 
>>> illuminate the issue.
>>>
>>> Cheers,
>>>
>>> David
>>>
>>>
>>> Pinaki Poddar wrote:
>>>> Hi David & Philip,
>>>>    I have not had the time to pay attention to the use case it deserves
>>>> --
>>>> but reading the case brings up certain aspects that I will like to
>>>> share
>>>> with your experience.
>>>>  
>>>>   This interesting use case can fail and is failing. But the issue it
>>>> reveals goes beyond locking semantics. 
>>>>
>>>> Behavior of lock is described at datum level -- the levels of warranty
>>>> for 
>>>> shared access/mutation to a *single datum* in a consistent way. 
>>>> Transaction goes to the next stage and describes the level of warranty
>>>> of
>>>> a
>>>> set of datum as an atomic 'unit of work'. 
>>>> But this test case demands a even higher level of warranty --
>>>> consistency
>>>> or
>>>> invariance of a set-based property (in this case the odd-even parity of
>>>> 3
>>>> instances), which is neither the property of an individual datum nor
>>>> the
>>>> property of a unit of work.
>>>>
>>>> Of course, optimism of optimistic transaction model results in weaker
>>>> warranty of set-based invariance. To ensure set-based property
>>>> invariance, a
>>>> transaction must commit all 3 instances (with consistent odd-even
>>>> parity)
>>>> as
>>>> a unit of work, but what it does is that it reads {A,B,C} and writes
>>>> only
>>>> {B, C}.  
>>>>
>>>> I will refrain from describing what flags of which OpenJPA
>>>> configuration
>>>> property can be tweaked to get there because let me first hear your
>>>> comments
>>>> on the expressed views in this posts.  
>>>>  
>>>>
>>>> Philip Aston wrote:
>>>>> Hi David,
>>>>>
>>>>> Thanks for confirming this. So to summarise where we are, we have:
>>>>>
>>>>>  1. A reasonable use case that can fail with some unlucky timing.
>>>>>
>>>>>  2. A technical test case demonstrating the problem that does not rely
>>>>> on unlucky timing.
>>>>>
>>>>>  3. A disagreement in our readings of whether 1 and 2 are spec.
>>>>> compliant. Personally, I don't share your reading of the spec. In my
>>>>> reading, read locks are safe and provide a concrete guarantee that if
>>>>> locked entity is changed by another transaction, the locking
>>>>> transaction
>>>>> will not complete.
>>>>>
>>>>> (This is a different QoS compared to a write lock - if a write lock is
>>>>> obtained and the pc flushed, the transaction knows that it will not
>>>>> fail
>>>>> due to another transaction updating the locked entity. Read locks are
>>>>> "more optimistic" and can support higher concurrency if there is
>>>>> minimal
>>>>> contention - many transactions can hold read locks, only one can hold
>>>>> right locks.).
>>>>>
>>>>>
>>>>> How can I convince you to change your interpretation of the spec?
>>>>> Anyone
>>>>> else have an opinion?
>>>>>
>>>>> FWIW, EclipseLink passes the test case.
>>>>>
>>>>> - Phil
>>>>>
>>>>> dezzio (via Nabble) wrote:
>>>>>> Hi Philip,
>>>>>>
>>>>>> Let's take a closer look.
>>>>>>
>>>>>> We have two bank accounts, Account[1] and Account[2], shared
>>>>>> jointly by customers Innocent[1] and Innocent[2]. The bank's
>>>>>> business rule is that no withdrawal can be made the draws
>>>>>> the combined total of the accounts below zero. This rule is
>>>>>> enforced in the server side Java application that customer's
>>>>>> use.
>>>>>>
>>>>>> At the start of the banking day, the accounts stand at:
>>>>>>
>>>>>>      Account[1] balance 100.
>>>>>>      Account[2] balance 50.
>>>>>>
>>>>>> Innocent[1] wants to draw out all the money, and asks the
>>>>>> application to take 150 from Account[1]. Innocent[2] also
>>>>>> wants to draw out all the money, and asks the application to
>>>>>> take 150 from Account[2]. By itself, either transaction
>>>>>> would conform to the bank's business rule.
>>>>>>
>>>>>> The application implements the withdrawal logic by doing the
>>>>>> following for each transaction.
>>>>>>
>>>>>> For Innocent[1], read Account[1] and Account[2]. Obtain a
>>>>>> read lock on Account[2]. Refresh Account[2]. Deduct 150 from
>>>>>> Account[1]. Verify business rule, result, sum of balances =
>>>>>> 0. Call JPA commit.
>>>>>>
>>>>>> For Innocent[2], read Account[1] and Account[2]. Obtain a
>>>>>> read lock on Account[1]. Refresh Account[1]. Deduct 150 from
>>>>>> Account[2]. Verify business rule, result, sum of balances =
>>>>>> 0. Call JPA commit.
>>>>>>
>>>>>> Within JPA commit, as seen over the JDBC connections, the
>>>>>> following time sequence occurs. (Other time sequences can
>>>>>> yield the same result.)
>>>>>>
>>>>>> Innocent[1]: Check version of Account[2]: passes.
>>>>>>
>>>>>> Innocent[2]: Check version of Account[1]: passes.
>>>>>> Innocent[2]: Update balance of Account[2], withdraw 150,
>>>>>>                  setting balance to -100: does not block.
>>>>>> Innocent[2]: commit: successful
>>>>>> Innocent[2]: Receives 150.
>>>>>>
>>>>>> Innocent[1]: Update balance of Account[1], withdraw 150,
>>>>>>                  setting balance to -50: does not block.
>>>>>> Innocent[1]: commit: successful.
>>>>>> Innocent[1]: Receives 150.
>>>>>>
>>>>>> After the two transactions:
>>>>>>
>>>>>> Account[1]: balance -50
>>>>>> Account[2]: balance -100
>>>>>>
>>>>>> Clearly the bank would not be happy. What's a developer to
>>>>>> do?
>>>>>>
>>>>>> I think the developer needs an education about what is meant
>>>>>> by the JPA spec. What JPA is guaranteeing is that when JPA
>>>>>> commit is called, the objects with read locks will have
>>>>>> their versions checked. The objects with write locks will
>>>>>> have their versions checked and changed. The objects that
>>>>>> have been modified will have their versions checked, their
>>>>>> information updated, and their versions changed. Clearly all
>>>>>> of these rules were enforced in the above example.
>>>>>>
>>>>>> If the developer had used write locks, both transactions
>>>>>> would not have succeeded. In fact, for the above example and
>>>>>> a similar time sequence, if write locks had been used in
>>>>>> place of read locks, there would have been deadlock.
>>>>>>
>>>>>> Now, if in fact, I'm wrong about my interpretation of the
>>>>>> JPA spec (and it wouldn't be the first time) then you have a
>>>>>> case. I'd be curious to know whether other JPA
>>>>>> implementations pass your elegant test case, and what they
>>>>>> are doing differently that makes it so.
>>>>>>
>>>>>> Also, if I am wrong about my interpretation, then the JPA
>>>>>> TCK needs a test case that will snag this failure, because
>>>>>> OpenJPA passes the current JPA TCK.
>>>>>>
>>>>>> Cheers,
>>>>>>
>>>>>> David
>>>>>>
>>>>>> Philip Aston wrote:
>>>>>>
>>>>>>> Oh yeah - my bad. Try this one instead:
>>>>>>>
>>>>>>> Suppose there are a set of Accounts, and a business rule that says
>>>>>>> that the net balance must be positive.
>>>>>>>
>>>>>>> Innocent wants to draw down on Account 1 as far as possible. It read
>>>>>>> locks the of Accounts, sums up the value, and and subtracts the
>>>>>>> positive total from Account 1. Innocent begins its commit, and its
>>>>>>> read locks are validated.
>>>>>>>
>>>>>>> Meanwhile InnocentToo does the same for Account 2, and commits.
>>>>>>>
>>>>>>> Innocent updates Account 1 and finishes its commit.
>>>>>>>
>>>>>>> The total in account summary is now negative, violating the business
>>>>>>> rule. If read locks worked as I think they should, Innocent would
>>>>>>> have
>>>>>>> received an OptimisticLockException.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> dezzio wrote:
>>>>>>>> Hi Philip,
>>>>>>>>
>>>>>>>> When two transactions read the same version of AccountSummary, both
>>>>>>>> cannot successfully update its sum.  Only one will successfully
>>>>>>>> commit.
>>>>>>>>
>>>>>>>> David
>>>>>
>>>
>> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2329197.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by David Ezzio <de...@apache.org>.
Hi Pinaki,

I think the issue is whether a LockModeType.READ holds for the entire 
transaction (the subject tx), from the moment that the lock is obtained 
until the moment when the transaction has successfully committed.  By 
"hold", I mean that either another tx cannot successfully commit a 
change to an object that the subject tx has locked until the subject's 
tx ends, or that the subject tx will fail if another transaction has 
successfully committed a change prior to the subject tx's end.

In the case of the OpenJPA implementation and the time sequence under 
discussion, the lock would hold if the implementation obtained a 
database row level lock (SELECT FOR UPDATE) when it checked the locked 
object's version.

A peripheral question is whether the spec requires that a read lock hold 
for the entire tx (as defined above.)  If it does, it certainly doesn't 
test for that compliance, and OpenJPA is not in compliance.

A clear downside to locking the row when checking the version for a read 
lock is that two or more transactions with no incompatible changes but a 
variety of read locks for unchanged objects could end up in deadlock.

My take is that the tradeoff is worthwhile, especially since 
LockModeType.WRITE will give the consistency desired.  SFAIK, there are 
not a lot of implementation options to make a read lock hold as defined. 
  However, the expert group, currently discussing lock mode types, 
should make clear exactly what can be expected for all lock modes, and 
have TCK tests to ensure compliance.  Intentional ambiguity in a spec is 
like infidelity in a marriage: it's a knife in the heart of reasonable 
expectations.

Cheers,

David



Pinaki Poddar wrote:
> The expressed view relates to Philip's use case by his own observation:
>> It "works" if I run with non-enhanced classes, since then there is no
>> change detection and all rows get > written and version checked.
> 
> The point is the way OpenJPA decides what is flushed in a commit is not the
> entire set {A,B,C} but only {B,C} that are dirty. So 'transaction
> consistency' is ensured but not 'database consistency' because another
> transaction may have committed {A,B}. And that breaks the parity invariance
> of the entire set {A,B,C}. 
> 
> 
> 
> dezzio wrote:
>> Hi Pinaki,
>>
>> Actually, much as I like your concepts, I don't yet see how they 
>> illuminate the issue.
>>
>> Cheers,
>>
>> David
>>
>>
>> Pinaki Poddar wrote:
>>> Hi David & Philip,
>>>    I have not had the time to pay attention to the use case it deserves
>>> --
>>> but reading the case brings up certain aspects that I will like to share
>>> with your experience.
>>>  
>>>   This interesting use case can fail and is failing. But the issue it
>>> reveals goes beyond locking semantics. 
>>>
>>> Behavior of lock is described at datum level -- the levels of warranty
>>> for 
>>> shared access/mutation to a *single datum* in a consistent way. 
>>> Transaction goes to the next stage and describes the level of warranty of
>>> a
>>> set of datum as an atomic 'unit of work'. 
>>> But this test case demands a even higher level of warranty -- consistency
>>> or
>>> invariance of a set-based property (in this case the odd-even parity of 3
>>> instances), which is neither the property of an individual datum nor the
>>> property of a unit of work.
>>>
>>> Of course, optimism of optimistic transaction model results in weaker
>>> warranty of set-based invariance. To ensure set-based property
>>> invariance, a
>>> transaction must commit all 3 instances (with consistent odd-even parity)
>>> as
>>> a unit of work, but what it does is that it reads {A,B,C} and writes only
>>> {B, C}.  
>>>
>>> I will refrain from describing what flags of which OpenJPA configuration
>>> property can be tweaked to get there because let me first hear your
>>> comments
>>> on the expressed views in this posts.  
>>>  
>>>
>>> Philip Aston wrote:
>>>> Hi David,
>>>>
>>>> Thanks for confirming this. So to summarise where we are, we have:
>>>>
>>>>  1. A reasonable use case that can fail with some unlucky timing.
>>>>
>>>>  2. A technical test case demonstrating the problem that does not rely
>>>> on unlucky timing.
>>>>
>>>>  3. A disagreement in our readings of whether 1 and 2 are spec.
>>>> compliant. Personally, I don't share your reading of the spec. In my
>>>> reading, read locks are safe and provide a concrete guarantee that if
>>>> locked entity is changed by another transaction, the locking transaction
>>>> will not complete.
>>>>
>>>> (This is a different QoS compared to a write lock - if a write lock is
>>>> obtained and the pc flushed, the transaction knows that it will not fail
>>>> due to another transaction updating the locked entity. Read locks are
>>>> "more optimistic" and can support higher concurrency if there is minimal
>>>> contention - many transactions can hold read locks, only one can hold
>>>> right locks.).
>>>>
>>>>
>>>> How can I convince you to change your interpretation of the spec? Anyone
>>>> else have an opinion?
>>>>
>>>> FWIW, EclipseLink passes the test case.
>>>>
>>>> - Phil
>>>>
>>>> dezzio (via Nabble) wrote:
>>>>> Hi Philip,
>>>>>
>>>>> Let's take a closer look.
>>>>>
>>>>> We have two bank accounts, Account[1] and Account[2], shared
>>>>> jointly by customers Innocent[1] and Innocent[2]. The bank's
>>>>> business rule is that no withdrawal can be made the draws
>>>>> the combined total of the accounts below zero. This rule is
>>>>> enforced in the server side Java application that customer's
>>>>> use.
>>>>>
>>>>> At the start of the banking day, the accounts stand at:
>>>>>
>>>>>      Account[1] balance 100.
>>>>>      Account[2] balance 50.
>>>>>
>>>>> Innocent[1] wants to draw out all the money, and asks the
>>>>> application to take 150 from Account[1]. Innocent[2] also
>>>>> wants to draw out all the money, and asks the application to
>>>>> take 150 from Account[2]. By itself, either transaction
>>>>> would conform to the bank's business rule.
>>>>>
>>>>> The application implements the withdrawal logic by doing the
>>>>> following for each transaction.
>>>>>
>>>>> For Innocent[1], read Account[1] and Account[2]. Obtain a
>>>>> read lock on Account[2]. Refresh Account[2]. Deduct 150 from
>>>>> Account[1]. Verify business rule, result, sum of balances =
>>>>> 0. Call JPA commit.
>>>>>
>>>>> For Innocent[2], read Account[1] and Account[2]. Obtain a
>>>>> read lock on Account[1]. Refresh Account[1]. Deduct 150 from
>>>>> Account[2]. Verify business rule, result, sum of balances =
>>>>> 0. Call JPA commit.
>>>>>
>>>>> Within JPA commit, as seen over the JDBC connections, the
>>>>> following time sequence occurs. (Other time sequences can
>>>>> yield the same result.)
>>>>>
>>>>> Innocent[1]: Check version of Account[2]: passes.
>>>>>
>>>>> Innocent[2]: Check version of Account[1]: passes.
>>>>> Innocent[2]: Update balance of Account[2], withdraw 150,
>>>>>                  setting balance to -100: does not block.
>>>>> Innocent[2]: commit: successful
>>>>> Innocent[2]: Receives 150.
>>>>>
>>>>> Innocent[1]: Update balance of Account[1], withdraw 150,
>>>>>                  setting balance to -50: does not block.
>>>>> Innocent[1]: commit: successful.
>>>>> Innocent[1]: Receives 150.
>>>>>
>>>>> After the two transactions:
>>>>>
>>>>> Account[1]: balance -50
>>>>> Account[2]: balance -100
>>>>>
>>>>> Clearly the bank would not be happy. What's a developer to
>>>>> do?
>>>>>
>>>>> I think the developer needs an education about what is meant
>>>>> by the JPA spec. What JPA is guaranteeing is that when JPA
>>>>> commit is called, the objects with read locks will have
>>>>> their versions checked. The objects with write locks will
>>>>> have their versions checked and changed. The objects that
>>>>> have been modified will have their versions checked, their
>>>>> information updated, and their versions changed. Clearly all
>>>>> of these rules were enforced in the above example.
>>>>>
>>>>> If the developer had used write locks, both transactions
>>>>> would not have succeeded. In fact, for the above example and
>>>>> a similar time sequence, if write locks had been used in
>>>>> place of read locks, there would have been deadlock.
>>>>>
>>>>> Now, if in fact, I'm wrong about my interpretation of the
>>>>> JPA spec (and it wouldn't be the first time) then you have a
>>>>> case. I'd be curious to know whether other JPA
>>>>> implementations pass your elegant test case, and what they
>>>>> are doing differently that makes it so.
>>>>>
>>>>> Also, if I am wrong about my interpretation, then the JPA
>>>>> TCK needs a test case that will snag this failure, because
>>>>> OpenJPA passes the current JPA TCK.
>>>>>
>>>>> Cheers,
>>>>>
>>>>> David
>>>>>
>>>>> Philip Aston wrote:
>>>>>
>>>>>> Oh yeah - my bad. Try this one instead:
>>>>>>
>>>>>> Suppose there are a set of Accounts, and a business rule that says
>>>>>> that the net balance must be positive.
>>>>>>
>>>>>> Innocent wants to draw down on Account 1 as far as possible. It read
>>>>>> locks the of Accounts, sums up the value, and and subtracts the
>>>>>> positive total from Account 1. Innocent begins its commit, and its
>>>>>> read locks are validated.
>>>>>>
>>>>>> Meanwhile InnocentToo does the same for Account 2, and commits.
>>>>>>
>>>>>> Innocent updates Account 1 and finishes its commit.
>>>>>>
>>>>>> The total in account summary is now negative, violating the business
>>>>>> rule. If read locks worked as I think they should, Innocent would have
>>>>>> received an OptimisticLockException.
>>>>>>
>>>>>>
>>>>>>
>>>>>> dezzio wrote:
>>>>>>> Hi Philip,
>>>>>>>
>>>>>>> When two transactions read the same version of AccountSummary, both
>>>>>>> cannot successfully update its sum.  Only one will successfully
>>>>>>> commit.
>>>>>>>
>>>>>>> David
>>>>
>>
> 

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Pinaki Poddar <pp...@apache.org>.
The expressed view relates to Philip's use case by his own observation:
> It "works" if I run with non-enhanced classes, since then there is no
> change detection and all rows get > written and version checked.

The point is the way OpenJPA decides what is flushed in a commit is not the
entire set {A,B,C} but only {B,C} that are dirty. So 'transaction
consistency' is ensured but not 'database consistency' because another
transaction may have committed {A,B}. And that breaks the parity invariance
of the entire set {A,B,C}. 



dezzio wrote:
> 
> Hi Pinaki,
> 
> Actually, much as I like your concepts, I don't yet see how they 
> illuminate the issue.
> 
> Cheers,
> 
> David
> 
> 
> Pinaki Poddar wrote:
>> Hi David & Philip,
>>    I have not had the time to pay attention to the use case it deserves
>> --
>> but reading the case brings up certain aspects that I will like to share
>> with your experience.
>>  
>>   This interesting use case can fail and is failing. But the issue it
>> reveals goes beyond locking semantics. 
>> 
>> Behavior of lock is described at datum level -- the levels of warranty
>> for 
>> shared access/mutation to a *single datum* in a consistent way. 
>> Transaction goes to the next stage and describes the level of warranty of
>> a
>> set of datum as an atomic 'unit of work'. 
>> But this test case demands a even higher level of warranty -- consistency
>> or
>> invariance of a set-based property (in this case the odd-even parity of 3
>> instances), which is neither the property of an individual datum nor the
>> property of a unit of work.
>> 
>> Of course, optimism of optimistic transaction model results in weaker
>> warranty of set-based invariance. To ensure set-based property
>> invariance, a
>> transaction must commit all 3 instances (with consistent odd-even parity)
>> as
>> a unit of work, but what it does is that it reads {A,B,C} and writes only
>> {B, C}.  
>> 
>> I will refrain from describing what flags of which OpenJPA configuration
>> property can be tweaked to get there because let me first hear your
>> comments
>> on the expressed views in this posts.  
>>  
>> 
>> Philip Aston wrote:
>>> Hi David,
>>>
>>> Thanks for confirming this. So to summarise where we are, we have:
>>>
>>>  1. A reasonable use case that can fail with some unlucky timing.
>>>
>>>  2. A technical test case demonstrating the problem that does not rely
>>> on unlucky timing.
>>>
>>>  3. A disagreement in our readings of whether 1 and 2 are spec.
>>> compliant. Personally, I don't share your reading of the spec. In my
>>> reading, read locks are safe and provide a concrete guarantee that if
>>> locked entity is changed by another transaction, the locking transaction
>>> will not complete.
>>>
>>> (This is a different QoS compared to a write lock - if a write lock is
>>> obtained and the pc flushed, the transaction knows that it will not fail
>>> due to another transaction updating the locked entity. Read locks are
>>> "more optimistic" and can support higher concurrency if there is minimal
>>> contention - many transactions can hold read locks, only one can hold
>>> right locks.).
>>>
>>>
>>> How can I convince you to change your interpretation of the spec? Anyone
>>> else have an opinion?
>>>
>>> FWIW, EclipseLink passes the test case.
>>>
>>> - Phil
>>>
>>> dezzio (via Nabble) wrote:
>>>> Hi Philip,
>>>>
>>>> Let's take a closer look.
>>>>
>>>> We have two bank accounts, Account[1] and Account[2], shared
>>>> jointly by customers Innocent[1] and Innocent[2]. The bank's
>>>> business rule is that no withdrawal can be made the draws
>>>> the combined total of the accounts below zero. This rule is
>>>> enforced in the server side Java application that customer's
>>>> use.
>>>>
>>>> At the start of the banking day, the accounts stand at:
>>>>
>>>>      Account[1] balance 100.
>>>>      Account[2] balance 50.
>>>>
>>>> Innocent[1] wants to draw out all the money, and asks the
>>>> application to take 150 from Account[1]. Innocent[2] also
>>>> wants to draw out all the money, and asks the application to
>>>> take 150 from Account[2]. By itself, either transaction
>>>> would conform to the bank's business rule.
>>>>
>>>> The application implements the withdrawal logic by doing the
>>>> following for each transaction.
>>>>
>>>> For Innocent[1], read Account[1] and Account[2]. Obtain a
>>>> read lock on Account[2]. Refresh Account[2]. Deduct 150 from
>>>> Account[1]. Verify business rule, result, sum of balances =
>>>> 0. Call JPA commit.
>>>>
>>>> For Innocent[2], read Account[1] and Account[2]. Obtain a
>>>> read lock on Account[1]. Refresh Account[1]. Deduct 150 from
>>>> Account[2]. Verify business rule, result, sum of balances =
>>>> 0. Call JPA commit.
>>>>
>>>> Within JPA commit, as seen over the JDBC connections, the
>>>> following time sequence occurs. (Other time sequences can
>>>> yield the same result.)
>>>>
>>>> Innocent[1]: Check version of Account[2]: passes.
>>>>
>>>> Innocent[2]: Check version of Account[1]: passes.
>>>> Innocent[2]: Update balance of Account[2], withdraw 150,
>>>>                  setting balance to -100: does not block.
>>>> Innocent[2]: commit: successful
>>>> Innocent[2]: Receives 150.
>>>>
>>>> Innocent[1]: Update balance of Account[1], withdraw 150,
>>>>                  setting balance to -50: does not block.
>>>> Innocent[1]: commit: successful.
>>>> Innocent[1]: Receives 150.
>>>>
>>>> After the two transactions:
>>>>
>>>> Account[1]: balance -50
>>>> Account[2]: balance -100
>>>>
>>>> Clearly the bank would not be happy. What's a developer to
>>>> do?
>>>>
>>>> I think the developer needs an education about what is meant
>>>> by the JPA spec. What JPA is guaranteeing is that when JPA
>>>> commit is called, the objects with read locks will have
>>>> their versions checked. The objects with write locks will
>>>> have their versions checked and changed. The objects that
>>>> have been modified will have their versions checked, their
>>>> information updated, and their versions changed. Clearly all
>>>> of these rules were enforced in the above example.
>>>>
>>>> If the developer had used write locks, both transactions
>>>> would not have succeeded. In fact, for the above example and
>>>> a similar time sequence, if write locks had been used in
>>>> place of read locks, there would have been deadlock.
>>>>
>>>> Now, if in fact, I'm wrong about my interpretation of the
>>>> JPA spec (and it wouldn't be the first time) then you have a
>>>> case. I'd be curious to know whether other JPA
>>>> implementations pass your elegant test case, and what they
>>>> are doing differently that makes it so.
>>>>
>>>> Also, if I am wrong about my interpretation, then the JPA
>>>> TCK needs a test case that will snag this failure, because
>>>> OpenJPA passes the current JPA TCK.
>>>>
>>>> Cheers,
>>>>
>>>> David
>>>>
>>>> Philip Aston wrote:
>>>>
>>>>> Oh yeah - my bad. Try this one instead:
>>>>>
>>>>> Suppose there are a set of Accounts, and a business rule that says
>>>>> that the net balance must be positive.
>>>>>
>>>>> Innocent wants to draw down on Account 1 as far as possible. It read
>>>>> locks the of Accounts, sums up the value, and and subtracts the
>>>>> positive total from Account 1. Innocent begins its commit, and its
>>>>> read locks are validated.
>>>>>
>>>>> Meanwhile InnocentToo does the same for Account 2, and commits.
>>>>>
>>>>> Innocent updates Account 1 and finishes its commit.
>>>>>
>>>>> The total in account summary is now negative, violating the business
>>>>> rule. If read locks worked as I think they should, Innocent would have
>>>>> received an OptimisticLockException.
>>>>>
>>>>>
>>>>>
>>>>> dezzio wrote:
>>>>>> Hi Philip,
>>>>>>
>>>>>> When two transactions read the same version of AccountSummary, both
>>>>>> cannot successfully update its sum.  Only one will successfully
>>>>>> commit.
>>>>>>
>>>>>> David
>>>
>>>
>> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2316329.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by David Ezzio <de...@apache.org>.
Hi Pinaki,

Actually, much as I like your concepts, I don't yet see how they 
illuminate the issue.

Cheers,

David


Pinaki Poddar wrote:
> Hi David & Philip,
>    I have not had the time to pay attention to the use case it deserves --
> but reading the case brings up certain aspects that I will like to share
> with your experience.
>  
>   This interesting use case can fail and is failing. But the issue it
> reveals goes beyond locking semantics. 
> 
> Behavior of lock is described at datum level -- the levels of warranty for 
> shared access/mutation to a *single datum* in a consistent way. 
> Transaction goes to the next stage and describes the level of warranty of a
> set of datum as an atomic 'unit of work'. 
> But this test case demands a even higher level of warranty -- consistency or
> invariance of a set-based property (in this case the odd-even parity of 3
> instances), which is neither the property of an individual datum nor the
> property of a unit of work.
> 
> Of course, optimism of optimistic transaction model results in weaker
> warranty of set-based invariance. To ensure set-based property invariance, a
> transaction must commit all 3 instances (with consistent odd-even parity) as
> a unit of work, but what it does is that it reads {A,B,C} and writes only
> {B, C}.  
> 
> I will refrain from describing what flags of which OpenJPA configuration
> property can be tweaked to get there because let me first hear your comments
> on the expressed views in this posts.  
>  
> 
> Philip Aston wrote:
>> Hi David,
>>
>> Thanks for confirming this. So to summarise where we are, we have:
>>
>>  1. A reasonable use case that can fail with some unlucky timing.
>>
>>  2. A technical test case demonstrating the problem that does not rely
>> on unlucky timing.
>>
>>  3. A disagreement in our readings of whether 1 and 2 are spec.
>> compliant. Personally, I don't share your reading of the spec. In my
>> reading, read locks are safe and provide a concrete guarantee that if
>> locked entity is changed by another transaction, the locking transaction
>> will not complete.
>>
>> (This is a different QoS compared to a write lock - if a write lock is
>> obtained and the pc flushed, the transaction knows that it will not fail
>> due to another transaction updating the locked entity. Read locks are
>> "more optimistic" and can support higher concurrency if there is minimal
>> contention - many transactions can hold read locks, only one can hold
>> right locks.).
>>
>>
>> How can I convince you to change your interpretation of the spec? Anyone
>> else have an opinion?
>>
>> FWIW, EclipseLink passes the test case.
>>
>> - Phil
>>
>> dezzio (via Nabble) wrote:
>>> Hi Philip,
>>>
>>> Let's take a closer look.
>>>
>>> We have two bank accounts, Account[1] and Account[2], shared
>>> jointly by customers Innocent[1] and Innocent[2]. The bank's
>>> business rule is that no withdrawal can be made the draws
>>> the combined total of the accounts below zero. This rule is
>>> enforced in the server side Java application that customer's
>>> use.
>>>
>>> At the start of the banking day, the accounts stand at:
>>>
>>>      Account[1] balance 100.
>>>      Account[2] balance 50.
>>>
>>> Innocent[1] wants to draw out all the money, and asks the
>>> application to take 150 from Account[1]. Innocent[2] also
>>> wants to draw out all the money, and asks the application to
>>> take 150 from Account[2]. By itself, either transaction
>>> would conform to the bank's business rule.
>>>
>>> The application implements the withdrawal logic by doing the
>>> following for each transaction.
>>>
>>> For Innocent[1], read Account[1] and Account[2]. Obtain a
>>> read lock on Account[2]. Refresh Account[2]. Deduct 150 from
>>> Account[1]. Verify business rule, result, sum of balances =
>>> 0. Call JPA commit.
>>>
>>> For Innocent[2], read Account[1] and Account[2]. Obtain a
>>> read lock on Account[1]. Refresh Account[1]. Deduct 150 from
>>> Account[2]. Verify business rule, result, sum of balances =
>>> 0. Call JPA commit.
>>>
>>> Within JPA commit, as seen over the JDBC connections, the
>>> following time sequence occurs. (Other time sequences can
>>> yield the same result.)
>>>
>>> Innocent[1]: Check version of Account[2]: passes.
>>>
>>> Innocent[2]: Check version of Account[1]: passes.
>>> Innocent[2]: Update balance of Account[2], withdraw 150,
>>>                  setting balance to -100: does not block.
>>> Innocent[2]: commit: successful
>>> Innocent[2]: Receives 150.
>>>
>>> Innocent[1]: Update balance of Account[1], withdraw 150,
>>>                  setting balance to -50: does not block.
>>> Innocent[1]: commit: successful.
>>> Innocent[1]: Receives 150.
>>>
>>> After the two transactions:
>>>
>>> Account[1]: balance -50
>>> Account[2]: balance -100
>>>
>>> Clearly the bank would not be happy. What's a developer to
>>> do?
>>>
>>> I think the developer needs an education about what is meant
>>> by the JPA spec. What JPA is guaranteeing is that when JPA
>>> commit is called, the objects with read locks will have
>>> their versions checked. The objects with write locks will
>>> have their versions checked and changed. The objects that
>>> have been modified will have their versions checked, their
>>> information updated, and their versions changed. Clearly all
>>> of these rules were enforced in the above example.
>>>
>>> If the developer had used write locks, both transactions
>>> would not have succeeded. In fact, for the above example and
>>> a similar time sequence, if write locks had been used in
>>> place of read locks, there would have been deadlock.
>>>
>>> Now, if in fact, I'm wrong about my interpretation of the
>>> JPA spec (and it wouldn't be the first time) then you have a
>>> case. I'd be curious to know whether other JPA
>>> implementations pass your elegant test case, and what they
>>> are doing differently that makes it so.
>>>
>>> Also, if I am wrong about my interpretation, then the JPA
>>> TCK needs a test case that will snag this failure, because
>>> OpenJPA passes the current JPA TCK.
>>>
>>> Cheers,
>>>
>>> David
>>>
>>> Philip Aston wrote:
>>>
>>>> Oh yeah - my bad. Try this one instead:
>>>>
>>>> Suppose there are a set of Accounts, and a business rule that says
>>>> that the net balance must be positive.
>>>>
>>>> Innocent wants to draw down on Account 1 as far as possible. It read
>>>> locks the of Accounts, sums up the value, and and subtracts the
>>>> positive total from Account 1. Innocent begins its commit, and its
>>>> read locks are validated.
>>>>
>>>> Meanwhile InnocentToo does the same for Account 2, and commits.
>>>>
>>>> Innocent updates Account 1 and finishes its commit.
>>>>
>>>> The total in account summary is now negative, violating the business
>>>> rule. If read locks worked as I think they should, Innocent would have
>>>> received an OptimisticLockException.
>>>>
>>>>
>>>>
>>>> dezzio wrote:
>>>>> Hi Philip,
>>>>>
>>>>> When two transactions read the same version of AccountSummary, both
>>>>> cannot successfully update its sum.  Only one will successfully commit.
>>>>>
>>>>> David
>>
>>
> 

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Pinaki Poddar <pp...@apache.org>.
Hi David & Philip,
   I have not had the time to pay attention to the use case it deserves --
but reading the case brings up certain aspects that I will like to share
with your experience.
 
  This interesting use case can fail and is failing. But the issue it
reveals goes beyond locking semantics. 

Behavior of lock is described at datum level -- the levels of warranty for 
shared access/mutation to a *single datum* in a consistent way. 
Transaction goes to the next stage and describes the level of warranty of a
set of datum as an atomic 'unit of work'. 
But this test case demands a even higher level of warranty -- consistency or
invariance of a set-based property (in this case the odd-even parity of 3
instances), which is neither the property of an individual datum nor the
property of a unit of work.

Of course, optimism of optimistic transaction model results in weaker
warranty of set-based invariance. To ensure set-based property invariance, a
transaction must commit all 3 instances (with consistent odd-even parity) as
a unit of work, but what it does is that it reads {A,B,C} and writes only
{B, C}.  

I will refrain from describing what flags of which OpenJPA configuration
property can be tweaked to get there because let me first hear your comments
on the expressed views in this posts.  
 

Philip Aston wrote:
> 
> Hi David,
> 
> Thanks for confirming this. So to summarise where we are, we have:
> 
>  1. A reasonable use case that can fail with some unlucky timing.
> 
>  2. A technical test case demonstrating the problem that does not rely
> on unlucky timing.
> 
>  3. A disagreement in our readings of whether 1 and 2 are spec.
> compliant. Personally, I don't share your reading of the spec. In my
> reading, read locks are safe and provide a concrete guarantee that if
> locked entity is changed by another transaction, the locking transaction
> will not complete.
> 
> (This is a different QoS compared to a write lock - if a write lock is
> obtained and the pc flushed, the transaction knows that it will not fail
> due to another transaction updating the locked entity. Read locks are
> "more optimistic" and can support higher concurrency if there is minimal
> contention - many transactions can hold read locks, only one can hold
> right locks.).
> 
> 
> How can I convince you to change your interpretation of the spec? Anyone
> else have an opinion?
> 
> FWIW, EclipseLink passes the test case.
> 
> - Phil
> 
> dezzio (via Nabble) wrote:
>> Hi Philip,
>> 
>> Let's take a closer look.
>> 
>> We have two bank accounts, Account[1] and Account[2], shared
>> jointly by customers Innocent[1] and Innocent[2]. The bank's
>> business rule is that no withdrawal can be made the draws
>> the combined total of the accounts below zero. This rule is
>> enforced in the server side Java application that customer's
>> use.
>> 
>> At the start of the banking day, the accounts stand at:
>> 
>>      Account[1] balance 100.
>>      Account[2] balance 50.
>> 
>> Innocent[1] wants to draw out all the money, and asks the
>> application to take 150 from Account[1]. Innocent[2] also
>> wants to draw out all the money, and asks the application to
>> take 150 from Account[2]. By itself, either transaction
>> would conform to the bank's business rule.
>> 
>> The application implements the withdrawal logic by doing the
>> following for each transaction.
>> 
>> For Innocent[1], read Account[1] and Account[2]. Obtain a
>> read lock on Account[2]. Refresh Account[2]. Deduct 150 from
>> Account[1]. Verify business rule, result, sum of balances =
>> 0. Call JPA commit.
>> 
>> For Innocent[2], read Account[1] and Account[2]. Obtain a
>> read lock on Account[1]. Refresh Account[1]. Deduct 150 from
>> Account[2]. Verify business rule, result, sum of balances =
>> 0. Call JPA commit.
>> 
>> Within JPA commit, as seen over the JDBC connections, the
>> following time sequence occurs. (Other time sequences can
>> yield the same result.)
>> 
>> Innocent[1]: Check version of Account[2]: passes.
>> 
>> Innocent[2]: Check version of Account[1]: passes.
>> Innocent[2]: Update balance of Account[2], withdraw 150,
>>                  setting balance to -100: does not block.
>> Innocent[2]: commit: successful
>> Innocent[2]: Receives 150.
>> 
>> Innocent[1]: Update balance of Account[1], withdraw 150,
>>                  setting balance to -50: does not block.
>> Innocent[1]: commit: successful.
>> Innocent[1]: Receives 150.
>> 
>> After the two transactions:
>> 
>> Account[1]: balance -50
>> Account[2]: balance -100
>> 
>> Clearly the bank would not be happy. What's a developer to
>> do?
>> 
>> I think the developer needs an education about what is meant
>> by the JPA spec. What JPA is guaranteeing is that when JPA
>> commit is called, the objects with read locks will have
>> their versions checked. The objects with write locks will
>> have their versions checked and changed. The objects that
>> have been modified will have their versions checked, their
>> information updated, and their versions changed. Clearly all
>> of these rules were enforced in the above example.
>> 
>> If the developer had used write locks, both transactions
>> would not have succeeded. In fact, for the above example and
>> a similar time sequence, if write locks had been used in
>> place of read locks, there would have been deadlock.
>> 
>> Now, if in fact, I'm wrong about my interpretation of the
>> JPA spec (and it wouldn't be the first time) then you have a
>> case. I'd be curious to know whether other JPA
>> implementations pass your elegant test case, and what they
>> are doing differently that makes it so.
>> 
>> Also, if I am wrong about my interpretation, then the JPA
>> TCK needs a test case that will snag this failure, because
>> OpenJPA passes the current JPA TCK.
>> 
>> Cheers,
>> 
>> David
>> 
>> Philip Aston wrote:
>> 
>>> Oh yeah - my bad. Try this one instead:
>>>
>>> Suppose there are a set of Accounts, and a business rule that says
>>> that the net balance must be positive.
>>>
>>> Innocent wants to draw down on Account 1 as far as possible. It read
>>> locks the of Accounts, sums up the value, and and subtracts the
>>> positive total from Account 1. Innocent begins its commit, and its
>>> read locks are validated.
>>>
>>> Meanwhile InnocentToo does the same for Account 2, and commits.
>>>
>>> Innocent updates Account 1 and finishes its commit.
>>>
>>> The total in account summary is now negative, violating the business
>>> rule. If read locks worked as I think they should, Innocent would have
>>> received an OptimisticLockException.
>>>
>>>
>>>
>>> dezzio wrote:
>>>> Hi Philip,
>>>>
>>>> When two transactions read the same version of AccountSummary, both
>>>> cannot successfully update its sum.  Only one will successfully commit.
>>>>
>>>> David
> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2315372.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.
Hi David,

Thanks for confirming this. So to summarise where we are, we have:

 1. A reasonable use case that can fail with some unlucky timing.

 2. A technical test case demonstrating the problem that does not rely
on unlucky timing.

 3. A disagreement in our readings of whether 1 and 2 are spec.
compliant. Personally, I don't share your reading of the spec. In my
reading, read locks are safe and provide a concrete guarantee that if
locked entity is changed by another transaction, the locking transaction
will not complete.

(This is a different QoS compared to a write lock - if a write lock is
obtained and the pc flushed, the transaction knows that it will not fail
due to another transaction updating the locked entity. Read locks are
"more optimistic" and can support higher concurrency if there is minimal
contention - many transactions can hold read locks, only one can hold
right locks.).


How can I convince you to change your interpretation of the spec? Anyone
else have an opinion?

FWIW, EclipseLink passes the test case.

- Phil

dezzio (via Nabble) wrote:
> Hi Philip,
> 
> Let's take a closer look.
> 
> We have two bank accounts, Account[1] and Account[2], shared
> jointly by customers Innocent[1] and Innocent[2]. The bank's
> business rule is that no withdrawal can be made the draws
> the combined total of the accounts below zero. This rule is
> enforced in the server side Java application that customer's
> use.
> 
> At the start of the banking day, the accounts stand at:
> 
>      Account[1] balance 100.
>      Account[2] balance 50.
> 
> Innocent[1] wants to draw out all the money, and asks the
> application to take 150 from Account[1]. Innocent[2] also
> wants to draw out all the money, and asks the application to
> take 150 from Account[2]. By itself, either transaction
> would conform to the bank's business rule.
> 
> The application implements the withdrawal logic by doing the
> following for each transaction.
> 
> For Innocent[1], read Account[1] and Account[2]. Obtain a
> read lock on Account[2]. Refresh Account[2]. Deduct 150 from
> Account[1]. Verify business rule, result, sum of balances =
> 0. Call JPA commit.
> 
> For Innocent[2], read Account[1] and Account[2]. Obtain a
> read lock on Account[1]. Refresh Account[1]. Deduct 150 from
> Account[2]. Verify business rule, result, sum of balances =
> 0. Call JPA commit.
> 
> Within JPA commit, as seen over the JDBC connections, the
> following time sequence occurs. (Other time sequences can
> yield the same result.)
> 
> Innocent[1]: Check version of Account[2]: passes.
> 
> Innocent[2]: Check version of Account[1]: passes.
> Innocent[2]: Update balance of Account[2], withdraw 150,
>                  setting balance to -100: does not block.
> Innocent[2]: commit: successful
> Innocent[2]: Receives 150.
> 
> Innocent[1]: Update balance of Account[1], withdraw 150,
>                  setting balance to -50: does not block.
> Innocent[1]: commit: successful.
> Innocent[1]: Receives 150.
> 
> After the two transactions:
> 
> Account[1]: balance -50
> Account[2]: balance -100
> 
> Clearly the bank would not be happy. What's a developer to
> do?
> 
> I think the developer needs an education about what is meant
> by the JPA spec. What JPA is guaranteeing is that when JPA
> commit is called, the objects with read locks will have
> their versions checked. The objects with write locks will
> have their versions checked and changed. The objects that
> have been modified will have their versions checked, their
> information updated, and their versions changed. Clearly all
> of these rules were enforced in the above example.
> 
> If the developer had used write locks, both transactions
> would not have succeeded. In fact, for the above example and
> a similar time sequence, if write locks had been used in
> place of read locks, there would have been deadlock.
> 
> Now, if in fact, I'm wrong about my interpretation of the
> JPA spec (and it wouldn't be the first time) then you have a
> case. I'd be curious to know whether other JPA
> implementations pass your elegant test case, and what they
> are doing differently that makes it so.
> 
> Also, if I am wrong about my interpretation, then the JPA
> TCK needs a test case that will snag this failure, because
> OpenJPA passes the current JPA TCK.
> 
> Cheers,
> 
> David
> 
> Philip Aston wrote:
> 
>> Oh yeah - my bad. Try this one instead:
>>
>> Suppose there are a set of Accounts, and a business rule that says
>> that the net balance must be positive.
>>
>> Innocent wants to draw down on Account 1 as far as possible. It read
>> locks the of Accounts, sums up the value, and and subtracts the
>> positive total from Account 1. Innocent begins its commit, and its
>> read locks are validated.
>>
>> Meanwhile InnocentToo does the same for Account 2, and commits.
>>
>> Innocent updates Account 1 and finishes its commit.
>>
>> The total in account summary is now negative, violating the business
>> rule. If read locks worked as I think they should, Innocent would have
>> received an OptimisticLockException.
>>
>>
>>
>> dezzio wrote:
>>> Hi Philip,
>>>
>>> When two transactions read the same version of AccountSummary, both
>>> cannot successfully update its sum.  Only one will successfully commit.
>>>
>>> David


-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2298432.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by David Ezzio <de...@apache.org>.
Hi Philip,

Let's take a closer look.

We have two bank accounts, Account[1] and Account[2], shared
jointly by customers Innocent[1] and Innocent[2]. The bank's
business rule is that no withdrawal can be made the draws
the combined total of the accounts below zero. This rule is
enforced in the server side Java application that customer's
use.

At the start of the banking day, the accounts stand at:

     Account[1] balance 100.
     Account[2] balance 50.

Innocent[1] wants to draw out all the money, and asks the
application to take 150 from Account[1]. Innocent[2] also
wants to draw out all the money, and asks the application to
take 150 from Account[2]. By itself, either transaction
would conform to the bank's business rule.

The application implements the withdrawal logic by doing the
following for each transaction.

For Innocent[1], read Account[1] and Account[2]. Obtain a
read lock on Account[2]. Refresh Account[2]. Deduct 150 from
Account[1]. Verify business rule, result, sum of balances =
0. Call JPA commit.

For Innocent[2], read Account[1] and Account[2]. Obtain a
read lock on Account[1]. Refresh Account[1]. Deduct 150 from
Account[2]. Verify business rule, result, sum of balances =
0. Call JPA commit.

Within JPA commit, as seen over the JDBC connections, the
following time sequence occurs. (Other time sequences can
yield the same result.)

Innocent[1]: Check version of Account[2]: passes.

Innocent[2]: Check version of Account[1]: passes.
Innocent[2]: Update balance of Account[2], withdraw 150,
                 setting balance to -100: does not block.
Innocent[2]: commit: successful
Innocent[2]: Receives 150.

Innocent[1]: Update balance of Account[1], withdraw 150,
                 setting balance to -50: does not block.
Innocent[1]: commit: successful.
Innocent[1]: Receives 150.

After the two transactions:

Account[1]: balance -50
Account[2]: balance -100

Clearly the bank would not be happy. What's a developer to
do?

I think the developer needs an education about what is meant
by the JPA spec. What JPA is guaranteeing is that when JPA
commit is called, the objects with read locks will have
their versions checked. The objects with write locks will
have their versions checked and changed. The objects that
have been modified will have their versions checked, their
information updated, and their versions changed. Clearly all
of these rules were enforced in the above example.

If the developer had used write locks, both transactions
would not have succeeded. In fact, for the above example and
a similar time sequence, if write locks had been used in
place of read locks, there would have been deadlock.

Now, if in fact, I'm wrong about my interpretation of the
JPA spec (and it wouldn't be the first time) then you have a
case. I'd be curious to know whether other JPA
implementations pass your elegant test case, and what they
are doing differently that makes it so.

Also, if I am wrong about my interpretation, then the JPA
TCK needs a test case that will snag this failure, because
OpenJPA passes the current JPA TCK.

Cheers,

David

Philip Aston wrote:
> Oh yeah - my bad. Try this one instead:
> 
> Suppose there are a set of Accounts, and a business rule that says
> that the net balance must be positive.
> 
> Innocent wants to draw down on Account 1 as far as possible. It read
> locks the of Accounts, sums up the value, and and subtracts the
> positive total from Account 1. Innocent begins its commit, and its
> read locks are validated.
> 
> Meanwhile InnocentToo does the same for Account 2, and commits.
> 
> Innocent updates Account 1 and finishes its commit.
> 
> The total in account summary is now negative, violating the business
> rule. If read locks worked as I think they should, Innocent would have
> received an OptimisticLockException.
> 
> 
> 
> dezzio wrote:
>> Hi Philip,
>>
>> When two transactions read the same version of AccountSummary, both 
>> cannot successfully update its sum.  Only one will successfully commit.
>>
>> David
>>
>>
> 

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.
Oh yeah - my bad. Try this one instead:

Suppose there are a set of Accounts, and a business rule that says
that the net balance must be positive.

Innocent wants to draw down on Account 1 as far as possible. It read
locks the of Accounts, sums up the value, and and subtracts the
positive total from Account 1. Innocent begins its commit, and its
read locks are validated.

Meanwhile InnocentToo does the same for Account 2, and commits.

Innocent updates Account 1 and finishes its commit.

The total in account summary is now negative, violating the business
rule. If read locks worked as I think they should, Innocent would have
received an OptimisticLockException.



dezzio wrote:
> 
> Hi Philip,
> 
> When two transactions read the same version of AccountSummary, both 
> cannot successfully update its sum.  Only one will successfully commit.
> 
> David
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2296335.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by David Ezzio <de...@apache.org>.
Hi Philip,

When two transactions read the same version of AccountSummary, both 
cannot successfully update its sum.  Only one will successfully commit.

David

Philip Aston wrote:
> 
> Suppose Innocent read locks a bunch of Accounts, sums up the value, and
> writing out the total to a separate AccountSummary entity. Innocent
> begins its commit, and its read locks are validated.
> 
> Meanwhile InnocentToo modifies an Account, taking care to update the
> total in AccountSummary.
> 
> Innocent finishes its commit.
> 
> The total in account summary is now wrong.
> 
> Due to this "corner case", the OpenJPA read lock implementation can't be
> relied on.
> 
> - Phil
> 
> dezzio (via Nabble) wrote:
>> Hi Philip and Pinaki,
>>
>> Since Philip brought his test case to me before coming here, I am
>> familiar with it. So, I'll try to elucidate.
>>
>> The test case concerns the order of operations within the OpenJPA commit
>> method as seen over the two JDBC connections involved. (Philip actually
>> uses three transactions, but the third is just to make the timings of
>> the other two always come out the same.)
>>
>> Time sequence:
>>
>>      Innocent: SELECT to check version of Flag1 (passes)
>>      Evil:     UPDATE to change Flag1 (does not block)
>>      Evil:     COMMIT (succeeds)
>>      Innocent: UPDATE to change Flag2 (does not block)
>>      Innocent: COMMIT (succeeds)
>>
>> Philip's argument is that both transactions ought not to succeed since
>> Innocent holds a READ lock on Flag1 that ought to be enforced until his
>> transaction commits.
>>
>> I believe that the implementor's argument is that optimistic
>> transactions ought to avoid deadlock, and READ locks ought not to
>> increase the chances of deadlock.
>>
>> To accomplish this end, it is acceptable to allow evil to triumph,in
>> this test case, especially since Evil has not caused any harm.
>> Presumably, whatever information in the object Flag1 that Innocent
>> relied upon has already been used to modify the object Flag2, and the
>> net result is that image in the database is the same as it would be if
>> Evil has dallied until Innocent was done. On the other hand, if Evil had
>> been eager to accomplish his nefarious deed, Innocent's transaction
>> would not have succeeded.
>>
>> In short, Philip, where is the harm?
>>
>> Cheers,
>>
>> David
>>
>>
>> Philip Aston wrote:
>>
>>> All understood and agreed.
>>>
>>> The problem is not 1., and I'm using read committed. The problem is in
>> the
>>> implementation of the VersionLockManager itself. It does the check, then
>>> leaves a gap where the data can be modified by a separate commit
>> before the
>>> update happens.  Thus its actual behaviour is "verifying that the
>> version of
>>> all read-locked instances is unchanged at the start of the commit of the
>>> transaction".
>>>
>>>
>>>
>>> Pinaki Poddar wrote:
>>>> Hi Philip,
>>>>   I did not execute the test case but reading it gave me the impression
>>>> that it is exercising/exposing the following known limitation. But
>> may be
>>>> I should analyze the test case deeper to understand what is causing this
>>>> behavior.
>>>>
>>>> 1. "In order to maintain reasonable performance levels when loading
>> object
>>>> state, OpenJPA can only guarantee that an object is locked at the proper
>>>> lock level after the state has been retrieved from the database. This
>>>> means that it is technically possible for another transaction to "sneak
>>>> in" and modify the database record after OpenJPA retrieves the state,
>> but
>>>> before it locks the object. The only way to positively guarantee that
>> the
>>>> object is locked and has the most recent state to refresh the object
>> after
>>>> locking it."
>>>>  
>>>>   2. On VersionLockManager (which is the default and being used in this
>>>> case): "This lock manager does not perform any exclusive locking, but
>>>> instead ensures read consistency by verifying that the version of all
>>>> read-locked instances is unchanged at the end of the transaction."
>>>>
>>>>   3. Another possibly relevant point: "In order for the version lock
>>>> manager to prevent the dirty read phenomenon, the underlying data
>> store's
>>>> transaction isolation level must be set to the equivalent of "read
>>>> committed" or higher."
>>>>
>>>>
>>>>
>>>> Philip Aston wrote:
>>>>> Thanks Pinaki.
>>>>>
>>>>> I don't think the documented known issues cover my case. The first
>> bullet
>>>>> point is about the pessimistic lock manager, which I'm not using. The
>>>>> second bullet is about the read consistency of the locked object - this
>>>>> isn't the problem either. The last paragraph is again about the
>>>>> pessimistic lock manager. And I don't see any of the INFO level
>> messages
>>>>> referred to.
>>>>>
>>>>> I find the documented issues to be reasonable, but I'm afraid I
>> can't say
>>>>> the same of the read locking implementation.
>>>>>
>>>>> - Phil
>>>>>
>>>>>
>>>>> Pinaki Poddar wrote:
>>>>>> Hi Philip,
>>>>>>    The points you raise are valid, correct and known. Please refer to
>>>>>> [1].
>>>>>>
>>>>>>    By the way, the test case is very neat!
>>>>>>
>>>>>> [1]
>>>>>>
>> http://openjpa.apache.org/builds/latest/docs/manual/manual.html#ref_guide_locking_issues
> 

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.

Suppose Innocent read locks a bunch of Accounts, sums up the value, and
writing out the total to a separate AccountSummary entity. Innocent
begins its commit, and its read locks are validated.

Meanwhile InnocentToo modifies an Account, taking care to update the
total in AccountSummary.

Innocent finishes its commit.

The total in account summary is now wrong.

Due to this "corner case", the OpenJPA read lock implementation can't be
relied on.

- Phil

dezzio (via Nabble) wrote:
> Hi Philip and Pinaki,
> 
> Since Philip brought his test case to me before coming here, I am
> familiar with it. So, I'll try to elucidate.
> 
> The test case concerns the order of operations within the OpenJPA commit
> method as seen over the two JDBC connections involved. (Philip actually
> uses three transactions, but the third is just to make the timings of
> the other two always come out the same.)
> 
> Time sequence:
> 
>      Innocent: SELECT to check version of Flag1 (passes)
>      Evil:     UPDATE to change Flag1 (does not block)
>      Evil:     COMMIT (succeeds)
>      Innocent: UPDATE to change Flag2 (does not block)
>      Innocent: COMMIT (succeeds)
> 
> Philip's argument is that both transactions ought not to succeed since
> Innocent holds a READ lock on Flag1 that ought to be enforced until his
> transaction commits.
> 
> I believe that the implementor's argument is that optimistic
> transactions ought to avoid deadlock, and READ locks ought not to
> increase the chances of deadlock.
> 
> To accomplish this end, it is acceptable to allow evil to triumph,in
> this test case, especially since Evil has not caused any harm.
> Presumably, whatever information in the object Flag1 that Innocent
> relied upon has already been used to modify the object Flag2, and the
> net result is that image in the database is the same as it would be if
> Evil has dallied until Innocent was done. On the other hand, if Evil had
> been eager to accomplish his nefarious deed, Innocent's transaction
> would not have succeeded.
> 
> In short, Philip, where is the harm?
> 
> Cheers,
> 
> David
> 
> 
> Philip Aston wrote:
> 
>> All understood and agreed.
>>
>> The problem is not 1., and I'm using read committed. The problem is in
> the
>> implementation of the VersionLockManager itself. It does the check, then
>> leaves a gap where the data can be modified by a separate commit
> before the
>> update happens.  Thus its actual behaviour is "verifying that the
> version of
>> all read-locked instances is unchanged at the start of the commit of the
>> transaction".
>>
>>
>>
>> Pinaki Poddar wrote:
>>> Hi Philip,
>>>   I did not execute the test case but reading it gave me the impression
>>> that it is exercising/exposing the following known limitation. But
> may be
>>> I should analyze the test case deeper to understand what is causing this
>>> behavior.
>>>
>>> 1. "In order to maintain reasonable performance levels when loading
> object
>>> state, OpenJPA can only guarantee that an object is locked at the proper
>>> lock level after the state has been retrieved from the database. This
>>> means that it is technically possible for another transaction to "sneak
>>> in" and modify the database record after OpenJPA retrieves the state,
> but
>>> before it locks the object. The only way to positively guarantee that
> the
>>> object is locked and has the most recent state to refresh the object
> after
>>> locking it."
>>>  
>>>   2. On VersionLockManager (which is the default and being used in this
>>> case): "This lock manager does not perform any exclusive locking, but
>>> instead ensures read consistency by verifying that the version of all
>>> read-locked instances is unchanged at the end of the transaction."
>>>
>>>   3. Another possibly relevant point: "In order for the version lock
>>> manager to prevent the dirty read phenomenon, the underlying data
> store's
>>> transaction isolation level must be set to the equivalent of "read
>>> committed" or higher."
>>>
>>>
>>>
>>> Philip Aston wrote:
>>>> Thanks Pinaki.
>>>>
>>>> I don't think the documented known issues cover my case. The first
> bullet
>>>> point is about the pessimistic lock manager, which I'm not using. The
>>>> second bullet is about the read consistency of the locked object - this
>>>> isn't the problem either. The last paragraph is again about the
>>>> pessimistic lock manager. And I don't see any of the INFO level
> messages
>>>> referred to.
>>>>
>>>> I find the documented issues to be reasonable, but I'm afraid I
> can't say
>>>> the same of the read locking implementation.
>>>>
>>>> - Phil
>>>>
>>>>
>>>> Pinaki Poddar wrote:
>>>>> Hi Philip,
>>>>>    The points you raise are valid, correct and known. Please refer to
>>>>> [1].
>>>>>
>>>>>    By the way, the test case is very neat!
>>>>>
>>>>> [1]
>>>>>
> http://openjpa.apache.org/builds/latest/docs/manual/manual.html#ref_guide_locking_issues
>>>>>

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2289651.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by David Ezzio <de...@apache.org>.
Hi Philip and Pinaki,

Since Philip brought his test case to me before coming here, I am 
familiar with it. So, I'll try to elucidate.

The test case concerns the order of operations within the OpenJPA commit 
method as seen over the two JDBC connections involved. (Philip actually 
uses three transactions, but the third is just to make the timings of 
the other two always come out the same.)

Time sequence:

     Innocent: SELECT to check version of Flag1 (passes)
     Evil:     UPDATE to change Flag1 (does not block)
     Evil:     COMMIT (succeeds)
     Innocent: UPDATE to change Flag2 (does not block)
     Innocent: COMMIT (succeeds)

Philip's argument is that both transactions ought not to succeed since 
Innocent holds a READ lock on Flag1 that ought to be enforced until his 
transaction commits.

I believe that the implementor's argument is that optimistic 
transactions ought to avoid deadlock, and READ locks ought not to 
increase the chances of deadlock.

To accomplish this end, it is acceptable to allow evil to triumph,in 
this test case, especially since Evil has not caused any harm. 
Presumably, whatever information in the object Flag1 that Innocent 
relied upon has already been used to modify the object Flag2, and the 
net result is that image in the database is the same as it would be if 
Evil has dallied until Innocent was done. On the other hand, if Evil had 
been eager to accomplish his nefarious deed, Innocent's transaction 
would not have succeeded.

In short, Philip, where is the harm?

Cheers,

David


Philip Aston wrote:
> All understood and agreed.
> 
> The problem is not 1., and I'm using read committed. The problem is in the
> implementation of the VersionLockManager itself. It does the check, then
> leaves a gap where the data can be modified by a separate commit before the
> update happens.  Thus its actual behaviour is "verifying that the version of
> all read-locked instances is unchanged at the start of the commit of the
> transaction".
> 
> 
> 
> Pinaki Poddar wrote:
>> Hi Philip,
>>   I did not execute the test case but reading it gave me the impression
>> that it is exercising/exposing the following known limitation. But may be
>> I should analyze the test case deeper to understand what is causing this
>> behavior.
>>
>> 1. "In order to maintain reasonable performance levels when loading object
>> state, OpenJPA can only guarantee that an object is locked at the proper
>> lock level after the state has been retrieved from the database. This
>> means that it is technically possible for another transaction to "sneak
>> in" and modify the database record after OpenJPA retrieves the state, but
>> before it locks the object. The only way to positively guarantee that the
>> object is locked and has the most recent state to refresh the object after
>> locking it."
>>  
>>   2. On VersionLockManager (which is the default and being used in this
>> case): "This lock manager does not perform any exclusive locking, but
>> instead ensures read consistency by verifying that the version of all
>> read-locked instances is unchanged at the end of the transaction."
>>
>>   3. Another possibly relevant point: "In order for the version lock
>> manager to prevent the dirty read phenomenon, the underlying data store's
>> transaction isolation level must be set to the equivalent of "read
>> committed" or higher." 
>>
>>
>>
>> Philip Aston wrote:
>>> Thanks Pinaki.
>>>
>>> I don't think the documented known issues cover my case. The first bullet
>>> point is about the pessimistic lock manager, which I'm not using. The
>>> second bullet is about the read consistency of the locked object - this
>>> isn't the problem either. The last paragraph is again about the
>>> pessimistic lock manager. And I don't see any of the INFO level messages
>>> referred to.
>>>
>>> I find the documented issues to be reasonable, but I'm afraid I can't say
>>> the same of the read locking implementation.
>>>
>>> - Phil
>>>
>>>
>>> Pinaki Poddar wrote:
>>>> Hi Philip,
>>>>    The points you raise are valid, correct and known. Please refer to
>>>> [1].
>>>>
>>>>    By the way, the test case is very neat!
>>>>
>>>> [1]
>>>> http://openjpa.apache.org/builds/latest/docs/manual/manual.html#ref_guide_locking_issues
>>>>
>>>>
>>>>
>>>
>>
> 

Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.
All understood and agreed.

The problem is not 1., and I'm using read committed. The problem is in the
implementation of the VersionLockManager itself. It does the check, then
leaves a gap where the data can be modified by a separate commit before the
update happens.  Thus its actual behaviour is "verifying that the version of
all read-locked instances is unchanged at the start of the commit of the
transaction".



Pinaki Poddar wrote:
> 
> Hi Philip,
>   I did not execute the test case but reading it gave me the impression
> that it is exercising/exposing the following known limitation. But may be
> I should analyze the test case deeper to understand what is causing this
> behavior.
> 
> 1. "In order to maintain reasonable performance levels when loading object
> state, OpenJPA can only guarantee that an object is locked at the proper
> lock level after the state has been retrieved from the database. This
> means that it is technically possible for another transaction to "sneak
> in" and modify the database record after OpenJPA retrieves the state, but
> before it locks the object. The only way to positively guarantee that the
> object is locked and has the most recent state to refresh the object after
> locking it."
>  
>   2. On VersionLockManager (which is the default and being used in this
> case): "This lock manager does not perform any exclusive locking, but
> instead ensures read consistency by verifying that the version of all
> read-locked instances is unchanged at the end of the transaction."
> 
>   3. Another possibly relevant point: "In order for the version lock
> manager to prevent the dirty read phenomenon, the underlying data store's
> transaction isolation level must be set to the equivalent of "read
> committed" or higher." 
> 
> 
> 
> Philip Aston wrote:
>> 
>> Thanks Pinaki.
>> 
>> I don't think the documented known issues cover my case. The first bullet
>> point is about the pessimistic lock manager, which I'm not using. The
>> second bullet is about the read consistency of the locked object - this
>> isn't the problem either. The last paragraph is again about the
>> pessimistic lock manager. And I don't see any of the INFO level messages
>> referred to.
>> 
>> I find the documented issues to be reasonable, but I'm afraid I can't say
>> the same of the read locking implementation.
>> 
>> - Phil
>> 
>> 
>> Pinaki Poddar wrote:
>>> 
>>> Hi Philip,
>>>    The points you raise are valid, correct and known. Please refer to
>>> [1].
>>> 
>>>    By the way, the test case is very neat!
>>> 
>>> [1]
>>> http://openjpa.apache.org/builds/latest/docs/manual/manual.html#ref_guide_locking_issues
>>> 
>>> 
>>> 
>> 
>> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2284778.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Pinaki Poddar <pp...@apache.org>.
Hi Philip,
  I did not execute the test case but reading it gave me the impression that
it is exercising/exposing the following known limitation. But may be I
should analyze the test case deeper to understand what is causing this
behavior.

1. "In order to maintain reasonable performance levels when loading object
state, OpenJPA can only guarantee that an object is locked at the proper
lock level after the state has been retrieved from the database. This means
that it is technically possible for another transaction to "sneak in" and
modify the database record after OpenJPA retrieves the state, but before it
locks the object. The only way to positively guarantee that the object is
locked and has the most recent state to refresh the object after locking
it."
 
  2. On VersionLockManager (which is the default and being used in this
case): "This lock manager does not perform any exclusive locking, but
instead ensures read consistency by verifying that the version of all
read-locked instances is unchanged at the end of the transaction."

  3. Another possibly relevant point: "In order for the version lock manager
to prevent the dirty read phenomenon, the underlying data store's
transaction isolation level must be set to the equivalent of "read
committed" or higher." 



Philip Aston wrote:
> 
> Thanks Pinaki.
> 
> I don't think the documented known issues cover my case. The first bullet
> point is about the pessimistic lock manager, which I'm not using. The
> second bullet is about the read consistency of the locked object - this
> isn't the problem either. The last paragraph is again about the
> pessimistic lock manager. And I don't see any of the INFO level messages
> referred to.
> 
> I find the documented issues to be reasonable, but I'm afraid I can't say
> the same of the read locking implementation.
> 
> - Phil
> 
> 
> Pinaki Poddar wrote:
>> 
>> Hi Philip,
>>    The points you raise are valid, correct and known. Please refer to
>> [1].
>> 
>>    By the way, the test case is very neat!
>> 
>> [1]
>> http://openjpa.apache.org/builds/latest/docs/manual/manual.html#ref_guide_locking_issues
>> 
>> 
>> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2284592.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@mail.com>.
Thanks Pinaki.

I don't think the documented known issues cover my case. The first bullet
point is about the pessimistic lock manager, which I'm not using. The second
bullet is about the read consistency of the locked object - this isn't the
problem either. The last paragraph is again about the pessimistic lock
manager. And I don't see any of the INFO level messages referred to.

I find the documented issues to be reasonable, but I'm afraid I can't say
the same of the read locking implementation.

- Phil


Pinaki Poddar wrote:
> 
> Hi Philip,
>    The points you raise are valid, correct and known. Please refer to [1].
> 
>    By the way, the test case is very neat!
> 
> [1]
> http://openjpa.apache.org/builds/latest/docs/manual/manual.html#ref_guide_locking_issues
> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2284440.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Pinaki Poddar <pp...@apache.org>.
Hi Philip,
   The points you raise are valid, correct and known. Please refer to [1].

   By the way, the test case is very neat!

[1]
http://openjpa.apache.org/builds/latest/docs/manual/manual.html#ref_guide_locking_issues


-- 
View this message in context: http://n2.nabble.com/Is-the-implementation-of-lock%28LockModeType.READ%29-correct--tp2272546p2284285.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Is the implementation of lock(LockModeType.READ) correct?

Posted by Philip Aston <ph...@oracle.com>.

There's an error in my original test case - it can fail the assertion
due to writes of the same value being optimised away. This is why it
failed so fast. I've fixed the test case, but now it fails only very rarely.

This prompted me to write a separate test case (imaginatively titled
Test2), that doesn't rely on lucky timing. This fails every time with
OpenJPA 1.1 and 1.2, so long as the entity class is enhanced. The test
case passes using EclipseLink.

Updates attached.

Should I open a bug?

- Phil


Philip Aston wrote:
> Hello OpenJPA people,
>
> I don't think the OpenJPA implementation of lock(LockModeType.READ) is
> correct. Please tell me where I'm mistaken.
>
>
> JPA 1.0:
>
>     If transaction T1 calls lock(entity, |LockModeType.READ|
>     <http://java.sun.com/javaee/5/docs/api/javax/persistence/LockModeType.html#READ>)
>     on a versioned object, the entity manager must ensure that neither
>     of the following phenomena can occur:
>
>         ...
>
>           *P2 (Non-repeatable read): Transaction T1 reads a row.
>           Another transaction T2 then modifies or deletes that row,
>           before T1 has committed. Both transactions eventually commit
>           successfully. * 
>
>     *Lock modes must always prevent the phenomena ... P2.*
>
>
> An application should be able to read lock a row from which it only
> reads (say R1), and be sure that it will be consistent with a row it
> both reads and writes (say R2). That is, T1 will commit and R2 will be
> updated if and only if R1 has not been changed.
>
> I would expect an implementation to remember the version of R1 at the
> time the lock is acquired, then perform an additional version check
> (additional SELECT) on R1 after doing the UPDATE to R2, rolling back
> the transaction if the R1 version has changed.
>
> This is not how I observe OpenJPA 1.1 or 1.2 behaving. When the
> application calls commit() on T1, OpenJPA does the additional check,
> but does so before the UPDATE. This presents a window between the
> SELECT and the UPDATE where evil T2 can overwrite R1 and commit,
> before T1 commits and changes the value of R2. Although T1 committed
> last, its read lock has been ignored and R1 is now inconsistent with R2.
>
> Test case attached. This fails pretty quickly (usually sub-second,
> occasionally a minute or two) on my dual core machine against a local
> Oracle database. A couple of further observations:
>
>    1. There are no problems with LockModeType.WRITE. If you change the
>       test case to use that, it will run and run. I contend that
>       LockModeType.WRITE should not be required for correct behaviour
>       for this test case.
>    2. It "works" if I run with non-enhanced classes, since then there
>       is no change detection and all rows get written and version checked.
>
>
> - Phil
>
>