|
Replies:
25
-
Last Post:
Nov 12, 2008 10:10 AM
by: vladbalan
|
|
|
|
|
|
|
How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 2:33 AM
|
|
|
Hi,
i'm trying to understand the behaviour of the EntityManager.lock() call. The Sun documentation ( http://java.sun.com/javaee/5/docs/api/javax/persistence/LockModeType.html) on this is a bit confusing for me and i will explain why. It says that if T1 calls EntityManager.lock() on an entity, then T2 couldn't make a dirty read. Also i read here (http://www.nabble.com/JPA-locking-td19525631.html) that " The lock() API acquires an optimistic lock, not a pessimistic lock. It means that the version will be checked, or updated on commit, it does not matter when it is called in the transaction, as the check occurs on commit " So it does not matter when the lock call is made. Ok, now let's imagine the following typical scenario of the dirty read in a time-point follow-up: T1 T2 --------------------------------------------------- T1: W(edit some entity E) T2: R(reads E) T2: commit T1: lock(E) T1: commit --------------------------------------------------- So there is nothing from preventing T2 to commit in this concurrent transaction scenario and thus making a dirty read. So my guess is: - either the javadoc would be wrong presenting that T1 is the one doing the lock() call instead of T2 doing it. - or the lock() call moment is infact important and, in this case, doing it before the W(edit) in T1 should solve the problem. Would be nice to give the insights on how this works. Thanks.
Vlad
Message was edited by: vladbalan
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 8:23 AM
in response to: vladbalan
|
 |
Helpful |
|
|
My understanding is as follows:
- The first time you read some object from the database, it gets added into the first-level cache.
- Any subsequent reads of that same object will return the instance in the cache
- The object has a "version" associated with it, for optimistic locking. If you read-lock an object, JPA will ensures that at commit time the object still has the same version number (if not it will fail the commit).
- Here is the hole in this logic: If you're using READ_COMMITTED transaction isolation then there is a time between the beginning of transaction and the time the object is read where it could have been modified and the read lock will not help you. Then again, read locks aren't supposed to help in this case anyway. They are only supposed to ensure that the object remains unchanged since the first time you read it.
- As for your specific example. I believe that you are wrong that it is, strictly speaking, an example of a dirty-read. Remember that from the database's point of view reads happen immediately but writes occur atomically at commit time.
This means that from the database's point of view E's value didn't change until T1 committed. Strictly-speaking, the database could rearrange your transactions as follows:
T1: R(reads E) T2: R(reads E) T2: commit T1: W(edit some entity E) T1: lock(E) T1: commit
- Here is an example of a real dirty read:
T1: read E T2: read E T2: modify E T2: commit T1: modify D based on E's value <-- mistake resulting from a dirty-read T1: commit
- If you were to "T1: lock(E)" then when T1 goes to commit JPA would throw an exception notifying it if the dirty-read.
I hope this answers your question.
Gili
Message was edited by: cowwoc
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 8:58 AM
in response to: cowwoc
|
|
|
Hi Gili
First of all thank you for your post. I see what you mean. But still, in my example there is clearly a dirty-read, at least from the cache point of view: T2 is using a new value updated by T1, value that may never be commited, and also T2 commits before T1 commits. It is matchig the javadoc dirty-read definition.
Waiting for your opinion on this strict scenario at cache level.
Thanks.
Message was edited by: vladbalan
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 9:06 AM
in response to: vladbalan
|
|
|
I'd also add that your example isn't matching the javadoc "phaenomena" P1: it says that the read is taking place after an edit. In your example T1 (it doesen't matter that you inversed them) does not perform a read after T2's edit.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 9:11 AM
in response to: vladbalan
|
|
|
And also, the transaction doing the dirty-read (in your example T1) should be the first to commit before the one doing the edit (according to my initial scenario). So once again, your example is not assuming the scenario i submitted.
would be happy to have your opinion on all this.
Message was edited by: vladbalan
Message was edited by: vladbalan
Message was edited by: vladbalan
Message was edited by: vladbalan
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 9:56 AM
in response to: vladbalan
|
|
|
You are right this is all very confusing.
Looking at http://java.sun.com/javaee/5/docs/api/javax/persistence/LockModeType.html
1) By definition, P1 is impossible under READ_COMMITTED regardless of what JPA does. I'm not sure how one would go about implementing this without READ_COMMITTED...
2) P2 doesn't indicate whether T2 commits before T1 but as far as I can tell it has to, because there is no way of causing T1's commit to fail otherwise.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 10:09 AM
in response to: cowwoc
|
|
|
Gili, could you comment on my exact initial example and confirm it is a dirty-read at cache level. See my above -3 comment. In my example there is nothing preventing T2 to commit, although it used a value updated by T1 that maybe is never commited by T1. T1's lock() call is useless in this case, it takes place after T2's commit (i don't event know if calling lock() before T2's commit would change anything too - see my guess number 2 at the end of my first post). Thanks.
Message was edited by: vladbalan
Message was edited by: vladbalan
Message was edited by: vladbalan
Message was edited by: vladbalan
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 10:27 AM
in response to: vladbalan
|
|
|
> Gili, could you comment on my exact initial example > and confirm it is a dirty-read at cache level. See my > above -3 comment. In my example there is nothing > preventing T2 to commit, although it used a value > updated by T1 that maybe is never commited by T1. > T1's lock() call is useless in this case, it takes > place after T2's commit (i don't event know if > calling lock() before T2's commit would change > anything too - see my guess number 2 at the end of my > first post). Thanks.
I believe you're assuming a transaction isolation level that allows dirty reads. As I've stated, the default isolation level (READ_COMMITTED) doesn't allow this to begin with so you have little to worry about.
Assuming you *are* using a lower isolation level then yes I would agree your example shows a potential dirty read. As I've stated above, I'm not sure how *any* JPA implementation would prevent this from taking place given the current API. It's not clear whether the specification was assuming READ_COMMITTED, some mistake was made or maybe they know of an implementation technique I do not. In any case, I've asked James from EclipseLink to comment on your post. I'll defer to his expertise on this one.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 10:36 AM
in response to: cowwoc
|
|
|
Suppose my transaction isolation level IS READ_COMITTED. What would happen exactly in my example. How and who prevents T2 from commiting. (Lock() is not the one doing it since it doesn't even get the chance to act. T2 is commiting before lock() ever gets called in T1) Thanks.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 10:48 AM
in response to: cowwoc
|
|
|
yes, but we're at the objects(entities) in the persistence context level. So i see it like this: T1 updates E in the persistence context. T2 gets the new value when it reads E from the persistence context.
Maybe is there a similar READ_COMMITED isolation level for entites in the persistence context?
Message was edited by: vladbalan
Message was edited by: vladbalan
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 11:48 AM
in response to: vladbalan
|
|
|
As far as I know, JPA is always based on top of relational databases and as such there is no distinction between the persistence context level and database level. In short, if you are using READ_COMMITTED isolation level, different transactions should never see dirty reads from one another.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 11:57 AM
in response to: cowwoc
|
|
|
If this is the case and given that JPA assumes READ_COMMITED as for its specification (!) then dirty reads are prevented by default without need for calling lock(). The javadoc should be updated to eliminate the phrase who states that it ensures no dirty-reads take place. It has nothing to do with it. Not to say that lock() may not even get the chance to be called before T2 (the one assumed to be doing a dirty read) commits.
Someone should inform them.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 12:00 PM
in response to: vladbalan
|
|
|
I suspect that they're leaving the door open to lower isolation levels which need this sort of locking.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 12:06 PM
in response to: cowwoc
|
|
|
OK, then let's assume READ_UNCOMMITED, the next and last lower isolation level. How would the lock() in T1 prevent T2 from commiting after a dirty-read as in the scenario exposed in the first post?
It cannot in this scenario, the lock() doesn't even get the chance to be called before T2 commits.
Thanks. Waiting your opinion.
Message was edited by: vladbalan
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in JPA
Posted:
Nov 10, 2008 12:12 PM
in response to: vladbalan
|
|
|
I'm not sure how optimistic locking could ever prevent dirty-reads for READ_UNCOMMITTED (regardless of when you actually lock()). Then again, maybe others know. James Sutherland from EclipseLink should know for sure.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in J
Posted:
Nov 11, 2008 4:08 PM
in response to: cowwoc
|
|
|
may be my browser doesn't display your example correctly, but if you call em.lock() in tx1 *after* tx2 has committed, nothing will change. Look at the slides 36-43 from this JavaONE presentation: http://developers.sun.com/learning/javaoneonline/2007/pdf/TS-4902.pdf to understand how the locks are expected to work.
Regards, -marina
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in J
Posted:
Nov 11, 2008 5:35 PM
in response to: mvatkina
|
|
|
marina,
How would you fix vladbalan's original example? Looking at slide 42:
em.lock(d1, WRITE);
em.flush(); //version++ for d1
This looks like it'll issue an SQL "update" on the database which acquires a row-lock on most databases. It doesn't sound like even this approach would help vladbalan since one thread is only reading, not writing.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in J
Posted:
Nov 11, 2008 5:48 PM
in response to: cowwoc
|
|
|
I'm not sure what vladbalan is trying to achieve. TX1 changes are in the em1 persistence context until commit, so TX2 won't see them unless TX1 calls em.flush() after an update (but before TX2 starts) *and* his database is setup for read_uncommitted.
Regards, -marina
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in J
Posted:
Nov 11, 2008 5:57 PM
in response to: mvatkina
|
|
|
I guess part of his question is whether LockModeType is meant to work at all under READ_UNCOMMITTED and if so how. I haven't seen any document that says JPA locks only work for READ_COMMITTED, but I don't see the opposite being said either.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in J
Posted:
Nov 11, 2008 6:07 PM
in response to: cowwoc
|
|
|
This is the spec:
3.4 Optimistic Locking and Concurrency This specification assumes the use of "optimistic locking". It assumes that the databases to which persistence units are mapped will be accessed by implementations using read-committed isolation (or a vendor equivalent in which long-term read locks are not held), and that writes to the database typically occur only when the flush method has been invoked [...] Applications that prefer the use of pessimistic locking may require that database isolation levels higher than read-committed be in effect. The configuration of the setting of such database isolation levels, however, is outside the scope of this specification.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in J
Posted:
Nov 11, 2008 6:12 PM
in response to: mvatkina
|
|
|
Good catch! Too bad the Javadoc doesn't mention this only works for READ_COMMITTED.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in J
Posted:
Nov 11, 2008 11:20 PM
in response to: cowwoc
|
|
|
Marina and Gili,
thanks for your posts. As i said in a previous post, i knew already that READ_COMMITED is assumed by JPA.
BUT then: dirty reads are prevented by default without need for calling lock(). The javadoc should be updated to eliminate the phrase who states that lock() ensures no dirty-reads take place.
Pls give an example of where and how lock() is really useful in preventing a dirty-read (not lost-updates).
Waiting for your opinion.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in J
Posted:
Nov 11, 2008 11:30 PM
in response to: vladbalan
|
|
|
Marina,
I have read the slides. As expected, it is about preventing lost-updates. Not dirty-reading as per the definition stated in the javadoc of LockModeType. There it says about the phaenomena P1: T2 reading an updated but not commited value of T1 and T2 commiting succesfully. This is indeed the definiton of a dirty read.
But if you're under READ_COMMITED this never happens anyway. So lock() has no contribution here.
Pls give your opinion.
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in J
Posted:
Nov 12, 2008 12:05 AM
in response to: vladbalan
|
|
|
The javadoc for the LockModeType was created from the words in the spec (see section 3.4.3 Lock Modes in JPA 1.0) and approved by the spec lead. If you think that the spec needs to be worded differently (and I agree, it is confusing, and this is why we put that lock use section into the JavaONE presentation), I suggest you send your comments to the JPA 2.0 EG (see http://jcp.org/en/jsr/detail?id=317 for the early draft download and the feedback alias).
Regards, -marina
|
|
|
|
|
|
|
|
Re: How does an explicit EntityManager.lock() call prevent dirty reads in J
Posted:
Nov 12, 2008 12:32 AM
in response to: mvatkina
|
|
|
I think it is not about the wording in the specs. There are 2 distinct phaenomena: - dirty-reading : possible only under READ_UNCOMMITED and its dirty-data of the reading transaction is the temporary data of a parallel transction (as perfectly described by P1 in the javadoc of LockModeType). READ_COMMITED is enough to "take care" of this and disallow it - Check the section "Transaction isolation" here (http://edocs.bea.com/workshop/docs81/doc/en/core/index.html). - lost-updates : it's about checking that the initial version of some data you retrieved is not old at commit time, meaning other transaction commited newer versions meanwhile, so the transaction with the old version is not allowed to commit to avoid overwriting the newer values (in case it is doing an edit of that data, but you can still do the checking of the version even if you do only reading of it, just to be sure that the decisions you did based on the old value are not commited, as they could trigger inconssistencies). This phaenomena is possible including under READ_COMMITED. So you need to prevent it. And this is what optimistic locking (or commit-time version-checking) does, by throwing the exception in case the version has become old.
Just for you to see one facet of the diffrence between them, imagine that dirty-reading is no more possible under READ_COMMITED, whereas lost-updates still are even under READ_COMMITED. Also, dirty-reading is about "peeking" at temporary values of some parallel transaction, whereas lost-updates is about making sure, when commiting, that the initial value you retrieved was not modified once or several times by other ended transactions (so it's not even about "peeking") since you read it and until you are at the point to commit.
Marina's and Gili's examples are helpful examples about lost-updates scenarios, not dirty-reading.
LockModeType javadoc says it's preventing dirty-reads. Dirty-reads are prevented by the isolation level itself: READ_COMMITED, with no contribution from lock(). Lock() is indeed useful for version checking, or otherwise put: prevening lost-updates.
|
|
|
|
|