JTA defaults and timeouts in Java EE
Your system might work flawlessly for years, but then one day, JTA (Java Transaction API) timeouts start showing up:
[WebLogic] Transaction Rolledback.: weblogic.transaction.internal.TimedOutException: Transaction timed out after 301 seconds
(...) No further JDBC access is allowed within this transaction....
[WebSphere] TimeoutManage I WTRN0006W: Transaction (...) has timed out after 120 seconds.
[JBoss/WildFly] javax.transaction.RollbackException: ARJUNA016102: The transaction is not active!
Although specific to some use cases, the occurrence of JTA transaction timeout is not that unusual and can happen in production. Especially, when the amounts of the data drastically increase compared to the limits defined initially. Maybe, you've just also switched to a different application server?
In Spring this is a piece of cake. On a class/interface/method level we can configure this using @org.springframework.transaction.annotation.Transactional(timeout = SECONDS)
annotation. It's the same regardless, whether we want to use container provided JTA, embedded one, or just a plain data source TM.
In the JavaEE world, however, this is not so straightforward. The equivalent javax.transaction.Transactional
at the moment does not expose an API to configure the timeout, this enhancement has been left open. Thus, the default timeout, as well as the ability to configure it per unit of work, have been left to the implementers' decision.
Example and defaults
Let's see how this works in the JavaEE environment. We will define the simplest Singleton Bean that will be instantiated on application startup:
package mypackage;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
@Startup
@Singleton(name = "StartupBean")
public class StartupBean {
@PostConstruct
public void init() {
System.out.println("Starting the initialization");
try {
Thread.sleep(31_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finished initializing");
}
}
For the verbosity, this bean is missing one relevant annotation, which is @TransactionManagement(TransactionManagementType.CONTAINER)
. This might be not intuitive for people starting with JavaEE but:
If this annotation is not used, the bean is assumed to have container-managed transaction management.
Furthermore, on the method level, there is also a missing @TransactionAttribute(TransactionAttributeType.REQUIRED)
annotation:
If the TransactionAttribute annotation is not specified, and the bean uses container-managed transaction demarcation, the semantics of the REQUIRED transaction attribute are assumed.
It is worth remembering these defaults when creating new beans. In the end, when I deploy this on a WebLogic 12.2, I get:
weblogic.ejb.container.InternalException: Transaction marked rollback or not expected transaction status: 4
and the WebLogic is not able to deploy the application (it stays in the STATE_NEW status). That is because the @PostConstruct
method is also covered by the JTA transaction, which in this case, has a default timeout of 30 seconds.
To disable the JTA, we can use either use @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
on a method/class level or @TransactionManagement(TransactionManagementType.BEAN)
annotation on the class level.
Timeouts
On the other hand, if we really have a transactional logic in such a method (probably not in the @PostConstruct
), we would likely be interested in configuring the timeout just like in the Spring. In this case, we have to consult the documentation of the particular JEE container implementation.
In the case of WebLogic, we can configure the timeouts through the WebLogic console on a domain level.
This adds some entries to the config.xml
file found in the config folder of your domain installation directory:
<!--...-->
</security-configuration>
<jta>
<timeout-seconds>31</timeout-seconds>
<abandon-timeout-seconds>86400</abandon-timeout-seconds>
<forget-heuristics>true</forget-heuristics>
<before-completion-iteration-limit>10</before-completion-iteration-limit>
<max-transactions>10000</max-transactions>
<max-unique-name-statistics>1000</max-unique-name-statistics>
<checkpoint-interval-seconds>300</checkpoint-interval-seconds>
<parallel-xa-enabled>true</parallel-xa-enabled>
<unregister-resource-grace-period>30</unregister-resource-grace-period>
<two-phase-enabled>true</two-phase-enabled>
<clusterwide-recovery-enabled>false</clusterwide-recovery-enabled>
<tightly-coupled-transactions-enabled>false</tightly-coupled-transactions-enabled>
<tlog-write-when-determiner-exists-enabled>false</tlog-write-when-determiner-exists-enabled>
</jta>
<!--...-->
More specific (bean-level) configuration can be done through the weblogic-ejb-jar.xml
descriptor placed in the WAR's WEB-INF or JAR's META-INF directory. The configuration is matched through the ejb-name
defined also by the bean name
annotation element.
<?xml version = '1.0' encoding = 'UTF-8'?>
<weblogic-ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-ejb-jar http://www.bea.com/ns/weblogic/weblogic-ejb-jar/1.0/weblogic-ejb-jar.xsd"
xmlns="http://www.bea.com/ns/weblogic/weblogic-ejb-jar">
<weblogic-enterprise-bean>
<ejb-name>StartupBean</ejb-name>
<transaction-descriptor>
<trans-timeout-seconds>60</trans-timeout-seconds>
</transaction-descriptor>
</weblogic-enterprise-bean>
</weblogic-ejb-jar>
For other providers, it might differ. A quick check in the newest Oracle Commerce Platform installation guide gives some general overview for the JBoss, WebLogic and WebSphere. JBoss/WildFly has additional @org.jboss.ejb3.annotation.TransactionTimeout
annotation akin to Spring. Similar to the WebLogic, JBoss/Wildfly also provides a <tx:trans-timeout>
configuration element in the jboss-ejb3.xml
descriptor. In the WebSphere, on the other hand, this element is called <global-transaction>
and is placed in ibm-ejb-jar-ext.xml
descriptor.
Finally, for a fine-grained control we can switch to BMT (Bean Managed Transactions). In that case we would use javax.transaction.TransactionManager.setTransactionTimeout(int seconds)
or javax.transaction.UserTransaction.setTransactionTimeout(int seconds)
methods for setting the timeout.
Summary
There are a few key takeaways from this:
- the
@Transactional
API differs between Java EE and Spring; - EJB beans have enabled CMT (Container Managed Transactions) by default unless you use explicitly declare it otherwise;
- controlling the CMT timeout is implementation-specific, the best idea is to check the container documentation.