JBoss Optimizations 101by Sacha Labourey and Juha Lindfors
This article introduces a fictitious application to show some basic configuration mistakes, frequently made by new J2EE developers, that lead to bad application performance. While the application is fictitious, all surrounding issues and optimizations we cover originate from real-life consulting experiences in the J2EE and JBoss fields.
Presentation of the CMS101
CMS101 is a basic content management server (CMS) implemented as a J2EE application. Its purpose is to serve HTML pages that are dynamically built, based on a set of basic elements:
- Page content
- Left side
- Right side
Usually, different contents will share the same header, footer, and left and right side.
Figure 1. Structure of a web page for CMS101
Obviously, the flexibility offered by this CMS is not overwhelming and will only satisfy limited scenarios, so don't try this at work. However, its pedagogic interest remains intact.
At the beginning of the project, after several passionate meetings, CMS101's architecture team ended up with the EJB design excerpted here:
Figure 2. UML design of CMS101
A web page is identified by a name and has links to a header, a footer, and left and right sides. The page content is versioned: the current production version number is stored in the WebPage EJB and different versions are stored in the PageContent EJB. All of these EJBs are Container Managed Persistence (CMP) entity beans. An EJB Page Renderer stateless session bean handles web page creation. It starts the transaction and sequentially asks each component composing the web page for its HTML output:
Figure 3. Page-rendering activity chart
Note: for the sake of simplicity, only access to the Header EJB is shown in the picture above. However, access to the footer and left and right sides is similar.
A few days later, the development team comes up with a first version of CMS101 and deploys it under JBoss 3.2. After a successful deployment, the development teams define a set of web pages for testing and start playing with CMS101. The result is satisfying. However, as real professionals, they decide to put a scalability test suite into place!
First Headaches: Heavy Locking
While simple testing produced satisfactory results, the development team quickly detects that their application doesn't scale when they run the scalability test suite. For example, some of their test web pages contain code that must get salary information from a remote ERP system. This operation, while not CPU-intensive for the application server, takes a few seconds (network latency, ERP processing time, etc.). They observe that, while such a page is being processed, no other page can be rendered by CMS101! What is the reason for that problem?
By default, JBoss uses pessimistic locking. An entity bean that is enrolled in a transaction cannot be used by another transaction until the one that uses the bean has committed or rolled back. Consequently, as the transaction is started by the PageRenderer EJB, all entity beans used by PageRenderer are only "released" once the whole work is finished.
As all Web pages of the CMS101 test suite use the same header and footer, there is a huge contention taking place, namely:
- The display of a given web page is serialized: as the WebPage bean for a given page is exclusively locked by an HTTP request, no other display of this page is possible until the first request is finished.
- Even the display of two different pages may be serialized if they use either the same header, footer, or left or right side (as is the case in the test suite).
Figure 4. Activity chart showing pessimistic locking
When you face entity bean access contention problems that lead to bad performance of your J2EE application, there is no (magic) general solution that can be used; each scenario must be individually analyzed.
In the current scenario, it is obvious that there is no real need for the entity beans to be enrolled in the transaction. Each bean is only accessed once during the transaction. None of its fields is updated. JBoss offers a way to handle this situation by defining either an entire EJB as being "read-only" or simply as a subset of its methods. When accessing a read-only method (or EJB), while JBoss still prevents concurrent access to the same bean instance, the bean will not be enrolled in the transaction and will not be locked during the whole transaction lifetime. Consequently, other transactions can directly use it for their own work.
Our brilliant development team decides that all calls to methods prefixed by
get were read-only, so they modify the jboss.xml
deployment descriptor accordingly:
<jboss> <enterprise-beans> <entity> <ejb-name>WebPage</ejb-name> <method-attributes> <method> <method-name>get*</method-name> <read-only>true</read-only> </method> <method-attributes> </entity> <entity> <ejb-name>PageContent</ejb-name> <method-attributes> <method> <method-name>get*</method-name> <read-only>true</read-only> </method> <method-attributes> </entity> <entity> <ejb-name>Header</ejb-name> <method-attributes> <method> <method-name>get*</method-name> <read-only>true</read-only> </method> <method-attributes> </entity> <!-- and so on for Footer, LeftSide and RightSide --> <session> <ejb-name>PageRenderer</ejb-name> <jndi-name>PageRenderer</jndi-name> </session> </enterprise-beans> </jboss>
They then decide to re-run the test suite to admire the new results. However, they still have mixed feelings. While the concurrency and overall performance of their application is much better, their database suffers from heavy access and quickly becomes the bottleneck of their solution.
Pages: 1, 2