Let me expand a bit on this.
What we have is web application that interacts with long running persisted processes using JBPM and Drools ( JPA and Hibernate ). We cannot limit the request from our users, they're concurrent by nature and as such there's almost always more than one thread trying to interact with jBPM and the knowledge session.
When running in this kind of enviroment using a StatefulKnowledgeSession, what we are observing is an increase of rollbacks and transaction problems in hibernate as the concurrency increases ( "proxy handle is no longer valid", "Flush during cascade is dangerous", "Could not commit transaction" and so on ). None of them is related to our application. And all can be traced up until org.drools.persistence.jta.JtaTransactionManager
Whenever we see that kind of exceptions, the problems are of different nature and generally bad ... task doesn't get created, process execution die without chance of later restoring it's state ... task state change doesn't happen ...
One obvious solution would be to synchronize all our methods that interact with JBPM just to queue the requests but that, by experience, leads to deadlocks in JBPM when two threads try to get a lock to execute a certain operation ( by the nature of our application, most of the time something related with human task life cycle things ).
As JBPM and Drools manage themselves their persistence contexts and manage the transactions on their own pace, we are in a situation where little can be done from our part ( this statement can be very wrong, please shout if it is ).
We're using JBPM for human task related work, our shortest process lasts 1 day and several of our processes can last up to 1 month ... ( that's why we need high avalailabily in the form of persistence both for Drools and JBPM ), all of them need human tasks to be completed to resume the execution of the process.
Our Drools session is created like this ( snippet, only relevant code ):
// Transaction environment
Context ctx = new InitialContext(); TransactionManager txm = (TransactionManager)ctx.lookup("java:/TransactionManager"); UserTransaction ut = (UserTransaction)ctx.lookup("java:comp/UserTransaction");
// JPA persistence Context
emf = Persistence.createEntityManagerFactory( "org.jbpm.persistence.jpa" );
// KnowledgeBase
env = KnowledgeBaseFactory.newEnvironment(); env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf ); env.set(EnvironmentName.TRANSACTION_MANAGER,txm); env.set(EnvironmentName.TRANSACTION,ut);
// Human task service
TaskService taskService = new org.jbpm.task.service.TaskService(emf, SystemEventListenerFactory.getSystemEventListener()); Map<String, User> users = new HashMap<String, User>();
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
// process definition files follow
kbuilder.add(ResourceFactory.newInputStreamResource(bpmn_process),ResourceType.BPMN2);....
KnowledgeBase kbase = kbuilder.newKnowledgeBase();...
if (thereisasession){
// loads the persisted session
sesion = JPAKnowledgeService.loadStatefulKnowledgeSession(sessionId, kbase, null, env ); } else { sesion = JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env ); }
...
// Human task handler
LocalHTWorkItemHandler localHTWorkItemHandler = new LocalHTWorkItemHandler(humanTaskClient, sesion, OnErrorAction.RETHROW); localHTWorkItemHandler.connect(); sesion.getWorkItemManager().registerWorkItemHandler("Human Task", localHTWorkItemHandler);
<persistence version="1.0" xsi:schemaLocation= "http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/persistence"> <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA">. ...
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>We're really getting stuck with this ... :(
<property name="hibernate.connection.autocommit" value="false" /> <property name="hibernate.max_fetch_depth" value="3"/> <property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.show_sql" value="false" /> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" /> <property name="hibernate.order_updates" value="true"/> <property name="hibernate.connection.isolation" value="2"/>
...
</persistence>
Our pooled XA datasource for JBPM is defined like this ( JBoss 7, IronJCAmar) :
<xa-datasource jta="true" jndi-name="java:/JBPM_PG_DS" pool-name="JBPM_PG_DS_Pool" enabled="true" use-java-context="true" use-ccm="false">
<xa-datasource-property name="ServerName">localhost</xa-datasource-property>
<xa-datasource-property name="PortNumber">5432</xa-datasource-property>
<xa-datasource-property name="DatabaseName">JBPM</xa-datasource-property>
<xa-datasource-property name="User">jbpm</xa-datasource-property>
<xa-datasource-property name="Password">jbpm</xa-datasource-property>
<driver>postgresXA</driver>
<transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
<xa-pool>
<min-pool-size>25</min-pool-size>
<max-pool-size>100</max-pool-size>
<prefill>true</prefill>
<use-strict-min>false</use-strict-min>
</xa-pool>
<security>
<user-name>jbpm</user-name>
<password>jbpm</password>
</security>
<validation>
<check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
<validate-on-match>false</validate-on-match>
<background-validation>false</background-validation>
</validation>
</xa-datasource>
I'm now wondering what to do to improve the concurrency of the system to avoid those transaction problems ....
- Is there additional parameter/configuration we should be doing?
- Any magic hibernate, JPA or JBoss JCA parameer we should be using?
- Will dividing the persistence context into two, one for Drools related info and one for JBPM info will help?
- What about creating a Drools session with org.drools.conf.MultithreadEvaluationOption.YES ??
Hi guys,
We have an application built around long running JBPM processes with a large user base and high concurrency ... early on we began to have issues with transactions in JBPM and nowadays are watching lots of different realizations of transaction managing problems that lead to dead human tasks ( state: ERROR ), errors running processes and so on ( different exceptions follows ):
13:22:48,261 WARN [org.drools.persistence.jta.JtaTransactionManager] (pool-11-thread-1) Unable to commit transaction: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction
Caused by: javax.persistence.PersistenceException: error during managed flush
Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1177)
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:117)
at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
at org.jboss.tm.usertx.client.ServerVMClientUserTransaction.commit(ServerVMClientUserTransaction.java:167)
at org.drools.persistence.jta.JtaTransactionManager.commit(JtaTransactionManager.java:179) [drools-persistence-jpa-6.0.0-SNAPSHOT.jar:6.0.0-SNAPSHOT]
Caused by: javax.persistence.PersistenceException: org.hibernate.HibernateException: Flush during cascade is dangerous
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1361) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1289) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
11:46:23,754 ERROR [stderr] (schedulerVT_Worker-8) javax.persistence.PersistenceException: org.hibernate.HibernateException: proxy handle is no longer valid
11:46:23,754 ERROR [stderr] (schedulerVT_Worker-8) at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1361)
11:46:23,755 ERROR [stderr] (schedulerVT_Worker-8) at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1289)
11:46:23,756 ERROR [stderr] (schedulerVT_Worker-8) at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:261)
11:46:23,756 ERROR [stderr] (schedulerVT_Worker-8) at
So, we're running this installation by the manual. Are using JBoss 7 as application server, it's transaction manager, Hibernate 4.x as entity manager, our connection pool delivers READ_COMMITED connections ( so does hibernate ) ...
Is there anything else we should be doing to avoid this transactional problems? Has anyone ever tested JBPM in such concurrent scenario? Any experiences?
Alberto R. Galdo
argaldo@gmail.com