PESSIMISTIC_READ lock on same entity cannot be acquired by 2 services - jpa

From JPA documentation, PESSIMISTIC_READ lock on an entity can be acquired if no one is holding a PESSIMISTIC_WRITE lock on the entity. However, as I tested using OpenJPA 2.0.0, WebSphere and MSSQL (as well as DB2), it seems that 2 services cannot acquire PESSIMISTIC_READ lock on the same entity at the same time.
This code (in ConfigEJB) was used to lock the entity:
ConfigEntity configEntity = this.getEntityById(1); // successfully get the entity
this.entityManager.lock(configEntity, LockModeType.PESSIMISTIC_READ);
2 instances of ConfigEJB were invoked. The first instance could acquire the lock successfully. However, the second instance couldn't get the lock, and was blocked until the first instance finished its transaction (I expected it to successfully get the lock).
Has anyone ever encountered this problem? Or is this an expected behavior of JPA? How to let the services obtain PESSIMISTIC_READ locks properly?

Not found in reference, but Pro JPA2 saying:
Some databases support locking mechanisms to get repeatable read isolation without acquiring a write lock. A PESSIMISTIC_READ mode can be used to pessimistically achieve repeatable read semantics when no writes to the entity are expected. The fact that this kind of situation will not be encountered very often, combined with the allowance that providers have of implementing it using a pessimistic write lock, leads us to say that this mode is not one to be easily picked up and commonly used.
So, seems your database provider replace PESSIMISTIC_READ with PESSIMISTIC_WRITE

Related

Hibernate JPA LockModeType NONE is locking the row [duplicate]

With JPA, we can use manually OPTIMISTIC or PESSIMISTIC locking to handle entity changes in transactions.
I wonder how JPA handles locking if we don't specify one of these 2 modes ?
No locking mode is used?
If we don't define an explicit locking mode, can the database integrity be lost?
Thanks
I've scanned through section 3.4.4 Lock Modes of the Java Persistence API 2.0 Final Release specification and while I couldn't find anything specific (it doesn't state that this is the default or anything like that) there is a footnote which says the following.
The lock mode type NONE may be specified as a value of lock mode
arguments and also provides a default value for annotations.
The section is about the kinds of LockModeType values available and their usages and describes which methods takes an argument of this kind and whatnot.
So, as it said LockModeType.NONE is default for annotations (JPA, annotations left and right) I guess when you use EntityManager.find(Class, Object) the default LockModeType is used.
There are some other, subtle, hints to reinforce this. Section 3.1.1 EntityManager interface.
The find method (provided it is invoked without a lock or invoked with
LockModeType.NONE) and the getReference method are not required to be
invoked within a transaction context.
It makes sense. For example if you use MySQL as your database and your database engine of choice is InnoDB then (by default) your tables will use REPEATABLE READ, if you use some other RDBMS or other database engines this could change.
Right now I'm not exactly sure that isolation levels has anything to do with JPA lock modes (although it seems that way), but my point is that different database systems differ so JPA can't decide for you (at least according to the specification) what lock mode to use by default, so it'll use LockModeType.NONE if you don't instruct it otherwise.
I've also found an article regarding isolation levels and lock modes, you might want to read it.
Oh, and to answer your last question.
If we don't define an explicit locking mode, can the database
integrity be lost?
It depends, but if you have concurrent transactions then the answer is probably yes.
Due to JPA 2.1 FR
3.2 Version Attributes
The Version field or property is used by the persistence provider to perform optimistic locking. It is accessed and/or set by the persistence provider in the course of performing lifecycle operations on the entity instance. An entity is automatically enabled for optimistic locking if it has a property or field mapped with a Version mapping.
So if the entity is a versioned object, such as a #Version has been specified, then the default persistence provider will perform optimistic locking.
In the specification persistence_2.0, page 89:
If a versioned object is otherwise updated or removed, then the implementation must ensure that the
requirements of LockModeType.OPTIMISTIC_FORCE_INCREMENT are met, even if no explicit
call to EntityManager.lock was made.

What's the difference between DbUpdateConcurrencyException and 40001 postgres code?

I have two transactions.
In first I select an entity, do validations, upload provided by client file to S3 and then update this entity with info about S3 file.
Second transaction is simply deleting this entity.
Now, assume that someone called first transaction and immediately second. Second one will proceed faster and first one will throw DbUpdateConcurrencyException, as selected entity no longer exists on update query.
I get DbUpdateConcurrencyException, when my transaction has IsolationLevel.ReadCommited. But if I set IsolationLevel.Serializable it throws InvalidOperationException with 40001 postgres code. Could someone explain why do I get different errors, because it seems to me that outcome should be the same, as both errors invoked by updating non-existing entity?
The 40001 error corresponds to the SQLSTATE serialization_failure (see the table of error codes).
It's generated by the database engine in serializable isolation level when it detects that there are concurrent transactions and this transaction may have produced a result that could not have been obtained if the concurrent transactions had been run serially.
When using IsolationLevel.ReadCommited, it's impossible to obtain this error, because choosing this level of isolation precisely means that the client-side doesn't want to have these isolation checks being done by the database.
On the other hand, the DbUpdateConcurrencyException is probably not generated by the database engine. It's generated by the entity framework. The database itself is fine with an UPDATE updating zero row, it's not an error at the SQL level.
I think you get the serialization failure if the database errors out first, and the DbUpdateConcurrencyException error if the database doesn't error out, but the second layer in the order of layering (the EF) does.
The typical way to deal with serialization failures, at the serializable isolation level, is for the client-side to retry the transaction when it gets a 40001 error. The retried transaction will have a fresh view of the data and hopefully will pass (otherwise, loop on retrying).
The typical way to deal with concurrency at lesser isolation levels like Read Committed it to explicitly lock objets before accessing them to force the serialization of concurrent transactions.

When does flush and clear commit?

I'm using JPA EclipseLink 2.0 with Glassfish 3.1.2.2
I want to know if after I call
em.flush()
em.clear()
The objects are immediatly commited to the database. My problem is I'm doing so many transactions that I'm getting OutOfMemory. I want to avoid this by flushing the transaction's objects.
After I flush and clear, I can't see any immediate entity commited to the database, I can only see them AFTER the whole process is done, which tells me this isn't actually commiting.
If flush and clear doesn't commit:
1) What does it actually do?
2) Why am I no longer getting OutOfMemory?
Please tell me if I'm right:
The objects that were allocated in my RAM are sent to the database, but changes are not yet commited. This only means I cleared my RAM, the objects are now in the DB server but the transaction is not yet commited.
Entities are synchronized to the connected database at transaction commit time. If you only have n = 1 ongoing transaction (here: JTA/container managed), changes on one or more entities get written to the DB the moment you call flush() on the EntityManager instance.
However, changes become "visible" only after the transaction has been properly executed by the container (here: Glassfish) which is responsible for transaction handling. For reference, see. section 7.6.1 (p. 294) of JPA Spec 2.0 which defines:
A new persistence context begins when the container-managed entity manager is invoked (Specifically, when one of the methods of the EntityManager interface is invoked) in the scope of an active JTA transaction, and there is no current persistence context already associated with the JTA transaction. The persistence context is created and then associated with the JTA transaction.
The persistence context ends when the associated JTA transaction commits or rolls back, and all entities that were managed by the EntityManager become detached.
In section 3.2.4 (Synchronization to the Database) of the JPA Spec 2.0 we find:
The state of persistent entities is synchronized to the database at transaction commit.
[..]
The persistence provider runtime is permitted to perform synchronization to the database at other times as well when a transaction is active. The flush method can be used by the application to force synchronization.
It applies to entities associated with the persistence context. The EntityManager and Query setFlushMode methods can be used to control synchronization semantics. The effect of FlushModeType.AUTO is defined in section 3.8.7. If FlushModeType.COMMIT is specified, flushing will occur at transaction commit; the persistence provider is permitted, but not required, to perform to flush at other times. If there is no transaction active, the persistence provider must not flush to the database.
Most likely in your scenario, the container (Glassfish) and/or your application is configured for FlushModeType.COMMIT(*1). In case FlushModeType.AUTO is in place, it is up to the Persistence Provider (EclipseLink) which "is responsible for ensuring that all updates to the state of all entities in the persistence context which could potentially affect the result of the query are visible to the processing of the query." (Section 3.8.7, p. 122)
By contrast, the clear() method does NOT commit anything by itself. It simply detaches all managed entities from the current persistence context, thus causing any changes on entities which have not been flushed (committed) to get lost. For reference, see p. 70 of the linked JPA Spec.
With respect to the OutOfMemoryError, it's hard to tell what's causing this under which circumstances, as you did not provide much detail either. However, I would:
read the aforementioned sections of the JPA specification
check how your environment is configured and
reevaluate how your application is written/implemented, potentially making false assumptions on the transaction handling of the container it is running in.
Related to 2., you might check your persistence.xml whether it configures
<property name="eclipselink.persistence-context.flush-mode" value="COMMIT" />
and change it to AUTO to see if there is any difference.
Hope it helps.
Footnotes
*1: But that's a good guess, as you did not provide that much detail on your setup/environment.
On JPA transaction commit, JPA is doing flush automatically. You should see object in DB right after first transaction end, not only after whole process end. Check if really do more transactions or just one.

How to set isolation level in #Transactional "READ_UNCOMMITTED". I am using EclipseLink 2.5.1-RC1

I have a requirement to start new Transaction within an ongoing Transaction so that an exception in 2nd transaction will rollback only new transaction not the old one.
This I am doing by setting propagation attribute in 2nd transaction like this:
#Transactional(propagation = Propagation.REQUIRES_NEW)
This created a new Transaction, but the new Transaction needs to read some uncommitted data of the first transaction (dirty read), and also update that data. This I am trying to do by setting isolation attribute as :
#Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.READ_UNCOMMITTED)
This throws an Exception - InvalidIsolationLevelException, saying "Standard JPA does not support custom isolation levels - use a special JpaDialect for your JPA implementation".
Can any help me to implement JpaDialect? I am using Eclipse Link 2.5.1 .
Or can I some how close the first transaction before starting a new transaction? Since First transaction is closed, the Second will have no problem reading the data committed by First Transaction.
In JPA you can try somehting like this, not much certain on how to achieve similar in EclipseLink/Spring.
But the overall concept might remain same, get the underlying database connection & set appropriate isolation level.
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class);
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
//-- Or with some other constant something like Isolation.READ_UNCOMMITTED
Afterwards, you might also want to reset the isolation level back to the default.
If you don't want to make changes, then you might need to implement JpaDialect overriding methods to accommodate the change for isolation level in transaction.
You can refer here which describes implementation for Hibernate, can try similar for EclipseLink.

WF4 TransactionScope containing several custom activities with EF4 database updates

I have created several custom activities that update tables in my DB (in this case SQL Server Compact), using Entity Framework 4 with POCOs.
If I put more than one of these inside a WF4 TransactionScope activity, I'm running into problems: EF disposes the DB connection after the first activity has finished, and when the next DB activity tries to do a DB update a new connection is built up. At this moment an exception is thrown.
System.Activities.WorkflowApplicationAbortedException : The workflow has been aborted.
----> System.Data.EntityException : The underlying provider failed on Open.
----> System.InvalidOperationException : The connection object can not be enlisted in transaction scope.
Do I have to keep the EF connection open during the whole transaction scope? How can I do that? Create an explicit custom activity for that, or is there a standard way?
My current workaround goes like this: I created a new code activity that creates our ObjectContext and explicitely calls dbContext.Connection.Open(). It returns the ObjectContext, which is then saved in a workflow variable. That one is passed to all the DB related activities as an InArgument<>. Inside my DB activities, I use this ObjectContext if it is passed in, otherwise I create a new one.
This does work, but I'm not satisfied with this solution: It needs the new InArgument for every DB related activity. In the workflow designer, I have to insert that special OpenDatabaseConnection activity inside the transaction scope, and then make sure that the correct variable is passed into all DB activities. This seems to be very inelegant and error prone, especially if other team members have to use these DB activities.
What would be a better way to handle this?
The problem is that when you open a second connection in the same transaction scope, an attempt is made to promote the transaction to a distributed transaction (even though there's nothing distributed about it since you connect to the same database). SQL Server CE doesn't support this scenario.
What I would do is create a custom 'container' activity that opens (and closes) the connection and makes it available to child activities. This is still not optimal but at least you no longer need to pass InArgument's around. You get the following activity tree:
TransactionScope
InitializeConnection
Sequence
CustomDataActivity1
CustomDataActivity2
CustomDataActivity3
InitializeConnection is a NativeActivity that uses NativeActivityContext.Properties to expose the connection (or the ObjectContext) to child activities.
Make sure you implement proper error handling to ensure you close the connection at all times.
NOTE: Distributed transactions are supported by the full SQL Server only through a Windows service called MSDTC (Microsoft Distributed Transaction Coordinator). You can find this one in your 'Local Services'. Since SQL Server CE is a database that should be able to operate completely standalone, it makes sense that it has no dependency on MSDTC. Therefore it has no support for distributed transactions.