Monitoring Declarative Transactions in Spring
Declarative transaction management is arguably one of the most useful and widely used features of the Spring Framework. It allows sophisticated control over transactions with only a few lines of configuration. A typical application need only to add an @Transactional annotation to a given method, plus drop in a <tx:annotation-driven/> and a few other tags in a Spring configuration file to achieve reliable transaction management.
As easy as it is to configure Spring Framework to manage transactions, it is almost as easy to misconfigure such that transactions are not managed at all. Because Spring uses a non-invasive approach to starting and ending transactions, it can be difficult to tell whether a given section of code is operating inside a managed transaction. I once reviewed an application for a client that was having database problems in production; these were eventually traced to a complete lack of transactional integrity. The root problem (you guessed it): misconfiguration of Spring transaction management. The application had been running for a year in this condition.
So the question is: how can we reliably detect whether or not application logic is running inside managed transactions. This article explores several possibly paths and develops the most appropriate solution.
Possible Solutions
Check from Code
Probably the most straight-forward way to verify whether the business logic is in a transaction is to add some code that tests for this condition. Spring Framework provides a class that's suitable for this purpose: TransactionSynchronizationManager. Typical usage might look like the following:
@Transactional
public void invokeInsert() throws SQLException {
assert TransactionSynchronizationManager.isActualTransactionActive();
logger.info("invoking insert");...
}
The major detraction from this approach is the invasiveness of the check. Every class that needs verification of transaction management would have to be modified to include the new check. This not only implies doing some work, but also means that verification is limited to situations where the application can be redeployed with the new changes. In some cases the lead time for an full deployment cycle will rule out this approach all together.
Empirical Test
Another approach to verifying transaction management is to induce application failure during transaction processing, then see if changes were in fact rolled back by the resource manager (i.e., database). Failure might be induced by running the application through a debugger, stopping at various critical points and tweaking values such that the application throws an exception and aborts the supposed transaction. Other scenarios are common, such as detaching the application server from the network, killing the database server, etc.
This is the ultimate test for a system's transactional behavior, but there are a few problems:
- you really can't perform this test in a database that has valuable information. If the business logic really isn't under transaction management then the database may be corrupted.
- unless the application is controlled by a debugger, it may be difficult to get pinpoint control over where the failure condition occurs
- achieving any kind of significant code coverage involves a lot of testing
- access for the tester may be a problem for some production systems.
- approach is a point in time measurement; it may need to be repeated as the application evolves
Logging Decoration
If an application is using a logging framework already, it may make sense to plug into the logging framework to indicate whether the calling code is wrapped in a transaction. Each message that the application currently emits could be decorated to indicate whether a transaction is present:
INFO: [main -] creating table TEST
Nov 18, 2008 6:24:50 PM com.skillcorp.transactionlogger.jdklog.JdbcQueryBean invokeInsert
INFO: [main +] invoking insert
Nov 18, 2008 6:24:50 PM com.skillcorp.transactionlogger.jdklog.JdbcQueryBean invokeInsertINFO: [main +] inserts complete
Nov 18, 2008 6:24:50 PM com.skillcorp.transactionlogger.jdklog.JdbcQueryBean invokeInsert
INFO: [main +] throwing exception to abort transactionNov 18, 2008 6:24:50 PM com.skillcorp.transactionlogger.jdklog.JdbcQueryBean invokeQuery
INFO: [main -] invoking query
In the log output above, each message has been decorated with a '+' or '-' to indicate the presence of a transaction. The message invoking insert, for instance, indicates that the business logic was in a transaction (+) driven by a thread named 'main'. The benefits of this approach, while restricted to applications that actually log messages, include:
- non-invasive. Can be applied to an existing application that already uses logging
- comprehensive. Easily configurable to cover all transactional code in the app
- removable. Incurs no performance penalty when removed from logging configuration
- easily testable/verifiable. Results are present in the application's log output
- Login or register to post comments
- 9258 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)










Comments
Yaozong Zhu replied on Mon, 2008/12/01 - 4:49am
hoikin replied on Mon, 2008/12/01 - 9:55am
tac replied on Mon, 2008/12/01 - 10:08am
in response to: zyzjaffery
Using Spring AOP was an option that I considered as a solution, but it had a couple of negatives that swayed me towards the logging. The first problem is that Spring AOP is just as 'magical' as the declarative transaction management itself, and therefore as prone to misconfiguration. The second was that it's a little more intrusive to modify the spring configuration for an app after it's deployed; the typical WAR would have to be opened, modified and redeployed with the changes. Certainly a viable solution though, not a bad idea at all if AOP's in your toolkit and you're implementing prior to deployment.
-Tom
tac replied on Mon, 2008/12/01 - 10:18am
in response to: hoikin
Yaozong Zhu replied on Mon, 2008/12/01 - 12:33pm
in response to: tac
I can understand your point. It's like introducing a mechanism to monitor the same mechanism. It's just a quick and one-place-setting way to verify if transaction is working or not.
Our projects use JMX to adapt logging on the fry, which could be an backup solution for existing jmx-enabled system to turn on or off loggers retrieving relevant information with a plenty of irrevant one as you mentioned.
Paul Hixson replied on Mon, 2008/12/01 - 1:48pm
Nice article. Reminds me of the time I spent a couple of days figuring out the a client needed record versioning in Hibernate. Spring is so nice and quiet that it is hard to tell when something like TM is misconfigured.
I like the logging solution personally, since, once you find something like this, it's always a prime suspect in future problems. Logging quickly let's you prove the solution is still working.
Also, you're a better man than I am for plumbing the depths of Log4j **shudders**.
Rick Hightower replied on Mon, 2008/12/01 - 10:28pm
Paul Grove replied on Wed, 2008/12/03 - 3:32am
Have you considered doing an example for LogBack? Log4j is no longer under active development and it seems to me SL4J with LogBack is starting to gain traction and will be the next evolution in Java logging. No more class loader issues of JCL!
Nice example thou cheers
tac replied on Wed, 2008/12/03 - 10:43am
in response to: paulg
novoj replied on Tue, 2008/12/09 - 12:34am