Friday, December 28, 2012

Adding Turbchargers to JEE Apps

One of the key roles, I play is evangelizing Akka within my local community. As part of the discussions, the question/doubt usually in people's mind is how can Akka provide better scalability and concurrency against a well written Java/JEE application. Since the underlying hardware/JVM remains the same, how can the actor model ooze out more power than the traditional JEE applications. In order to showcase the skeptics we decided to do small test where we take an existing JEE Web application, remodel the business logic to make use of the actor model and run tests against the same.

DayTrader Application

DayTrader is a benchmark application built around the paradigm of an online stock trading system. The application allows users to login, view their portfolio, lookup stock quotes, and buy or sell stock shares. DayTrader not only serves as an excellent application for functional testing, but it also provides a standard set of workloads for characterizing and measuring application server and component level performance.

DayTrader is built on a core set of Java EE technologies that includes Java Servlets and JavaServer Pages (JSPs) for the presentation layer and Java database connectivity (JDBC), Java Message Service (JMS), Enterprise JavaBeans (EJBs) and Message-Driven Beans (MDBs) for the back-end business logic and persistence layer.

More information about DayTrader is available here.

DayTrader seemed the right fit of an application to test our theory. We decided on the JSP->JDBC model to keep things simple and comparable. We took 2 use cases and remodelled the business logic to make use of the TypedActors.

Scenario 1 - Quote/Trade screen – Get quotes 
In the Quotes/Traders screen of DayTrader application there is a facility for selecting the details of a list of quotes by clicking the quotes button. The price quotes for the stock shares will be retrieved and displayed to the user.

In the standard flow, the get quote request is handled by a dedicated TradeAction which internally invokes the getQuote() interface of the TradeDirectJEEE object. For each request a TradeAction object is created.

In the updated flow, a set of worker actors was created which listens for requests from various modules to get the quote details. The TradeActionManager will create the Typed actor pool at the start and it will also have operations to route the incoming requests to the Typed actors which hold the TradeAction objects for invoking the getQuote function. Since Typed actors are used the same TradeActionManager can cater to other TradeAction calls with minimal changes in the existing application.

Both the orignal and the modified DayTrader application was executed with 20, 50, 75 and 100 Typed actors with as many Trade Action objects.

The graph shows the relative throughput for each tested scenario with dark red bars indicating the throughput values of the original application and the other bars indicating the throughput for application with Akka for different actor pool sizes. 

  • Akka Typed Actors has given per second throughput better than the original DayTrader application (for larger actor pool sizes) with lesser memory usage (especially for 700 and 300 users * 2 requests each). 
  • The original application required an additional 168 MB for serving 1400 requests (700 users with 2 requests each) whereas for the modified application with a Typed Actor pool size of 50 actors the additional memory used for serving the same kind of request volume was observed to be 104 MB which is an improvement of 38%.  For 75 and 100 typed actors the additional memory usage was observed to between 126MB-136MB.

The graph shows the relative throughput for each tested scenario with dark red bar indicating the throughput values of the original application and the other bars indicating the throughput for application with Akka for different actor pool sizes. 

Using Jmeter simulation of the get quotes call was done for 300 users in parallel for 100 and 200 actors for different system and Akka settings for about 45minutes for each case under the same high load conditions.

  • It was observed that increasing the number of Typed Actors from 100 to 200 has improved the throughput relatively by about 15% and 18% relative to the original application under the same condition.
  • It is also observed that an increase in the heap size to 1024 MB and changing the garbage collection method to concurrent mark sweep helped improve the throughput for high load condition. 
Scenario 2 - 4 screens - Login, home page, get Quote, buy 
A more complex use case consisting of 4 user screens was tried in which the user will use four steps to complete the use case scenario. The four steps are

  1. User logs in via the login page
  2. User presented with the home page after submitting the login credentials.
  3. Get the quotes for stock shares the symbols of which are entered by the user in the home page screen.
  4. Buy the stock shares after submitting the quantity to be bought under each symbol.

All the requests use the TradeAction object to service the request. The TradeAction object implements the TradeService interface. Therefore the same TypedActor model implemented for the Quote/Trade screen – Get quotes scenario identified in the previous case was applied in this case also without little or no changes in the TradeAction module.

Using Jmeter simulation of the use case consisting of four screens was done for 300 users with different Typed Actor pool sizes. The number of users was set to ramp up to the maximum of 300 users in 60 seconds and the test was run for 15 minutes.

It is observed that increasing the number of actors from 0 to 300 has improved the throughput by about 8%.

Any increase beyond 300 Typed actors has shown lesser improvement.

The peak memory usage for application using typed actors has seen an improvement of about 30-40% for the same throughput (100 typed actors) relative the original application’s memory usage.

Conclusion Even with a simple change, the application running on a standard laptop was able to provide better throughput (+8%) and the overall memory usage went down 38% which points to the efficiency of the actor model and Akka's handling of the memory and threads.

Details of Testing Environment
Processor - Intel Core i5-2410M CPU @ 2.30 GHz
RAM - 4 GB
OS - Windows 7 Enterprise
Application Server - Apache Geronimo v2.2.1
Compiler and Build tool - Apache Maven v2.2.1
Java version - 1.7.0_03
Akka Version - Akka 2.0.2
Database – Apache Derby

Additional optimizations we could have done
  • Akka untyped actors pools that are grouped based on the request pattern. Say one small pool catering only for requests that are less frequently used and a large pool (or multiple pools) catering to requests like Get Quote or Get Account that are more frequently used. The ratio of the pool sizes may be changed based on the request pattern to obtain better throughput.
  • Use actor’s PreStart and PostStart functions to add initialization tasks for the database like getting a connection and closing a connection or any other initialization tasks.
  • Akka untyped actors for concurrent processing of holdings, multiple quotes for the same account and session.
  • Use the Akka actor hierarchy so that there are multiple levels of actors with the higher level supervisor actor dividing a task into smaller subtasks and delegating to the child actors in the next level.
  • Optimizing the Akka dispatcher thread pool size for the actor system.

I want to say thank you to my colleague Chintu Vijay who conducted and run the tests.