cancel
Showing results for 
Search instead for 
Did you mean: 

Transaction rolled back

hbf
Champ on-the-rise
Champ on-the-rise
Hi,

I need some advice from a Spring transaction guru, it seems: I don't get the following to work


    try {
      UserTransaction transaction = transactionService.getNonPropagatingUserTransaction();
      transaction.begin();
      authenticationService.authenticate(LOGIN, PASSWORD.toCharArray());
      NodeRef nodeRef = new NodeRef("workspace://SpacesStore/rubbish"); // invalid input
      int status = alfrescoContext.getTransaction().getStatus(); // 0
      nodeService.getProperty(nodeRef, ContentModel.PROP_CATEGORIES);
    }
    catch (Exception e)
    {
      int status = alfrescoContext.getTransaction().getStatus(); // (*)
    }
    transaction.commit();

This code is executed in a seperate Tomcat webapp running alongside the Alfresco Web Client webapp.

With a valid node-ref, the code works perfectly. With the invalid node-ref (as above), getProperty() throws an InvalidNodeRefException exception and already at (*), the transaction's status is 1= STATUS_MARKED_ROLLBACK. Spring is probably intercepting, right?

My question: Why does this mark the transaction for rollback? Is it my duty to make sure the node-ref is correct? I would have thought that if I caught the exception, I can fix the problem and try again (for which, of course, I need a transaction that is not yet marked for rollback)?

Regards,
Kaspar
11 REPLIES 11

kevinr
Star Contributor
Star Contributor
You are pretty much correct. Certain types of runtime exceptions - anything that extends AlfrescoRuntimeException in our case - has been configured to be caught by Spring and will cause the current exception to be marked as rollback. Basically if certain exceptions occur then you will have no choice but to start another transaction. However, InvalidNodeRefException is NOT derived from AlfrescoRuntimeException so i would not think this would cause the problem you are having…

Kevin

hbf
Champ on-the-rise
Champ on-the-rise
Thank you very much, Kevin, I guess that will help me track this down further: I'll check what exact exception is thrown that caused the roll-back…

In the meantime: How does the Web Client react if its transaction is rolled back? I've made the experience that once this happens, it starts behaving strangely.

Could it be that the Web Client does not check for rolled back transactions and does not start a new transaction?

Which would mean that you have to take the system down and restart to be able to work again.

Kaspar

hbf
Champ on-the-rise
Champ on-the-rise
I am trying to track down the cause of the problem described in the original post. Does somebody have answer to the following questions?

1. Where exactly is Spring configured to mark a transaction for rollback on AlfrescoRuntimeException?

I've tried

grep  AlfrescoRuntimeException `find HEAD -name '*xml'`

without success. – Is it done via exception catching?

2. I've set a breakpoint on both AlfrescoRuntimeException and InvalidNodeRefException (caught and uncaught) and only the latter breaks for the code in the original post. Still, the transaction gets marked as rollback. Any idea why this could be? In what other way can a transaction be marked as rollback?

3. The strange thing is that after the code from the original post is run in a a webapp running alongside the Web Client, the Web Client itself has a transaction that is marked for rollback – even though these are different threads! As a consequence, users cannot log in anymore without errors and see this:

An error occurred in one of the dashlets.

    * Failed to get to do tasks: javax.transaction.RollbackException: The transaction has already been marked for rollback
    * Failed to get pooled tasks: javax.transaction.RollbackException: The transaction has already been marked for rollback
    * Failed to get all active tasks: javax.transaction.RollbackException: The transaction has already been marked for rollback

Is it true that only the current transaction is marked for rollback and not all?

Thanks,
Kaspar

rivarola
Champ on-the-rise
Champ on-the-rise
Hi Kaspar,

Tu debug this kind of crash, you can set a breakpoint in org.springframework.transaction.support.ResourceHolderSupport, method setRollbackOnly().

hbf
Champ on-the-rise
Champ on-the-rise
Rivarola, thanks a lot for your kind hint. Using it, I found out the following but – unfortunately – am again stuck.

In order to isolate what is going on, I've added the following code to CutNodeEvaluator.java (in Alfresco's SVN code); it is COMPLETE NONSENSE semantically but serves to reproduce the behaviour I am struggling with.


package org.alfresco.web.action.evaluator;

import javax.faces.context.FacesContext;

import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.action.ActionEvaluator;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.web.bean.ml.MultilingualUtils;

import javax.faces.context.FacesContext;
import javax.transaction.UserTransaction;

import org.alfresco.web.app.servlet.FacesHelper;

public class CutNodeEvaluator extends BaseActionEvaluator
{
   public boolean evaluate(Node node)
   {

     // BEGIN for debugging only
     FacesContext fc = FacesContext.getCurrentInstance();

     NodeService nodeService = (NodeService) FacesHelper.getManagedBean(fc, "NodeService");
     TransactionService transactionService = (TransactionService) FacesHelper.getManagedBean(fc, "TransactionService");
     AuthenticationService authenticationService = (AuthenticationService) FacesHelper.getManagedBean(fc, "AuthenticationService");
     try
     {
       UserTransaction transaction = null;
       try
       {
         //transaction = transactionService.getNonPropagatingUserTransaction(); // (**)
         //transaction.begin(); // (**)
         //authenticationService.authenticate("admin", "J8uqw82_".toCharArray()); // (**)
         NodeRef nodeRef = new NodeRef("workspace://SpacesStore/rubbish"); // invalid input
         //int status = transaction.getStatus(); // 0  // (**)
         //System.err.println(status);  // (**)
         nodeService.getProperty(nodeRef, ContentModel.PROP_CATEGORIES);
       }
       catch (Exception e)
       {
         // int status = transaction.getStatus(); // (*)  // (**)
         // System.err.println(status); // (**)
       }
       // transaction.commit();  // (**)
     }
     catch (Exception e)
     {
     }
     // END for debugging only

     // just some dummy return value
     return false;
    }
  }

If you build Alfresco using this code and list the contents of a space in the Web Client with a few content nodes, a InvalidNodeRefException will be thrown. This results in a rollback (break on setRollbackOnly() to see this, as Rivarola suggested). But otherwise, you can log in, log out of the Web Client and everything works.

(Kevin: It seems that Spring is not configured to only rollback on AlfrescoRuntimeException but on RuntimeException's or Error's, see org/springframework/transaction/interceptor/DefaultTransactionAttribute.java, method rollbackOn(). At least, in my case, the latter method gets called…)

Nowthe strange behaviour: If you uncomment the 8 lines with (**) and rebuild, the transactions are also rolled back but you cannot log in/log out anymore without errors showing up: The transaction has already been marked for rollback, etc.

What am I doing wrong?

Even stranger: If I do the same in a different Tomcat webapp (implying: in a different thread of Tomcat with its own transactions), it knocks out the Web Client, too! How can this be?

Many thanks,
Kaspar

hbf
Champ on-the-rise
Champ on-the-rise
I've added a JIRA issue for this and am available if further debugging is needed.

hbf
Champ on-the-rise
Champ on-the-rise
Has anybody got an idea how to fix this? We are running into "transaction already marked for rollback" exceptions regularly and after these we cannot log in again, as discussed above. So we have to restart the server which is really annoying.

Thanks for any hint!
Kaspar

hbf
Champ on-the-rise
Champ on-the-rise
The JIRA issue mentioned above has changed: It is now this issue.

derek
Star Contributor
Star Contributor
Hi,

I need some advice from a Spring transaction guru, it seems: I don't get the following to work


    try {
      UserTransaction transaction = transactionService.getNonPropagatingUserTransaction();
      transaction.begin();
      authenticationService.authenticate(LOGIN, PASSWORD.toCharArray());
      NodeRef nodeRef = new NodeRef("workspace://SpacesStore/rubbish"); // invalid input
      int status = alfrescoContext.getTransaction().getStatus(); // 0
      nodeService.getProperty(nodeRef, ContentModel.PROP_CATEGORIES);
    }
    catch (Exception e)
    {
      int status = alfrescoContext.getTransaction().getStatus(); // (*)
    }
    transaction.commit();

Regards,
Kaspar

Hi, Kaspar

Any exception that passes through the Spring transactional layers and has not been explicitly declared will force the transaction to be marked for rollback.  So, InvalidNodeRefException will render the transaction as "ROLLBACK_ONLY".  You can't reuse the transaction or continue with it.  That's the first thing.  You work around this by doing NodeService.exists(), or by having alternate actions in a new transaction after you catch an exception.

The second thing is that you need to be 100% sure that the transactions are not leaked out of scope.  There are three ways to do this.
1. Fix up the try-catch-finally behaviour
2. (Recommended) Use the RetryingTransactionHelper available from TransactionService.  It doesn't leak transactions!
3. Switch on debug to detect any leaked transactions: log4j.logger.org.alfresco.util.transaction.SpringAwareUserTransaction.trace=DEBUG

Regards
Derek