Showing posts with label Performance Testing. Show all posts
Showing posts with label Performance Testing. Show all posts

Wednesday, October 31, 2012

Does Tomcat bite more than it can chew?

This is an interesting blog post for me, since its about the root cause for an issue we saw with Tomcat back in May 2011, which remained unresolved. Under high concurrency and load, Tomcat would reset (i.e. TCP level RST) client connections, without refusing to accept them - as one would expect. I posted this again to the Tomcat user list a few days back, but then wanted to find out the root cause for myself, since it would surely come up again in the future.

Background

This issue initially became evident when we ran high concurrency load tests at a customer location in Europe, where the customer had back-end services deployed on multiple Tomcat instances, and wanted to use the UltraESB for routing messages with load balancing and fail-over. For the ESB Performance Benchmark, we had been using an EchoService written over the Apache HttpComponents/Core NIO library that scaled extremely well and behaved well at the TCP level, even under load. However, at the client site, they wanted the test run against real services deployed on Tomcat - to analyse a more realistic scenario. We used a Java based clone of ApacheBench called the 'Java Bench' which is also a part of the Apache HttpComponents project, to generate load. The client would go up-to concurrency levels of 2560, pushing as many messages as possible through the ESB, to back end services deployed over Tomcat.

Under high load, the ESB would start to see errors while talking to Tomcat, and the cause would be IO errors such as "Connection reset by peer". Now the problem to the ESB is that it had already started to send out an HTTP request / payload over an accepted TCP connection, and thus it does not know if it can fail-over safely by default to another node, since the backend service might have  performed some processing over the request it may have already received. Of-course, the ESB could be configured to retry on such errors as well, but our default behaviour was to fail-over only on the safer connection refused or connect timeout errors (i.e. a connection could not be established within the allocated time) - which ensures correct operation, even for non-idempotent services.

Recent Observations

We recently experienced the same issue with Tomcat when a customer wanted to perform a load test scenario where a back-end service would block for 1-5 seconds randomly, to simulate realistic behaviour. Here, again we saw that Tomcat was resetting accepted TCP connections, and we were able to capture this with Wireshark as follows, using JavaBench directly against a Tomcat based servlet


As can be seen in the trace, the client initiated a TCP connection with the source port 9386, and Tomcat running on port 9000 accepted the connection - note “1”. The client kept sending packets of a 100K request, and Tomcat kept acknowledging them. The last such case is annotated with note “2”. Note that the request payload was not complete at this time from the client – note “3”. Suddenly, Tomcat resets the connection – note “4”

Understanding the root cause

After failing to locate any code in the Tomcat source code that resets established connections, I wanted to simulate the behaviour with a very simple Java program. Luckily the problem was easy to reproduce with a simple program as follows:

import java.net.ServerSocket;
import java.net.Socket;

public class TestAccept1 {

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8280, 0);
        Socket socket = serverSocket.accept();
        Thread.sleep(3000000); // do nothing
    }
}


We just open a server socket on port 8280, with a backlog of 0 and start listening for connections. Since the backlog is 0, one could assume that only one client connection would be allowed - BUT, I could open more than that via telnet as follows, and even send some data afterwards by typing it in and pressing the enter key.

telnet localhost 8280
hello world

A netstat command now confirms that more than one connection is opene:

netstat -na | grep 8280
tcp        0      0 127.0.0.1:34629         127.0.0.1:8280          ESTABLISHED
tcp        0      0 127.0.0.1:34630         127.0.0.1:8280          ESTABLISHED
tcp6       0      0 :::8280                 :::*                    LISTEN    
tcp6      13      0 127.0.0.1:8280          127.0.0.1:34630         ESTABLISHED
tcp6      13      0 127.0.0.1:8280          127.0.0.1:34629         ESTABLISHED

However, the Java program has only accepted ONE socket, although at the OS level, two would appear. It seems like the OS also allows more than two connections to be opened, even when the backlog is specified as 0. On Ubuntu 12.04 x64, the netstat command would not show me the actual listen queue length - but I believe it was not 0. However, before and after this test, I did not see a difference in the reported statistics for "listen queue" overflow, which I could see with the "netstat -sp tcp | fgrep listen" command

Next I used the JavaBench from the SOA ToolBox and issued a small payload at concurrency 1024, with a single iteration against the same port 8280


As expected, all requests failed, but my Wireshark trace on port 8280 did not detect any connection resets. Pushing the concurrency to 2560 and the iterations to 10 started to show tcp level RSTs - which were similar to those seen on Tomcat, though not exactly the same.

 

Can Tomcat do better?

Yes, Possibly .. What an end user would expect from Tomcat is that it refuses to accept new connections when under load, and not to accept connections and then reset them halfway through. But one would ask if that is achievable? Especially considering the behaviour seen with the simple Java example we discussed.

Well, the solution could be to perform better handling of the low level HTTP connections and the sockets, and this is already done by the free and open source high performance Enterprise Service Bus UltraESB, which utilizes the excellent Apache HttpComponents project underneath.

How does the UltraESB behave

One could easily test this by using the 'stopNewConnectionsAt' property of our NIO listener. If you set it to 2, you wont be able to even open a Telnet session to the socket beyond 2.

The first would work, the second too
But the third would see a "Connection refused"
And the UltraESB would report the following on its logs:

  INFO HttpNIOListener HTTP Listener http-8280 paused  
  WARN HttpNIOListener$EventLogger Enter maintenance mode as open connections reached : 2

Although it refuses to accept new connections, already accepted connections executes without any hindrance to completion. Thus a hardware level load balancer in front of an UltraESB cluster can safely load balance if an UltraESB node is loaded beyond its configured limits, without having to deal with any connection resets. Once a connection slot becomes free, the UltraESB will start accepting new connections as applicable.

Analysing a corresponding TCP dump

To analyse the corresponding behaviour, we wrote a simple Echo proxy service on the UltraESB, that also slept for 1 to 5 seconds before it replied, and tested this with the same JavaBench under 2560 concurrent users, each trying to push 10 messages in iteration.

Out of the 25600 requests, 7 completed successfully, while 25593 failed, as expected. We also saw many tcp level RSTs on the Wireshark dump - which must have been issued by the underlying operating system.


However, what's interesting to note is the difference - the RSTs occur immediately on receiving the SYN packet from the client - and are not established HTTP or TCP connections, but elegant "Connection Refused" errors - which would be what the client can expect. Thus the client can safely fail-over to another node without any doubt, overhead or delay.

Appendix : Supporting high concurrency in general

During testing we also saw that the Linux OS could detect the opening of many concurrent connections at the same time as a SYN flood attack, and then start using SYN cookies. You would see messages such as 
-->
Possible SYN flooding on port 9000. Sending cookies

displayed on the output of a "sudo dmesg", if this happens. Hence, for a real load, it would be better to disable SYN cookies by turning it off as follows as the root user
-->
# echo 0 > /proc/sys/net/ipv4/tcp_syncookies

To make the change persist over reboots, add the following line to your /etc/sysctl.conf
-->
net.ipv4.tcp_syncookies = 0

To allow the Linux OS to accept more connections, its also recommended that the 'net.core.somaxconn' be increased - as it usually defaults to 128 or so. This could be performed by the root user as follows,
-->
# echo 1024 > /proc/sys/net/core/somaxconn

To persist the change, append the following to the /etc/sysctl.conf
-->
net.core.somaxconn = 1024


Kudos!

The UltraESB could not have behaved gracefully without the support of the underlying Apache HttpComponents library, and the help and support received from that project community, especially by Oleg Kalnichevski - whose code and help has always fascinated me!



Tuesday, October 4, 2011

ESB performance testing - Round 5 Results for 8 Open Source ESBs

AdroitLogic Private Ltd. announced today the results of the fifth round of ESB Performance testing. This round compares the WSO2 ESB, Mule ESB CE, Fuse ESB, Apache ServiceMix, Talend ESB, Petals ESB, JBoss ESB and the UltraESB. The ESB performance benchmark has been in use since June 2007 by vendors such as WSO2, AdoritLogic, Mulesoft and BEA to compare ESBs across different loads and message sizes for six common use cases. This round also publishes an EC2 AMI to easily reproduce the results by average users, and also publishes all source configuration for the 8 different ESBs in a public BitBucket repository

What's significant about this test suite is that it has now been accepted as a de-facto ESB performance test suite by vendors such as WSO2, BEA and Mulesoft in the past, who have reported results against their implementations publicly. This has also been used extensively by end users for ESB comparison, and load testing of configurations before production deployments.

Although 8 open source ESBs were considered, 4 of these failed without being able to complete the load test. One ESB had a 3-4% error rate, while three completed successfully. The test includes two variants of the free and open source UltraESB. The enhanced version is supercharged with the power of VTD XML which allows the UltraESB to perform XPath evaluation on XML payloads without XML parsing. This works nicely with the RAM Disk based file cache used by the UltraESB to provide both Zero-Copy and Java Non-Blocking IO for the extreme levels of performance.


Full information available at http://esbperformance.org

Friday, February 12, 2010

The ESB Performance Testing Framework and Execution Round 4 - explained in detail

I've just published an article that explains the ESB Performance Test Framework in detail, and how an end user could compare an ESB of his choice to any other ESB.

http://adroitlogic.org/samples-articles-and-tutorials/16-articles/50-the-esb-performance-testing-framework-and-execution-round-4-explained-in-detail.html

PS: I was also able to resurrect this blog post of the Product Manager of the BEA AquaLogic ESB - Dain Hansen - from June 2008, which states that even BEA ran the ESB Performance Test Framework back then..

Thursday, January 21, 2010

HTTP client supporting REST, SOAP, Hessian etc for testing and Load testing!

The AdroitLogic UltraESB includes the ESB and SOA ToolBox - that can be used to test any payload over HTTP/S. Supporting all methods over HTTP such as GET, POST, PUT, DELETE, HEAD and OPTIONS, it allows a payload pasted into a text area, or read as a file to be sent to any HTTP/S endpoint, optionally authenticating the request with HTTP basic or digest authentication.


The user can select if the request should use HTTP 1.0 or 1.1, or send the entity as chunked or using 'Content-Length' encoding, if the '100-Continue' expect handshake should be used-or-not, if HTTP KeepAlive should be used or not, and if response  compression (i.e. GZip) is requested. If the request is over SSL, you could also turn off SSL Trust and Hostname verification for testing purposes - such as when using test certificates. Additionally, it can also use an identity certificate if 2-way SSL is requested.

To make this even better, the ToolBox also offers a HTTP/S Load test version of this same utility - that can be used to load test REST, SOAP, Hessian or any kind of payload over HTTP/S. This Load Test utility is a clone of the popular Apache Bench load test client - but much more advanced and stable!


The ToolBox also includes a TCP Dump utility - that can be used to view requests over the wire - in both HEX and Text formats. It additionally introduces support to capture a request into a file - which allows one to capture any kind of request message - even WS-Secured messages, Hessian binary messages etc, which can then be used with the JavaBench - to run a load test against your services.

For those who really knows what they are doing, and for example if you would like to test your ESB or SOA server for its support against malicious or malformed requests - there is a Raw socket client.


This allows one to hand-craft a HTTP request as you wish! The ToolBox also hosts a module that will start one or more Jetty servers with sample applications, which could then be used to test your ESB or SOA services. Starting multiple instances over different ports allows one to easily test load balancing and fail-over options of your ESB.

Finally the ToolBox allows you to start an instance of the UltraESB - the ultimate ESB that is simple to use, but supports ultra performance! Do read the full article "Getting started with the AdroitLogic ToolBox for the UltraESB" and follow through the end-to-end sample to get started.