mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Refinements to RetryingTransactionHelper to make it equivalent to
executeInUserTransaction(). Changed the on close callback for write listeners to use a RetryingTransaction. The point of this exercise is to make it possible for clients of the core server to ignore transient resource contention failures. CIFS, for example, will be able to take advantage of this, since a transient error condition currently results in a dead share. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4597 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -5,6 +5,8 @@ package org.alfresco.repo.transaction;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import javax.transaction.RollbackException;
|
||||
import javax.transaction.Status;
|
||||
import javax.transaction.SystemException;
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
@@ -79,25 +81,38 @@ public class RetryingTransactionHelper
|
||||
* Execute a callback in a transaction until it succeeds, fails
|
||||
* because of an error not the result of an optimistic locking failure,
|
||||
* or a deadlock loser failure, or until a maximum number of retries have
|
||||
* been attempted. NB that this ignores transaction status and relies entirely
|
||||
* on thrown exceptions to decide to rollback. Also this is non-reentrant, not
|
||||
* to be called within an existing transaction.
|
||||
* been attempted.
|
||||
* @param cb The callback containing the unit of work.
|
||||
* @param readOnly Whether this is a read only transaction.
|
||||
* @return The result of the unit of work.
|
||||
*/
|
||||
public Object doInTransaction(Callback cb, boolean readOnly)
|
||||
{
|
||||
// Track the last exception caught, so that we
|
||||
// can throw it if we run out of retries.
|
||||
RuntimeException lastException = null;
|
||||
for (int count = 0; fMaxRetries < 0 || count < fMaxRetries; ++count)
|
||||
{
|
||||
UserTransaction txn = null;
|
||||
boolean isNew = false;
|
||||
try
|
||||
{
|
||||
txn = fTxnService.getNonPropagatingUserTransaction(readOnly);
|
||||
txn.begin();
|
||||
txn = fTxnService.getUserTransaction(readOnly);
|
||||
// Do we need to handle transaction demarcation. If
|
||||
// no, we cannot do retries, that will be up to the containing
|
||||
// transaction.
|
||||
isNew = txn.getStatus() == Status.STATUS_NO_TRANSACTION;
|
||||
if (isNew)
|
||||
{
|
||||
txn.begin();
|
||||
}
|
||||
// Do the work.
|
||||
Object result = cb.execute();
|
||||
txn.commit();
|
||||
// Only commit if we 'own' the transaction.
|
||||
if (isNew)
|
||||
{
|
||||
txn.commit();
|
||||
}
|
||||
if (fgLogger.isDebugEnabled())
|
||||
{
|
||||
if (count != 0)
|
||||
@@ -107,13 +122,29 @@ public class RetryingTransactionHelper
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Throwable e)
|
||||
{
|
||||
// Somebody else 'owns' the transaction, so just rethrow.
|
||||
if (!isNew)
|
||||
{
|
||||
if (e instanceof RuntimeException)
|
||||
{
|
||||
throw (RuntimeException)e;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Unknown Exception.", e);
|
||||
}
|
||||
}
|
||||
// Rollback if we can.
|
||||
if (txn != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
txn.rollback();
|
||||
if (txn.getStatus() != Status.STATUS_ROLLEDBACK)
|
||||
{
|
||||
txn.rollback();
|
||||
}
|
||||
}
|
||||
catch (IllegalStateException e1)
|
||||
{
|
||||
@@ -128,12 +159,22 @@ public class RetryingTransactionHelper
|
||||
throw new AlfrescoRuntimeException("Failure during rollback.", e1);
|
||||
}
|
||||
}
|
||||
// This handles the case of an unexpected rollback in
|
||||
// the UserTransaction.
|
||||
if (e instanceof RollbackException)
|
||||
{
|
||||
RollbackException re = (RollbackException)e;
|
||||
e = re.getCause();
|
||||
}
|
||||
// These are the 'OK' exceptions. These mean we can retry.
|
||||
if (e instanceof ConcurrencyFailureException ||
|
||||
e instanceof DeadlockLoserDataAccessException ||
|
||||
e instanceof StaleObjectStateException ||
|
||||
e instanceof LockAcquisitionException)
|
||||
{
|
||||
lastException = (RuntimeException)e;
|
||||
// Sleep a random amount of time before retrying.
|
||||
// The sleep interval increases with the number of retries.
|
||||
try
|
||||
{
|
||||
Thread.sleep(fRandom.nextInt(500 * count + 500));
|
||||
@@ -144,6 +185,7 @@ public class RetryingTransactionHelper
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// It was a 'bad' exception.
|
||||
if (e instanceof RuntimeException)
|
||||
{
|
||||
throw (RuntimeException)e;
|
||||
@@ -151,6 +193,8 @@ public class RetryingTransactionHelper
|
||||
throw new AlfrescoRuntimeException("Exception in Transaction.", e);
|
||||
}
|
||||
}
|
||||
// We've worn out our welcome and retried the maximum number of times.
|
||||
// So, fail.
|
||||
throw lastException;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user