Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 25, 2022 05:01 pm GMT

Spring Transaction Debugging in Production with Lightrun

Spring makes building a reliable application much easier thanks to its declarative transaction management. It also supports programmatic transaction management, but thats not as common. In this article, I want to focus on the declarative transaction management angle, since it seems much harder to debug compared to the programmatic approach.

This is partially true. We cant put a breakpoint on a transactional annotation. But Im getting ahead of myself.

What is Springs Method Declarative Transaction Management?

When writing a spring method or class, we can use annotations to declare that a method or a bean (class) is transactional. This annotation lets us tune transactional semantics using attributes. This lets us define behavior such as:

  • Transaction isolation levels lets us address issues such as dirty reads, non-repeatable reads, phantom reads, etc.
  • Transaction Manager
  • Propagation behavior we can define whether the transaction is mandatory, required, etc. This shows whether the method expects to receive a transaction and how it behaves
  • readOnly attribute the DB does not always support a read-only transaction. But when it is supported, its an excellent performance/reliability tuning feature

And much more.

Isnt the Transaction Related to the Database Driver?

The concept of transactional methods is very confusing to new spring developers. Transactions are a feature of the database driver/JDBC Connection, not of a method. Why declare it in the method?

Theres more to it. Other features, such as message queues, are also transactional. We might work with multiple databases. In those cases, if one transaction is rolled back, we need to rollback all the underlying transactions. As a result, we do the transaction management in user code and spring seamlessly propagates it into the various underlying transactional resource.

How can we Write Programmatic Transaction Management if we dont use the Database API?

Spring includes a transaction manager that exposes the APIs we typically expect to see: begin, commit and rollback. This manager includes all the logic to orchestrate the various resources.

You can inject that manager to a typical spring class, but its much easier to just write declarative transaction management like this Java code:

@Transactionalpublic void myMethod() {    // ...}

I used the annotation on the method level, but I could have placed it on the class level. The class defines the default and the method can override it.

This allows for extreme flexibility and is great for separating business code from low level JDBC transaction details.

Dynamic Proxy, Aspect Oriented Programming and Annotations

The key to debugging transactions is the way spring implements this logic. Spring uses a proxy mechanism to implement the aspect oriented programming declarative capabilities. Effectively, this means that when you invoke myMethod on MyObject or MyClass spring creates a proxy class and a proxy object instance between them.

Spring routes your invocation through the proxy types which implement all the declarative annotations. As such, a transactional proxy takes care of validating the transaction status and enforcing it.

Debugging a Spring Transaction Management using Lightrun

IMPORTANT: I assume youre familiar with Lightrun basics. If not, please read this.

Programmatic transaction management is trivial. We can just place a snapshot where it begins or is rolled back to get the status.

But if an annotation fails, the method wont be invoked and we wont get a callback.

Annotations arent magic, though. Spring uses a proxy object, as we discussed above. That proxy mechanism invokes generic code, which we can use to bind a snapshot. Once we bind a snapshot there, we can detect the proxy types in the stack. Unfortunately, debugging proxying mechanisms is problematic since theres no physical code to debug. Everything in proxying mechanisms is generated dynamically at runtime. Fortunately, this isnt a big deal. We have enough hooks for debugging without this.

Finding the Actual Transaction Class

The first thing we need to do is look for the class that implements transaction functionality. Opening the IntelliJ/IDEA class view (Command-O or CTRL-O) lets us locate a class by name. Typing in Transaction resulted in the following view:

image1.png

This might seem like a lot, but we need a concrete public class. So annotations and interfaces can be ignored. Since we only care about Spring classes, we can ignore other packages. Still, the class we are looking for was relatively low in the list, so it took me some time to find it.

In this case, the interesting class is TransactionAspectSupport. Once we open the class, we need to select the option to download the class source code.

Once this is done, we can look for an applicable public method. getTransactionManager seemed perfect, but its a bit too bare. Placing a snapshot there provided me a hint:

image2.png

I dont have much information here but the invokeWithinTransaction method up the stack is perfect!

Moving on to that method, I would like to track information specific to a transaction on the findById method:

image3.png

To limit the scope only to findById we add the condition:

method.getName().equals("findById")

Once the method is hit, we can see the details of the transaction in the stack.

If you scroll further in the method, you can see ideal locations to set snapshots in case of an exception in thread, etc. This is a great central point to debug transaction failures.

One of the nice things with snapshots is that they can easily debug concurrent transactions. Their non-blocking nature makes them the ideal tool for that.

TL;DR

Declarative configuration in Spring makes transactional operations much easier. This significantly simplifies the development of applications and separates the object logic from low level transactional behavior details.

Spring uses class-based proxies to implement annotations. Because they are generated, we cant really debug them directly, but we can debug the classes, they use internally. Specifically: TransactionAspectSupport is a great example.

An immense advantage of Lightrun is that it doesnt suspend the current thread. This means issues related to concurrency can be reproduced in Lightrun. Everything discussed here can be accomplished with the free version of Lightrun.


Original Link: https://dev.to/codenameone/spring-transaction-debugging-in-production-with-lightrun-jl4

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To