cancel
Showing results for 
Search instead for 
Did you mean: 

How to write code with transactions

hbf
Champ on-the-rise
Champ on-the-rise
Dear list,

I need advice on how to use Alfresco transactions in my custom code. As far as I understand the Alfresco APIs, there are two ways to use transactions:

1. Create the transaction, begin() it, do the work, and eventually call commit() or rollback().
2. Do the work within a RetryingTransactionHelper's RetryingTransactionCallback.

The first approach can be difficult because, it seems, a transaction may fail due to concurrency issues, see this post. So it seems it's best to use approach 2.

However, the work I do in a transaction is spread over different classes/methods/places and I cannot easily write a single RetryingTransactionCallback that does it all. Therefore my question: is there maybe a third way to do work within a transaction that gets retried in case of concurrency issues?

Many thanks,
Kaspar
19 REPLIES 19

cbosdonnat
Champ in-the-making
Champ in-the-making
Hi Kaspar,

I don't know of any other way to create transactions. Don't you have a way to call all your code for the transaction in one method ?

Regards,

hbf
Champ on-the-rise
Champ on-the-rise
I can write my code to do all the work in a RetryingTransactionHelper callback. However, this overcomplicates the code in my web layer: Ideally, I'd like to start a transaction at the beginning of a form submit, the web framework (Wicket in my case) will transparently update the properties of my nodes, render the new values to HTML, and at the end of the request, the transaction's commit() will save the changes to the repository. You see: the begin(), the changes, and the commit() occur at very different positions in the code, with many more actions performed by the Web framework in between for the presentation of the data…

Therefore I am wondering: Isn't there a way to tell Alfresco's UserTransaction to use a stricter locking protocol in order to avoid (most) such concurrency issues?

Many thanks,
Kaspar

dranakan
Champ on-the-rise
Champ on-the-rise
Hello,

Can anyone tell me how to use multiple transactions ? If I want to use the code below, I receive an error if first transaction start a rollback : javax.transaction.RollbackException: The transaction has already been marked for rollback)

Code in my action:

   UserTransaction trx_A = serviceRegistry.getTransactionService()
            .getUserTransaction();
   try
   {
     trx_A.begin()
     nodeService.createNode(…);
     nodeService.createNode(…);
     trx_A.commit();
   }
   catch(Throwable e)
   {
     try
     {
       if (trx_A.getStatus() == Status.STATUS_ACTIVE)
       {
          trx_A.rollback();
       }
     }
     catch(Throwable ee)
     {
       // Handle double exception in whatever way is appropriate eg. log it
     }

     throw e;
   }


….

   UserTransaction trx_B = serviceRegistry.getTransactionService()
            .getUserTransaction();
   try
   {
     trx_B.begin()
     nodeService.createNode(…);
     nodeService.createNode(…);
     trx_B.commit();
   }
   catch(Throwable e)
   {
     try
     {
       if (trx_B.getStatus() == Status.STATUS_ACTIVE)
       {
          trx_B.rollback();
       }
     }
     catch(Throwable ee)
     {
       // Handle double exception in whatever way is appropriate eg. log it
     }

     throw e;
   }

From http://wiki.alfresco.com/wiki/Java_Foundation_API#Handling_UserTransaction_Directly

The error :

11:08:14,080 ERROR [ch.custom.manageoffice.actions.SplittPDFFromERP] Error during the transaction
javax.transaction.RollbackException: The transaction has already been marked for rollback
   at org.alfresco.util.transaction.SpringAwareUserTransaction.commit(SpringAwareUserTransaction.java:450)
   at ch.custom.manageoffice.actions.SplittPDFFromERP.createErrorFile(SplittPDFFromERP.java:271)
   at ch.custom.manageoffice.actions.SplittPDFFromERP.executeImpl(SplittPDFFromERP.java:229)
   at org.alfresco.repo.action.executer.ActionExecuterAbstractBase.execute(ActionExecuterAbstractBase.java:127)
   at org.alfresco.repo.action.ActionServiceImpl.directActionExecution(ActionServiceImpl.java:592)
   at org.alfresco.repo.action.executer.CompositeActionExecuter.executeImpl(CompositeActionExecuter.java:72)
   at org.alfresco.repo.action.executer.ActionExecuterAbstractBase.execute(ActionExecuterAbstractBase.java:127)
   at org.alfresco.repo.action.ActionServiceImpl.directActionExecution(ActionServiceImpl.java:592)
   at org.alfresco.repo.action.ActionServiceImpl.executeActionImpl(ActionServiceImpl.java:529)
   at org.alfresco.repo.action.AsynchronousActionExecutionQueueImpl$ActionExecutionWrapper$1$1.execute(AsynchronousActionExecutionQueueImpl.java:369)
   at org.alfresco.repo.transaction.RetryingTransactionHelper.doInTransaction(RetryingTransactionHelper.java:320)
   at org.alfresco.repo.transaction.RetryingTransactionHelper.doInTransaction(RetryingTransactionHelper.java:227)
   at org.alfresco.repo.action.AsynchronousActionExecutionQueueImpl$ActionExecutionWrapper$1.doWork(AsynchronousActionExecutionQueueImpl.java:378)
   at org.alfresco.repo.security.authentication.AuthenticationUtil.runAs(AuthenticationUtil.java:437)
   at org.alfresco.repo.action.AsynchronousActionExecutionQueueImpl$ActionExecutionWrapper.run(AsynchronousActionExecutionQueueImpl.java:381)
   at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
   at java.lang.Thread.run(Thread.java:619)

I would like in a action start some work in transaction A. And after that start a transaction B.

Thanks Smiley Happy

cheffilet
Champ in-the-making
Champ in-the-making
Why you dont take use of the Retrying-Transactionhandler-API?


Boolean result = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Boolean>(){
    public Boolean execute() throws Throwable {
           //your code should be right here dropped
      return true;
    }   
});

dranakan
Champ on-the-rise
Champ on-the-rise
Thank you Cheffilet Smiley Happy

I think return true = commit() and false = rollback(). I have tested, but when I return false… there is no rollback() started…
If I add files and return false… the files are in Alfresco…        
What I'm doing wrong ?

cheffilet
Champ in-the-making
Champ in-the-making
No, the transaction will be reseted if an exception will be raised:



Boolean result = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Boolean>(){
    public Boolean execute() throws Throwable {
             
      …
      if(val == null)
         throw new IllegalStateException("Val must not be null");
  
      return true;
    }  
});



You can use the returned value to go on with that result as its normaly that a transaction-scoped API like this above can return other values as only true or false.

dranakan
Champ on-the-rise
Champ on-the-rise
Thank you Cheffilet.

I have done like this :


final String var = …;
boolean resultAddFiles = transactionService.getRetryingTransactionHelper()
   .doInTransaction(
         new RetryingTransactionHelper.RetryingTransactionCallback<Boolean>() {
            public Boolean execute()
                  throws Throwable {

               boolean success= method(var);
               if (!success){
                  // Retry and rollback if always problem
                  throw  new IllegalStateException("Problem during moveFilesAndAddMeta");
               }
               return true;
            }
         });

If method return false, the exception is started and the execution continue (it not retry to execute). It is ok like this but the problem is that the rollback is not done. method will add some files, and if it return false, all files are always in Alfreso.

Has someone an idea ?

Thanks.

cheffilet
Champ in-the-making
Champ in-the-making
Sry, may fault:


Boolean result = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Boolean>(){
    public Boolean execute() throws Throwable {
            
      …
      if(val == null)
         throw new IllegalStateException("Val must not be null");
  
      return true;
    }  
//we need to setup a new transaction for this
}, false, true);

As if you dont provide a new transaction the still existing transaction will be taken… so if you throw away the result to alfresco (to the transaction-interceptor) a rollback will be taken (PROPAGATION_REQUIRES).

dranakan
Champ on-the-rise
Champ on-the-rise
Thank you Cheffilet !!! It works like I want !

If the method inside generate error, all is rollback.

I am just surprise because it doesn't restart the action. In my case, it is not a problem… I want that it doesn't restart Smiley Happy But…Shouldn't it ?


/**
     * 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.
     *
     * It is possible to force a new transaction to be created or to partake in
     * any existing transaction.
     *
     * @param cb                The callback containing the unit of work.
     * @param readOnly          Whether this is a read only transaction.
     * @param requiresNew       <tt>true</tt> to force a new transaction or
     *                          <tt>false</tt> to partake in any existing transaction.
     * @return                  Returns the result of the unit of work.
     * @throws                  RuntimeException  all checked exceptions are converted
     */
    public <R> R doInTransaction(RetryingTransactionCallback<R> cb, boolean readOnly, boolean requiresNew)

From : http://wiki.alfresco.com/wiki/Java_Foundation_API#Using_RetryingTransactionHelper_.28V2.1_and_later....

And by default :

    *  maxRetries: 20
    * minRetryWaitMs: 100
    * maxRetryWaitMs: 2000
    * retryWaitIncrementMs: 100

From http://dev.alfresco.com/resource/docs/java/repository/org/alfresco/repo/transaction/RetryingTransact...