cancel
Showing results for 
Search instead for 
Did you mean: 

Async Continuations with concurrent executions

meyerd
Champ on-the-rise
Champ on-the-rise
See
http://forums.activiti.org/en/viewtopic.php?f=6&t=2635&start=0

The underlying problem is that in the case of the process shown in the linked forum post, we have one root execution and 3 concurrent child executions. The child executions are continued asynchronously in three different transactions. When reaching the joining gateway, each transaction sets the property ExecutionEntity.forceUpdate=true on the parent execution. While this is not a persistent property, it nevertheless forces a new version of the parent execution to be committed hence the OptimisticLockingException.

This is totally fine from an engine/persistence point of view but maybe not what the user expects.
We can think about how to deal with this:
- simply accept it Smiley Happy  or
- implement exclusive jobs (some hooks in the code and database schema already there)

I noticed that optimistic concurrency control is not something people seem to "get". We currently have a lot of forum threads/jiras concerning some instance of this. Maybe the job executor should do things "pessimistically" by default (exclusive jobs would be the default behavior and if you know what you are doing you can use sth. like activiti:exclusive="false" in order to enhance performance) ?
7 REPLIES 7

filipearaujo
Champ in-the-making
Champ in-the-making
I totally agree with you and I think that the second choice is by far the right one, it will add a great value to the engine.  Smiley Wink

meyerd
Champ on-the-rise
Champ on-the-rise

filipearaujo
Champ in-the-making
Champ in-the-making
Thanks, I will give it a try!  Smiley Happy

filipearaujo
Champ in-the-making
Champ in-the-making
Hi, sorry for the delayed reply but here's my conclusions:

- The example with the parallel gateway works fine
- But a example with a loop, i.e., instead of a task a loop with a task and a exclusive gateway (I want to execute multiple times and when a certain value is true then finish the loop) it gives the same error and this exception:

SEVERE: Error while closing command context
org.apache.ibatis.exceptions.PersistenceException:
### Error updating database.  Cause: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_EXE_PROCINST: PUBLIC.ACT_RU_EXECUTION FOREIGN KEY(PROC_INST_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_)"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? [23003-132]
### The error may involve org.activiti.engine.impl.persistence.entity.ExecutionEntity.deleteExecution-Inline
### The error occurred while setting parameters
### Cause: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_EXE_PROCINST: PUBLIC.ACT_RU_EXECUTION FOREIGN KEY(PROC_INST_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_)"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? [23003-132]
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:8)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:120)
at org.apache.ibatis.session.defaults.DefaultSqlSession.delete(DefaultSqlSession.java:131)
at org.activiti.engine.impl.db.DbSqlSession$DeleteById.execute(DbSqlSession.java:146)
at org.activiti.engine.impl.db.DbSqlSession.flushDeletes(DbSqlSession.java:444)
at org.activiti.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:349)
at org.activiti.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:147)
at org.activiti.engine.impl.interceptor.CommandContext.close(CommandContext.java:103)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:49)
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33)
at org.activiti.engine.impl.RepositoryServiceImpl.deleteDeployment(RepositoryServiceImpl.java:55)
at org.activiti.engine.impl.test.TestHelper.annotationDeploymentTearDown(TestHelper.java:105)
at org.activiti.engine.test.ActivitiRule.finished(ActivitiRule.java:119)
at org.junit.rules.TestWatchman$1.evaluate(TestWatchman.java:54)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_EXE_PROCINST: PUBLIC.ACT_RU_EXECUTION FOREIGN KEY(PROC_INST_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_)"; SQL statement:
delete from ACT_RU_EXECUTION where ID_ = ? [23003-132]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:316)
at org.h2.message.DbException.get(DbException.java:167)
at org.h2.message.DbException.get(DbException.java:144)
at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:374)
at org.h2.constraint.ConstraintReferential.checkRowRefTable(ConstraintReferential.java:391)
at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:271)
at org.h2.table.Table.fireConstraints(Table.java:766)
at org.h2.table.Table.fireAfterRow(Table.java:783)
at org.h2.command.dml.Delete.update(Delete.java:79)
at org.h2.command.CommandContainer.update(CommandContainer.java:70)
at org.h2.command.Command.executeUpdate(Command.java:198)
at org.h2.jdbc.JdbcPreparedStatement.execute(JdbcPreparedStatement.java:179)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:22)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:51)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:29)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:75)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:43)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:118)
… 26 more

Maybe its not prepared for execute multiple times the same async task.

Thanks for the help.

Filipe Araújo

meyerd
Champ on-the-rise
Champ on-the-rise
Hi Filipe,

interesting… would it be possible to post a simplified version of your process-xml which would allow us to reproduce the behavior?

imoraru
Champ in-the-making
Champ in-the-making
We can think about how to deal with this:
- simply accept it Smiley Happy  or
- implement exclusive jobs (some hooks in the code and database schema already there)

Is there another solution?
None of the solutions above will work for me.
The requirements I have are as follows: given this process (same as the original post):
[img]http://dl.dropbox.com/u/175705/Screen%20shot%202011-10-30%20at%206.03.10%20PM.png[/img]

1. Tasks A, B and C have to be parallel (the time it takes between the parallel gateways can't be time(A) + time(B) + time(C)).
2. Task A, B, and C can be called only once (can't be rolled back)

So the A, B and C tasks need to be parallel (activiti:async="true" activiti:exclusive="false").

The solution is very well explained in the activity 5.9 user guide http://www.activiti.org/userguide/index.html#exclusiveJobs and this is exactly was I was looking for except for the issues mentioned when joining the tasks.

I understand the need for the optimistic locking and it will work for me if the retry will only retry the join transition and not call the execute on the service task again. Otherwise the task would book the flight again and again for every retry.

I guess that means the task transaction is separate from the activiti engine transition transaction.

Is there a difference if A, B and C are user tasks instead of service tasks and they're completed concurrently in different threads?
Would this solve the problem?

Looking forward for your input.
TIA.
Ion

jdev_hari
Champ in-the-making
Champ in-the-making
Hi,

I am also stuck in a similar design. Could you please let me know if you found any solution/workaround?

Thanks