cancel
Showing results for 
Search instead for 
Did you mean: 

Failed to compensate (Transaction marked Rollback only )

vivverma
Champ in-the-making
Champ in-the-making
Hi All,
The Problem is described below:-
1) My Application X has activiti engine embedded inside it to execute certain Workflows.
2) X utilizes global JTATransactionManager and an XA Datasource, and passes the same to Activiti while configuring SpringProcessEngineConfiguration.
3) The Workflow has two tasks T1 and T2.  T1 triggers a WS call to some external system. T2 executes a call to One of the Application's Transactional service API.
4) T2 call fails and marks the Trasaction Status to rollback, however the execption is caught in the Service Task so that a Compensation could be triggered.
5) Now T1 has compensation task defined.
6) Activiti tries to query the DB but fails as the Transaction has already been marked as rollback.

As i understand activiti heavily relies on the DB for its execution flow and as the transaction has already been marked as rollback, it encouters this Rollback Exeption.

Can anyone point me in the right direction as to how to overcome this issue.
5 REPLIES 5

frederikherema1
Star Contributor
Star Contributor
can you clarify this:

T2 call fails and marks the Trasaction Status to rollback, however the execption is caught in the Service Task so that a Compensation could be triggered.

Is the T2 a custom service-task or a webservice-task? If it's a service-task, make sure that the exception doesn't reach the activiti execution-stack (try-catch ignore in the execute() method itself). How are you throwing the compensation-event?

vivverma
Champ in-the-making
Champ in-the-making
T2 is a ServiceTask that invokes one of the Application's Service API(say updateCore). The invoked API(updateCore) is a transactional API and throws application specific Exceptions.
All code within the Service task is wrapped inside a try catch block so that exceptions could be caught and a compensation could be fired.

Whenever there is an exception thrown from the service api(updateCore), the exception crosses the logical transaction boundary(which in this case is the Service Class) and hence Spring marks the transaction to ROLLBACK only.
Now since Activiti and the Application are sharing the same Transaction Manager and participating in same the Transaction, whenever Activiti tries to query anything from the DB, it encounter this Rollback Exception.

frederikherema1
Star Contributor
Star Contributor
I see what you mean. I'm afraid there is no way around this. Activiti needs to hook into the an active transaction to retain the state/trace of the process up until that point, before executing the actual "compensation" behavior.

Hi Fred, to me it seems like a very valid use-case where a ServiceTask might call an existing @Transactional ServiceAPI(which could throw Service level Exceptions), and enterprise applications which are utilizing Container level transaction manager will run into the above mentioned issue.
Having said that, i wanted to check if i am doing something wrong by relying on both transaction+compensation to rollback the changes done by the workflow?
Do you recommend any alternative approach for the above scenario?

frederikherema1
Star Contributor
Star Contributor
Alternative approach that I can think of is using an additional TransactionSynchronisationAdapter (if you're using Spring, that is). After all changes have been rolled back (transaction-rollback) to both activiti and your service, you can use the adapter to force a new transaction (REQUIRES_NEW) and try running the compensation-behaviour manually. Offcourse, this won't be using the "compensation" that is built in into BPMN, as the process has been rolled back and the process-state is not suited for a compensation since it doesn't know anything went wrong (take a look at the org.activiti.engine.impl.jobexecutor.FailedJobListener which we use to decrement job retries after rollback in job-execution).

In the current implement ion, you should either rely on the transaction being rolled back completely (so process-state remains as it was before the API-call was made) or either use compensation for scenario's where the transaction has not been rolled back by the failure…

I understand that this is a valid use-case for compensation working with @Transactional rollback, although really hard to implement as I mentioned before due to the fact that the process-changes are done in the rolled-back transaction. So I don't see such behavior being added any time soon to the engine, I'm afraid.