The Source for Java Technology Collaboration

Home » java.net Forums » GlassFish » GlassFish

Thread: TopLink Essentials performance in GF v2b58 when using pessimistic locking

Welcome, Guest Help
Login Login
Guest Settings Guest Settings
This question is not answered. Helpful answers available: 1. Correct answers available: 1.

Reply to this Thread Reply to this Thread Search Forum Search Forum Back to Thread List Back to Thread List

Permlink Replies: 1 - Last Post: Oct 21, 2008 1:38 PM by: chris_delahunt
xj

Posts: 8
TopLink Essentials performance in GF v2b58 when using pessimistic locking
Posted: Oct 20, 2008 12:14 AM
 
  Click to reply to this thread Reply

A large amount of SQL is issued and the performance of TopLink remarkably deteriorates when executing JPQL with pessimistic lock compared with the case where pessimistic locking is not used.

It seems TopLink does not use cache when pessimistic locking is used and SQL for the number of the rows of table is issued.

An example that has ManyToOne relationship between Employee and Department. Below are the numbers of the rows in these tables.

Emplyee : 80000
Department: 2

When SELECT to Emplyee(all rows) was issued, the SQL issue number to the Department became the following results.

pessimistic locking : 80000
non pessimistic locking: 2

The result shows a large amount of access to DB occurred when pessimistic locking is used and it led to poor performance.

Although by specifying @ManyToOne(fetch = FetchType.LAZY) can work around this problem. Looking into the source code, it seems it is
intended not to use cache when using pessimistic locking.

-->
[ObjectBuilder.buildObjectInUnitOfWork]
/**
* For executing all reads on the UnitOfWork, the session when building
* objects from rows will now be the UnitOfWork. Usefull if the rows were
* read via a dirty write connection and we want to avoid putting uncommitted
* data in the global cache.
*
* Decides whether to call either buildWorkingCopyCloneFromRow (bypassing
* shared cache) or buildWorkingCopyCloneNormally (placing the result in the
* shared cache).
*/
protected Object buildObjectInUnitOfWork(ObjectBuildingQuery query, JoinedAttributeManager joinManager, AbstractRecord databaseRow, UnitOfWorkImpl unitOfWork, Vector primaryKey, ClassDescriptor concreteDescriptor) throws DatabaseException, QueryException {
// When in transaction we are reading via the write connection
// and so do not want to corrupt the shared cache with dirty objects.
// Hence we build and refresh clones directly from the database row.
if ((unitOfWork.getCommitManager().isActive() || unitOfWork.wasTransactionBegunPrematurely()) && !unitOfWork.isClassReadOnly(concreteDescriptor.getJavaClass())) {
// It is easier to switch once to the correct builder here.
return concreteDescriptor.getObjectBuilder().buildWorkingCopyCloneFromRow(query, joinManager, databaseRow, unitOfWork, primaryKey);
}

return buildWorkingCopyCloneNormally(query, databaseRow, unitOfWork, primaryKey, concreteDescriptor, joinManager);
}

[ObjectBuilder.buildWorkingCopyCloneFromRow]
/**
* INTERNAL:
* Builds a working copy clone directly from the database row.
* This is the key method that allows us to execute queries against a
* UnitOfWork while in transaction and not cache the results in the shared
* cache. This is because we might violate transaction isolation by
* putting uncommitted versions of objects in the shared cache.
*/
protected Object buildWorkingCopyCloneFromRow(ObjectBuildingQuery query, JoinedAttributeManager joinManager, AbstractRecord databaseRow, UnitOfWorkImpl unitOfWork, Vector primaryKey) throws DatabaseException, QueryException {
// If the clone already exists then it may only need to be refreshed or returned.
// We call directly on the identity map to avoid going to the parent,
// registering if found, and wrapping the result.
Object workingClone = unitOfWork.getIdentityMapAccessorInstance().getIdentityMapManager().getFromIdentityMap(primaryKey, getDescriptor().getJavaClass(), getDescriptor());

// If there is a clone, and it is not a refresh then just return it.
boolean wasAClone = workingClone != null;
boolean isARefresh = query.shouldRefreshIdentityMapResult() || (query.isLockQuery() && (!wasAClone || !query.isClonePessimisticLocked(workingClone, unitOfWork)));
if (wasAClone && (!isARefresh)) {
return workingClone;
}
<--

The above code and JavaDoc shows the method buildWorkingCopyCloneFromRow() is called by design that does not use cache compared with calling the method buildWorkingCopyCloneNormally().

Could someone tell me the background why cache is not used when using pessimistic locking.

Thanks in advance.

Message was edited by: xj

chris_delahunt

Posts: 153
Re: TopLink Essentials performance in GF v2b58 when using pessimistic locking
Posted: Oct 21, 2008 1:38 PM   in response to: xj
Helpful
  Click to reply to this thread Reply

Hello,
From my understanding, pessimistic locks are done through transactions so that they are released if the transaction commits/rolls back. Since Reads are now done through the transaction rather than the shared read connections, the transaction is marked as started early (from TopLink essentials point of view, not a JTA one), and so the shared cache is not used. This avoids problems where say a native update query was done in the transaction making data in the shared cache unreliable. There is no mechanism to keep track of what was done through the transaction, so once it is marked as started, the shared cache isn't used for reads.

Pessimistic locking is not the most performant option; optimistic locking is prefered. If you are in a highly concurrent environment where pessimistic locking is required, then bypassing the shared cache to ensure the object is refreshed is extra overhead but might be required anyway when multiple applications are involved - you are assuming that others are updating the same row from under you which might have made the shared cache data stale.

There are definetely situations where this is not be desirable, so I recommend filing a feature request that would allow pessimistic locking queries to access the shared cache.

Hope this helps understand it, though I don't have a workaround other than to use optimistic locking instead where ever possible.

Best Regards,
Chris




 XML java.net RSS