Configuring Transactions (JTA) in WildFly and JBoss EAP
This tutorial provides a comprehensive guide to configuring, monitoring, and tuning Java Transaction API (JTA) transactions on WildFly and JBoss Enterprise Application Platform (EAP). Updated for WildFly 41, this article highlights critical enhancements in transaction recovery during graceful shutdowns, particularly for containerized and cloud environments.
Transaction Subsystem Overview
A transaction is a logical unit of work containing one or more operations that must either all succeed or all fail together (ACID properties). When operations span across multiple databases, messaging brokers, or enterprise resources on separate servers, it becomes a distributed transaction.
The Java Transaction API (JTA) provides standard interfaces for application servers and resource managers to coordinate distributed transactions seamlessly. Within WildFly and JBoss EAP, transaction management is handled by the integrated Narayana transaction engine through the transactions subsystem.
The transactions subsystem consists of four core architecture elements:
- Core environment: Exposes the
TransactionManagerinterface, allowing the application server to demarcate and manage transaction boundaries on behalf of applications. - Recovery environment: Ensures transactional outcomes are applied consistently across all participating resources, even in the event of application crashes, network partitions, or hardware failures.
- Coordinator environment: Manages two-phase commit (2PC) protocol communications between transactional participants and resources.
- Object Store: Persistently records transaction state logs (prepared transactions, heuristics, and participant lists) to disk or database to enable post-crash recovery. The
RecoveryManagerscans the Object Store periodically to resolve in-doubt transactions.
WildFly 41 Feature Highlight: Graceful Shutdown & Transaction Recovery
Starting with WildFly 41, a major improvement was introduced to eliminate potential data integrity issues during server graceful shutdowns. This is controlled via the new transactions-recovery-graceful-shutdown attribute in the transactions subsystem.
The Cloud & Container Challenge
Historically, WildFly's graceful shutdown (server suspension) invoked Narayana's Recovery Coordinator suspension hook. Narayana operates under the assumption that a suspended server will eventually be resumed or restarted. During suspension, Narayana executes one extra recovery cycle and leaves any remaining in-doubt transactions in the local Object Store, expecting to resolve them when the server boots back up.
However, in modern cloud environments (Kubernetes, OpenShift, container autoscaling), scaling down a deployment destroys the hosting pod/container permanently. The pod's local file system, IP address, and memory state are completely erased. If Narayana leaves in-doubt transactions in a local Object Store during shutdown, those uncommitted or unrolled-back transactions are lost forever, leading to serious data integrity issues across external databases.
How transactions-recovery-graceful-shutdown Solves It
WildFly 41 introduces the transactions-recovery-graceful-shutdown attribute. When set to wait, WildFly instructs the transaction subsystem's ServerActivity Implementation (SAI) and EJB SAI to delay server suspension until all in-flight and in-doubt transactions in the Object Store are fully completed or resolved.
- In-Doubt Transaction Tracking: The Transactions SAI actively queries the Object Store for unresolved transactions and delays suspension until the count reaches zero.
- EJB SAI Retention: EJB suspension handlers retain control and prevent context termination as long as active transactions are pending completion.
- User Notifications: System log messages explicitly notify administrators when SAIs delay WildFly's graceful shutdown, which is particularly valuable when negative (indefinite) shutdown timeouts are configured in cloud scale-down scripts.
To enable graceful transaction recovery shutdown via JBoss CLI, execute:
/subsystem=transactions:write-attribute(name=transactions-recovery-graceful-shutdown,value=wait)
:reload
Standard Subsystem Configuration
Below is a typical transactions subsystem XML configuration block incorporating recovery environment bindings and Object Store paths:
<subsystem xmlns="urn:jboss:domain:transactions:6.0">
<core-environment node-identifier="${jboss.tx.node.id:1}">
<process-id>
<uuid/>
</process-id>
</core-environment>
<recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
<coordinator-environment statistics-enabled="${wildfly.transactions.statistics-enabled:${wildfly.statistics-enabled:false}}" default-timeout="300" maximum-timeout="3600"/>
<object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
</subsystem>
Key Attributes Explained
- default-timeout: Sets the default transaction timeout in seconds (default is
300seconds / 5 minutes). - maximum-timeout: Specifies the upper bound limit for transaction timeouts. If an application requests a timeout of
0(unlimited), WildFly caps it tomaximum-timeoutto prevent hung transactions. - enable-statistics: Enables or disables runtime collection of transaction metrics (commits, rollbacks, heuristics).
- object-store-path: Configures the directory path for transaction state logs. By default, files are stored in
standalone/data/tx-object-store.
To modify the Object Store path location dynamically using CLI:
/subsystem=transactions:write-attribute(name=object-store-path,value=logdir)
Alternatively, you can override the store directory globally via system property:
JAVA_OPTS="$JAVA_OPTS -Dcom.arjuna.ats.arjuna.objectstore.objectStoreDir=/var/log/wildfly/tx-store"
To learn how to store transaction logs in a relational database for shared recovery across clustered nodes, see our guide on How to Configure a JDBC Store for Transactions.
Managing Transactions in Applications
Applications typically manage transaction boundaries declaratively using @Transactional on Jakarta EE / CDI beans or Container-Managed Transactions (CMT) on EJBs. If a transaction exceeds its configured timeout duration, Narayana automatically marks it for rollback.
Configuring Transaction Timeouts
You can adjust the global default timeout via CLI:
/subsystem=transactions:write-attribute(name=default-timeout,value=600)
To override timeouts at the method or class level inside Java code, use the @TransactionTimeout annotation:
import org.jboss.ejb3.annotation.TransactionTimeout;
import java.util.concurrent.TimeUnit;
@Stateless
public class OrderProcessingBean {
@TransactionTimeout(value = 30, unit = TimeUnit.SECONDS)
public void processLargeBatch() {
// Long running transactional operations
}
}
Alternatively, define method timeouts inside META-INF/jboss-ejb3.xml deployment descriptors:
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:tx="urn:trans-timeout"
version="3.1">
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>OrderProcessingBean</ejb-name>
<method-name>*</method-name>
</method>
<tx:trans-timeout>
<tx:timeout>30</tx:timeout>
<tx:unit>Seconds</tx:unit>
</tx:trans-timeout>
</container-transaction>
</assembly-descriptor>
</jboss:ejb-jar>
Monitoring Transactions via Management CLI and Console
To monitor runtime statistics, first enable metrics gathering:
/subsystem=transactions:write-attribute(name=enable-statistics,value=true)
Read runtime transaction counters via CLI:
/subsystem=transactions:read-resource(include-runtime=true)
In the Web Management Console, navigate to Runtime > Subsystems > Transactions to view real-time commit, rollback, and heuristic counts:
Managing Prepared / In-Doubt Transactions from CLI
During failure recovery, administrators can inspect and manually resolve prepared transactions directly using the CLI:
1. List all active or prepared transactions:
/subsystem=transactions/log-store=log-store:read-children-names(child-type=transactions)
2. View detailed attributes of a specific transaction:
/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001-b66efc2\:4f9e6f8f\:9:read-resource
3. Force recovery on a participant:
/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001-b66efc2\:4f9e6f8f\:9/participants=1:recover
4. Forget / Delete a stuck transaction log:
/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001-b66efc2\:4f9e6f8f\:9:delete
Fixing "Node identifier property is set to default" Warning (WFLYTX0013)
When executing two-phase commit (2PC) XA transactions across multiple servers, Narayana requires every server instance to have a unique node-identifier. If left unconfigured, WildFly logs the following warning at startup:
WFLYTX0013: Node identifier property is set to the default value. Please make sure it is unique.
To assign a unique node identifier using system properties:
# Add a unique system property
/system-property=jboss.tx.node.id:add(value=node1_serverA)
# Bind the system property to the transaction subsystem
/subsystem=transactions:write-attribute(name=node-identifier,value="${jboss.tx.node.id}")
In Managed Domain topologies, configure host/server-specific system properties while leaving the shared profile configuration generic:
# Define unique properties per server instance
/host=host1/server-config=serverA/system-property=jboss.tx.node.id:add(boot-time=true,value=host1_serverA)
/host=host1/server-config=serverB/system-property=jboss.tx.node.id:add(boot-time=true,value=host1_serverB)
# Set the profile property once
/profile=full-ha/subsystem=transactions:write-attribute(name=node-identifier,value="${jboss.tx.node.id}")
Frequently Asked Questions & Troubleshooting
Why is my WildFly server taking longer to shut down in Kubernetes?
If transactions-recovery-graceful-shutdown is set to wait, WildFly actively delays server process termination until all prepared and in-doubt transactions are resolved by Narayana. Ensure your Kubernetes terminationGracePeriodSeconds is configured sufficiently higher than your transaction timeouts.
What is the difference between default-timeout and maximum-timeout?
default-timeout applies to transactions that do not explicitly declare a timeout. maximum-timeout acts as a hard ceiling, preventing applications from requesting excessively long or infinite timeouts.
Conclusion
Configuring the JTA transaction subsystem properly is crucial for maintaining data consistency across enterprise deployments. With WildFly 41's new transactions-recovery-graceful-shutdown=wait setting, cloud-native deployments can now safely scale down pods without risking orphaned in-doubt transactions or silent data loss.
Recommended Articles
Debugging Narayana Transactions with WildFly and Java 21 Using Enhanced Logger Configuration
Learn how to debug JTA transactions in WildFly using a custom Logger. Increase verbosity for detailed logging.
Using Transactions in CDI Beans with Java EE 7
Learn how to use transactions in CDI beans with Java EE 7, simplifying transaction management and improving code maintainability.
Retrieve and Monitor Transactions with JBoss-WildFly AS
Learn how to retrieve transaction information from your Java EE applications running on JBoss/WildFly and combine it with the Narayana Transaction Analyser application.
WildFly Transaction Timeout: Configure It (and Fix ARJUNA012117)
Configure the WildFly/JBoss EAP transaction timeout at every level (subsystem, EJB, MDB, BMT) and fix the ARJUNA012117 TransactionReaper timeout warning when the default-timeout change alone doesn't seem to work.