Assaf Arkin wrote:
> So the question is, how should one implement this? Surely we should beI dont think that is correct. Oracle provides for row level locking. There is no update in ejbLoad so no locking occurs. Locking only occurs when you update a row. See the following from the Oracle site:
> able to reach a consensus on this rather simple question?First, I would like to correct something. If you use serializable then
the second ejbLoad will block until the first transaction commits, so
you do get the results you expect to. You also get lock contention.
Row-Level Locking
Both read committed and serializable transactions use row-level locking,
and both will wait if
they try to change a row updated by an uncommitted concurrent transaction.
The second
transaction that tries to update a given row waits for the other
transaction to commit or roll
back and release its lock. If that other transaction rolls back,
the waiting transaction
(regardless of its isolation mode) can proceed to change the previously
locked row, as if the
other transaction had not existed.
However, if the other (blocking) transaction commits and releases
its locks, a read
committed transaction proceeds with its intended update. A serializable
transaction,
however, fails with the error "Cannot serialize access", because
the other transaction has
committed a change that was made since the serializable transaction
began.
Referential Integrity
Because Oracle does not use read locks in either read-consistent
or serializable transactions,
data read by one transaction can be overwritten by another. Transactions
that perform
database consistency checks at the application level should not
assume that the data they
read will remain unchanged during the execution of the transaction
(even though such
changes are not visible to the transaction). Database inconsistencies
can result unless such
application-level consistency checks are coded with this in mind,
even when using
serializable transactions.
But, if you opened a connection from a session bean with read committed,
then ran the entity bean from it, you're still in read committed, so the
serializable doesn't work for you.This is where the EJB server should allow you to mark the entity bean as
exclusive access and maintain a lock, blocking B until A finishes the
transaction. I'm working on code that does just that right now, and it's
not that hard.Once again, lock contention.
If you're looking for performance (and scalability and everything else)
you should consider either:1. Doing no 2) (in UPDATE from the entity bean), or
2. Using a stateless session bean (my favorite)
You must always assume the EJB entity bean mantra of load-modify-commit,
which either gets you into trouble if you don't pay attention to
locking, or slows down your application if you do too much locking.arkin
dan benanav wrote:
>
> I have a question about how to implement something using EJB. I am sure
> that this problem is very common and that it has come up in discussions
> in various forms on this list, but I don't think the right approach has
> been clarified. This is surprising since the spec should be able to
> handle such a situation rather easily. It appears to me that many
> people have a misunderstanding about EJB that leads to an incorrect way
> to solve this problem. Including in that misunderstanding is the Sun
> BluePrints guide. In the following I will assume we are talking EJB 1.1
> spec and I am interested in a solution that conforms to that spec.
>
> Problem: Suppose you have an Account class that is the remote interface
> to an Account bean. AccountBean is the entity bean implementation
> class. You want to write a method increment(int x) that adds x to the
> balance on the account represented by the Account instance. There are 2
> approaches that come to mind about how to do this. The first is the
> most obvious but I believe there are problems with it.
>
> 1) public void increment(int x) {balance += x;};
>
> 2) public void increment(int x) { //use jdbc to increment the balance in
> the db using sql like "updateaccount_table set balance = balance + ?
> where account_key = ?". The first question mark is set to x and the
> second question mark is set to the primary key of the bean.}
>
> Why isn't 1) good? Containers are free to use multiple bean instances
> to handle concurrent calls to an entity object as long as those calls
> are occurring under a different transaction. This means that you can
> have bean instance A and bean instance B both handling calls to
> increment x, where both A and B represent the same entity object (same
> primary key). Some containers my choose not to do this, however there
> is no guarantee that the container provider won't change their approach
> in the future. Furthermore if you rely on the container not doing that
> your code will not work on any EJB server. This would make it difficult
> for EJB bean providers (like the Theory Center) for providing server or
> container independent beans. So if you agree now that your code should
> work if the Container provider uses multiple bean instances then I think
> there is a problem. Suppose that in two separate threads calls are
> made to an entity object. One thread calls increment(1) and the other
> increment(2). These calls occur concurrently. Assume also that the
> initial balance before the calls is 3. After the call the new balance
> should be 6. Here is what can occur:
>
> T1:Container calls ejbLoad on Bean A: Balance gets set to 3.
> T2:Container calls ejbLoad on Bean B: Balance still is 3. (This
> happens in a separate thread and separate transaction context).
> T3:Container calls increment(1) on Bean A: Balance gets set to 4 in Bean
> A.
> T4:Container calls increment(2) on Bean B: Balance gets set to 5 in Bean
> B.
> T5:Container calls ejbStore on Bean A: balance in database is updates to
> 4.
> T6:Container calls ejbStore on Bean B: what happens??
>
> What happens may depend on what the Transaction isolation level is and
> perhaps on the underlying database. If you use Oracle and the default
> transaction isolation level (read committed) then the balance gets set
> to 5 in the database. (The wrong answer!). If you use serializable
> transaction isolation level then an exception is thrown and the client
> will have to react appropriately to the exception. So it would appear
> that the only way 1) could possibly work (in some sense) is to use
> transaction level serializable. So 1) with transaction serializable is
> an approach but it is incompletely specified since we haven't said how
> the client should handle this. I imagine there are ways to handle it.
> (Catch the exception and try again for example). Furthermore I have
> been told by WLS not to use transaction serializable due to a bug in
> Oracle so 1) does not currently work for Oracle. Even if this would
> work it seems like it would be better to just force the container or to
> specify in the deployment descriptor that calls to increment should be
> serialized. Currently there is no way to do that in the spec.
>
> The second solution would work however there are also problems with it.
> You need to be careful to make sure that when ejbStore is called the
> correct value is stored in the database. For example, you cannot just
> increment the value in the db. You would also need to set balance in the
> bean appropriately.
>
>
> dan
>
> ===========================================================================
> To unsubscribe, send email to [EMAIL PROTECTED] and include in the body
> of the message "signoff EJB-INTEREST". For general help, send email to
> [EMAIL PROTECTED] and include in the body of the message "help".--
----------------------------------------------------------------------
Assaf Arkin www.exoffice.com
CTO, Exoffice Technologies, Inc. www.exolab.org
