Troubleshooting Failed Transactions in Java

If you're planning to design data-intensive systems that utilize Java architecture, you'll probably use Jakarta (EE) / Java Transactions. They are essentially the lifeblood of complex open source enterprise applications. As such, they require careful planning and coordination. Without planning, transactions may try to commit on the same resource object causing an XAException or SystemException.

You need to add the right controls and tools to help you debug and track all transaction-related errors. The following guide will show you how to find and troubleshoot failed transactions. Since prevention is better than remediation, it will also show you the best coding and design practices to minimize and easily handle failed transactions.

What Are Java Transactions

Transactions (in software design and development) are, essentially, actions performed on data sources. If you've worked with the Java SE SQL packages, you've most likely encountered transactions. They enable the conditional execution of multiple statements. Transactions in Java SE SQL packages, known as local transactions, are executed singularly and directly on a resource. Thus, no transaction manager is required, and they can be handled with the Java SQL package alone.

However, the Jakarta Transaction API typically handles distributed transactions for high-level Java-based systems and applications. Since Oracle reacquired the Java Transaction API (JTA) https://www.oracle.com/java/technologies/jta.html name, the JTA javax package https://www.javadoc.io/doc/javax.transaction/javax.transaction-api/1.2/index.html can also manage transactions. These two implementations are similar.

The JTA and the Jakarta Transaction API provide interfaces and classes to manage transactions for (multi-transactional) applications and application servers. They are modeled after the X/Open XA standard https://pubs.opengroup.org/onlinepubs/009680699/toc.pdf, so a transaction manager coordinates with a resource manager to execute transactions on a set of software resources.

Consequently, in addition to the application transaction demarcation interface and transaction manager interface (for applications), the Jakarta Transaction API features a third layer for standard Java mapping of the X/Open XA protocol.

The JTA is a little simpler but functions on the same principle. It offers two interfaces: one embodies transactions (javax.transaction), and the other embodies resource objects (javax.transaction.xa).

Transactions are typically executed on software resources, such as databases, messaging services, and enterprise beans. Each of these resources has unique, specific managers. Accordingly, the Jakarta Transaction API shares a relationship with many (if not all) of the Java APIs that are used to abstract and control data sources.

For instance, Java Database Connectivity (JDBC) drivers for distributed transaction systems use either the javax.transaction.xa.XAResource, javax.sql.XAConnection, or javax.sql.XADataSource interface. This is true for both the JTA and the Jakarta Transactions API. That said, you can find the Jakarta Transactions API as part of the Jakarta EE library or as a stand-alone module https://github.com/jakartaee/transactions. The subsequent section will take a closer look at how all these disparate programming components fit together by looking at how a distributed transaction system works. It should apply to both APIs.

Example of a Distributed Transaction Process

In cases where there are multiple distributed relational databases, the components involved in the distributed transaction process include the following:

  • Application
  • Application server
  • Resource adapter (JDBC DriverManager)
  • Resource manager (JDBC driver)
  • Transaction manager
  • Databases

In this example, the transactions will be queued and directed to the necessary database management systems to fetch appropriate information. The resource adapter and manager translate the requests to the databases.

The application server handles the bulk of application requests sent by the application. The transaction manager administers and controls the boundaries of each transaction and is responsible for deciding whether the transaction should commit or roll back:

Distributed Transaction Process Architecture Diagram Fig. 1: Distributed Transaction Process Architecture Diagram

In the previous diagram, the transaction manager commits all transactions successfully. However, this isn't always the case. Sometimes transactions fail, and when they do, the transaction manager will perform a rollback.

Furthermore, even if the transaction manager commits the transaction successfully, an error may still cause it to fail at the resource adapter or manager. As such, the application must perform a check to verify if there are any errors related to the resource manager connection. In some instances, the resource manager may throw an exception.

Any errors that occur are often addressed by the application calling the resource manager's rollback method. But why do transactions fail in the first place, and is it avoidable?

Why Java Transactions Fail

Much like database transactions, Java/Jakarta transactions follow the ACID principle https://www.java-samples.com/showtutorial.php?tutorialid=205. Consequently, they either execute completely or not at all. A successful transaction execution results in a commit while a failed one results in a rollback. When a rollback occurs, all the components affected by the attempted commit are reverted to their pre-commit states.

While the sql package handles rollbacks automatically, it's important to explicitly handle transaction rollbacks in the code. Preferably, the rollback() https://www.tabnine.com/code/java/methods/javax.transaction.Transaction/rollback method should be preferably called when handling an exception (in a catch block).

Transactions may fail because of malformed statements (if you're dealing with JDBC drivers) or resources becoming suddenly unavailable. As such, the failure may occur because of an error in the transaction itself or during the call to commit. When the latter occurs, the runtime will throw a TransactionRolledBackException.

Often, the reason for a transaction failure is due to an error in scope or state. For example, class members that should be public are mistakenly being defined as private, or objects that should be defined as static are being made nonstatic.

There are many options and tools you can use to determine the cause of transaction failure.

How to Debug Transaction Failures

The examples used in this section will focus on Java SE's JTA package. However, many of the concepts are transferable to Jakarta's implementation.


[TIMESTAMP] 00000157 EmbeddableTra I WTRN0041I: Transaction
00000172E552D88A0000000115200494443873BE8709685AA43D3ED6C2AFF879F9EA3D4900000172E552D88A0000000115200494443873BE8709685AA43D3ED6C2AFF879F9EA3D4900000001 has been rolled back.
[TIMESTAMP] 00000157 WSRdbXaResour E DSRA0304E: XAException occurred. XAException contents and details are:
The XA Error is : -7
The XA Error message is : Resource manager is unavailable.
The Oracle Error code is : 17008
The Oracle Error message is: Internal XA Error
The cause is : java.sql.SQLRecoverableException: Closed Connection.
[TIMESTAMP] 00000157 WSRdbXaResour E DSRA0302E: XAException occurred. Error code is: XAER_RMFAIL (-7). Exception is: XAErr (-7): Resource manager is unavailable. ORA-17008 SQLErr (0)
[TIMESTAMP] 00000157 XATransaction E J2CA0027E: An exception occurred while invoking end on an XA Resource Adapter from DataSource jdbc/DraftDev, within transaction ID {XidImpl: formatId(53445741), gtrid_length(35), bqual_length(2),
data(00000172e552d88a0000000115200494443873be8709685aa43d3ed6c2aff879f9ea3d4900000172e552d88a015200494443873be8709685aa43d3ed6c2aff879f9ea3d49000000010000000000000000000000000001)} : oracle.jdbc.xa.OracleXAException: XAErr (-7): Resource manager is unavailable. ORA-17008 SQLErr (0)
at oracle.jdbc.xa.OracleXAResource.checkError(OracleXAResource.java:1112)

The above sack trace can be a little difficult to decipher. However, by focusing on the readable parts, you can better understand the reason for the exception. The stack trace can be simplified to the following components:


java.sql.SQLRecoverableException: Closed Connection
An exception occurred while invoking end on an XA Resource Adapter from DataSource jdbc/DraftDev, within transaction ID {XidImpl: formatId(53445741), gtrid_length(35), bqual_length(2)

This implies that the transaction failed due to the unavailability of the SQL database endpoint. You have the name of the datasource in question: jdbc/DraftDev.

As the exception stack trace does not indicate any problems in custom classes, it can be concluded that the failure was not caused by an issue in the code itself – at least not directly. Thus, the exception must have been thrown because of a server-side, endpoint, or connectivity issue.

The next step would be to identify the source of the issue. However, the stack trace only provides a starting point and further investigation is required. It's reccomended that you check your server, network connection and database to ensure that there aren’t any physical or digital impedances causing your transactions to fail.

Undoubtedly, this process can be time-consuming. This particular exception occurred because of a security update on the server. The security software deemed connections being made to the database as suspicious and thus blocked them. Based on the previous stack trace alone, identifying this cause is quite difficult and would require a lot of trial and error.

Transactions are more likely to fail because resources are suddenly made unavailable. When an application that once worked (perfectly) starts throwing exceptions (errors), it's rarely down to a mistake in the code.

Despite this not being strictly a code-based issue, you can still include contingencies in your code to address circumstances such as these. Regardless, debugging the code is only part of the solution.

If you're using a basic text editor and the Java virtual machine (JVM) to debug your code, you'll have to rely on logging or use the Java Debugger (jdb) https://docs.oracle.com/javase/7/docs/technotes/tools/windows/jdb.html command line interface. Of course, this isn't ideal for large programs.

Alternatively, you can use an IDE, such as Apache NetBeans https://netbeans.apache.org/, OpenBeans http://www.openbeans.org/, or Eclipse IDE for Java Developers https://www.eclipse.org/downloads/packages/. They provide visual debugging tools that allow you to place breakpoints, retrieve more information about exceptions, step in and out of code, and so on. NetBeans also features a system resource monitor that allows you to force garbage collection, which can be particularly useful when you're building and running high-load applications. If your transactions aren't properly committed or rolled back, it can cause system resource leaks under heavy load.

While this is a great feature, in some cases, you may need to add a JVM and application profiling tool to monitor resources properly and fine-tune your application. Some examples of such tools include the following:

  • VisualVM https://visualvm.github.io/
  • JProfiler https://www.ej-technologies.com/products/jprofiler/overview.html
  • YourKit Java Profiler https://www.yourkit.com/java/profiler/features/
  • JITWatch https://github.com/AdoptOpenJDK/jitwatch

Alternatively, you can use an all-in-one service such as Site24x7 https://www.site24x7.com/. It allows you to monitor all your remote and local resources. You can track every aspect of your development environment or workflow. This includes network resources, websites, servers, and cloud resources. You can profile your projects with its dedicated Java Application Performance Monitoring https://www.site24x7.com/java-application-monitor.html tool. It can identify slow traces, assess the performance of your databases, observe your JVM statistics and metrics, monitor your custom Java components, and most importantly, track your background transactions.

Best Coding Practices to Prevent and Handle Transaction Failures

When attempting to execute and commit a transaction, you must ensure that you've included necessary exception handling that will roll back the transaction:


} catch (javax.transaction.xa.XAException xae) { // Distributed transaction failed, so roll it back.

// The transaction could either fail on prepare or commit.
System.out.println("Distributed transaction prepare/commit failed. " +
​ "Rolling it back.");
System.out.println("XAException error code = " + xae.errorCode);
System.out.println("XAException message = " + xae.getMessage());
xae.printStackTrace();
try {
xaRes1.rollback(xid1); //Performs a distributed transaction roll-back on the first branch

} catch (javax.transaction.xa.XAException xae1) { //Catches and reports failure in the roll-back process

System.out.println("distributed Transaction rollback xares1 failed");
System.out.println("XAException error code = " + xae1.errorCode);
System.out.println("XAException message = " + xae1.getMessage());
​ }

The first catch is triggered by an exception from a failed commit. It logs and prints all the necessary information to help you find what caused the exception and then attempts to use the rollback method. Because it may throw an exception, it's implemented using a try statement. Again, you may add more catches for resource exceptions. Always catch the most explicit exceptions before general ones.

To minimize conflicts, each transaction must have a global transaction ID and branch qualifier, which must be unique to the current transaction manager:


byte[] gtrid = new byte[] { 0x44, 0x11, 0x55, 0x66 }; //Global transaction ID
byte[] bqual = new byte[] { 0x00, 0x22, 0x00 }; //Branch qualifier

It's common for the system to cancel or block your transaction at any moment. Thus, you must keep track of long-running transactions. For instance, you can use MBean operations, such as enableJDBCTiming and disableJDBCTiming, to enable and disable the timing of JDBC transactions, allowing you to set timeouts for transactions. You can also use threads to run concurrent transactions. You can retrieve the state of threads using the getThread() method.

Additionally, always remember the following basic coding best practices:

  • Keep your code as succinct as possible. Do not add unnecessary features. Stick to the you-aren't-gonna-need-it (YAGNI) https://martinfowler.com/bliki/Yagni.html principle.
  • Use distributed transactions when necessary. If there is a cleaner and more efficient way to perform your application's main function without transactions, use it.
  • Do not catch and rethrow exceptions.

Conclusion

Working with Java Transactions can be a nightmare. It can involve a large set of disparate pieces for system designers and architects. This is especially true when you introduce multiple threading to the equation. This guide explores what transactions are, why they may fail, and how you can debug and fix them. More often than not, transactions fail because of system issues. You can take the necessary steps to handle these situations in your code; however, they cannot be eliminated completely.

Thus, you must have a tool that makes it easier to track and trace the root cause of your failing transactions. Solutions like the Site24x7 Java Application Performance Monitoring https://www.site24x7.com/java-application-monitor.html tool can make creating and monitoring transaction-heavy applications infinitely easier. You can use it to monitor and identify which system resources or policies are causing your transactions to fail.

Furthermore, you can use it to optimize your projects' and applications' performance. Is your application's memory footprint too high, is the disk usage sustainable, and how much network bandwidth does it use? The Site24x7 Java Application Performance Monitoring tool can help you answer these questions. However, tools such as these must be paired with well-written code. This reduces the frequency of bugs and makes them easier to find.

Was this article helpful?

Related Articles

Write For Us

Write for Site24x7 is a special writing program that supports writers who create content for Site24x7 "Learn" portal. Get paid for your writing.

Write For Us

Write for Site24x7 is a special writing program that supports writers who create content for Site24x7 “Learn” portal. Get paid for your writing.

Apply Now
Write For Us