Debugging the Java Message Service (JMS) API Using Lightrun

The Java Message Service API (JMS) was developed by Sun Microsystems in the days of Java EE. The JMS API provides us with simple messaging abstractions, including message producer, message consumer, and so on. Messaging APIs let us place a message on a “queue” and consume messages placed into said queue. This is immensely useful for high-throughput systems — instead of wasting user time by performing a slow operation in real time, an enterprise application can send a message. This non-blocking approach enables extremely high throughput while maintaining reliability at scale.

The message carries a transactional context which provides some guarantees on deliverability and reliability. As a result, we can post a message in a method and then just return, which provides similar guarantees to the ones we have when writing to an ACID database.

Memory Debugging and Watch Annotations

Before diving into debugging memory issues and the other amazing running process, memory debugging capabilities (which are amazing)... I want to discuss a point I left open in the last duckling post. Back there we discussed customizing the watch renderer. This is super cool!

But it's also tedious. Before we continue, if you prefer, I cover most of these subjects in these videos:

Spring Transaction Debugging in Prod With Lightrun

Spring makes building a reliable application much easier thanks to its declarative transaction management. It also supports programmatic transaction management, but that’s 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 can’t put a breakpoint on a transactional annotation. But I’m getting ahead of myself.

Debugging jsoup Java Code in Production Using Lightrun

Scraping websites built for modern browsers is far more challenging than it was a decade ago. jsoup is a convenient API that makes scraping websites trivial via DOM traversal, CSS Selectors, JQuery-Like methods, and more. But it isn’t without its caveat. Every scraping API is a ticking time bomb.

Real-world HTML is flaky. It changes without notice since it isn’t a documented API. When our Java program fails in scraping, we’re suddenly stuck with a ticking time bomb. In some cases, this is a simple issue that we can reproduce locally and deploy. But some nuanced changes in the DOM tree might be harder to observe in a local test case. In those cases, we need to understand the problem in the parse tree before pushing an update. Otherwise, we might have a broken product in production.

Debugging Collections, Streams, and Watch Renderers

In the last two ducklings, I finished the extensive discussion on breakpoints and switched my focus to the watch area. In it, we have several amazing and lesser-known tools that let us build insight into our running application. Being able to tell at a glance if something works correctly is crucial for many applications.

This is very important for collections and arrays. We can have thousands or millions of elements within a collection. Debugging this is very difficult without some basic tools. 

Debugging JAXB Production Issues

Java Architecture for XML Binding (AKA JAXB API) is a popular API for marshaling XML data. It's a framework for mapping between XML documents and Java POJOs (Plain Old Java Objects, AKA regular Java classes) almost seamlessly. The API is very easy to use and many frameworks leverage it to provide their XML support. JAXB2.0 has gained popularity both in desktop applications (Java SE) and in application server code (Spring Boot, Java EE/Jakarta EE, Microprofile, etc.).

JAXB requires a runtime library but doesn't require static analysis, XML schema, or anything like that. While the schema isn't required, it's still the basis of a cool JAXB feature: the ability to generate Java sources from source schema!

How to Debug Selenium-Based Test Scripts

Writing and maintaining the test automation code is not always a piece of cake. As a matter of fact, we frequently face many scenarios where automated test cases don’t work as expected and might lead to false positive or false negative results, in such cases, debugging is the only way out. Debugging is the primary skill set that an automation tester must adopt. It increases the morale and confidence in automation testers to provide a better code solution to fix the problem permanently.

Debugging issues in the test automation framework become more complicated when the test automation framework has a huge number of test cases. With the expansion of features in the application, the number of test cases gradually increases. In such a scenario, fixing complex issues of hybrid frameworks might require enhanced techniques of debugging. In this article, we will deep dive into such essential debugging techniques that will not only fix script issues easily but also save a good amount of debugging time.

Debugging Heavy Load on Oracle Databases

A lot of enterprises rely on the Oracle database for their data layer. Although the licenses are costly, Oracle provides a proven product in terms of performance and scalability and very good support, so many people find it to be a good trade-off. However, every product will have certain limits. If Oracle is being used to serve data by busy applications, the number of parallel database connections will often cause bottlenecks. This can lead to high CPU usage on the Oracle side. It can also starve other applications from getting connections, leading to functional issues. So it becomes critical for developers to understand not just the number of connections their applications are consuming but also how effectively they are being used.

Before we dive in, there is one prerequisite for the below analysis to work. We need to ensure that an appropriate value is set for the module attribute. One of the ways to do it is during the connection creation. The application can set the initSql attribute to call dbms_application_info.set_module('<module-name>','<action-name>') . This will help us map the database connection to a certain application while looking at the oracle database.

Exception Breakpoint that Doesn’t Suck and a Real Use Case for Method Breakpoints

Two weeks ago, I left this series in a “cliffhanger” of sorts. Well, as much as a programming blog can leave things in the air… The big one amongst them is the premise that exception breakpoints don’t have to suck. If you used them in the past, you would know that grabbing all exceptions is ridiculous. You end up at a breakpoint every second and it doesn’t help.

There’s a solution, and it’s discussed in duckling 6 number 7 also covers a lot of interesting ground for us and another cliffhanger on method breakpoints:

Debugging Java Equals and Hashcode Performance in Production

I wrote a lot about the performance metrics of the equals method and hash code in this article. There are many nuances that can lead to performance problems in those methods. The problem is that some of those things can be well hidden.

To summarize the core problem: the hashcode method is central to the java collection API. Specifically, with the performance of hash tables (specifically the Map interface hash table). The same is true with the equals method. If we have anything more complex than a string object or a primitive, the overhead can quickly grow.

Debugging Race Conditions in Production

Race conditions can occur when a multithreaded application accesses a shared resource using over one thread. Unless we have guards in place, the result might depend on which thread "got there first". This is especially problematic when the state is changed externally.

A race can cause more than just incorrect behavior. It can enable a security vulnerability when the resource in question can be corrupted in the right way. A good example of race condition vulnerabilities is mangling memory. Let's say we have an admin user name that is restricted and privileged. You can't change your user name to admin because of validation. But you can change it to anything else...

The Basics of Breakpoints You Might Not Know

In episodes 4 and 5 of “140 Second Ducklings”, I got deeper into the more advanced underpinnings of breakpoints. There’s still a lot more to learn to move forward, but even at this stage, it’s surprising how many things are relatively unknown in the developer community, and I’m just getting started.

Introducing KoolKits: OSS Debugging Toolkits for Kubernetes

KoolKits (Kubernetes toolkits) are highly-opinionated, language-specific, batteries-included debug container images for Kubernetes. In practice, they’re what you would’ve installed on your production pods if you were stuck during a tough debug session in an unfamiliar shell.

To briefly give some background, note that these container images are intended for use with the new kubectl debug feature, which spins up Ephemeral containers for interactive troubleshooting. A KoolKit will be pulled by kubectl debug, spun up as a container in your pod, and have the ability to access the same process namespace as your original container.

Developing and Testing Services Among a Sea of Microservices

Microservices are great. They allow you to create large, complex applications using a large development team, without each team member needing to understand the complexities of the entire application. Each developer must understand only the service(s) for which they are ultimately responsible.

However, with all the advantages of building microservice-based production services, very little attention is being paid to the complexities of how you test a microservice. While there are many options, one option stands out for most situations.

Debugging Tutorial: Java Return Value, IntelliJ Jump to Line, and More

Introduction

I just published the 3rd episode of the "140 Second Duckling" tutorial series and I'm getting into the rhythm of doing them. I posted the 2nd episode last week and in this post, I'll dig deeper into both. 

I tackled a lot of basic stuff about debugging but I picked two big headliners that a surprising amount of developers aren't familiar with. E.g., how many times did you step over a return statement and cursed?

Enter the Cloud Native Dojo: Blackbelt-Level Debugging

Debugging is often viewed as an art form or a craft. This is true for most engineering-related troubleshooting processes (e.g., the art of motorcycle maintenance). We’re usually indoctrinated into the basic moves by a senior developer and are then thrown into the proverbial pool. As a result, even senior engineers sometimes have gaps in their debugging skills. There are very few university courses or books on the subject, so it’s really hard to blame them.

In his book, “Why Programs Fail — A Guide to Systemic Debugging”, Andreas Zeller told a story from his youth working at a computer store. A customer walked into the store with a new Commodore 64 computer. For context: The computers back then booted directly to a basic interpreter; basic would accept line numbers as the first argument. He tried inputting this valid basic line:

Fantastic Symbols and Where to Find Them (Part 2)

In the first blog post, we learned about the fantastic symbols (debug symbols), how the symbolization process works, and lastly, how to find the symbolic names of addresses in a compiled binary.

The actual location of the symbolic information depends on the programming language implementation the program is written in. We can categorize the programming language implementations into three groups: compiled languages (with or without a runtime), interpreted languages, and JIT-compiled languages.

Debugging RAM: Detect/Fix Memory Leaks in Managed Languages – Heap Deep Dive (Part 2)

In the previous installment, I talked about the Java garbage collector. In this part, I'll discuss the most common memory issue: the memory leak. I focus on managed languages, specifically Java, but I will mention some native code tools which are interesting. A memory leak contributes to heap size, which isn't the most pressing bug in most cases. But when left alone, memory usage can become a problem and, by that point, finding the issue is hard. Unlike a crash dump, where we get a reference to a specific line, a memory leak can remain hidden.

What are the Consequences of Memory Leaks?

Unfortunately, this often means that memory leaks can carry into production and even cause problems to end users. E.g. This recent story about memory leaks hobbling Apples latest M1 computers. Virtual memory effectively means operating systems can carry memory leaks for a very long time. The performance overhead will be noticeable, though.