What's load testing?
Load testing is a way to simulate a large number of users on your application at the same time.
The importance of load testing is directly proportional to the number of users your application will need to handle simultaneously. Obviously, an application with a large user community and lots of dynamic, database driven content will likely have the highest resource requirements in order to perform well. Nonetheless, in most cases it is the way the application itself was written that has the largest impact on performance and scalability.
Symptoms of Poor Performance
The symptoms of poor performance in a web application are generally straightforward:
- general slowness and unresponsiveness
- data integrity problems (depending on how the application was written)
- pages that only partially load and look ugly
- complete lock-up, resulting in a system outage
- unusually high hardware requirements in order to achieve acceptable performance
A Few Reasons for Poor Performance
There are literally hundreds of reasons for poor performance. A few common ones are:
- bloated code base
- unnecessarily large object graphs (excessive memory footprint)
- inefficient or outdated view/rendering frameworks
- mismanagement of pooled database connections
- poor memory management
- memory heap is too large or too small (for Java apps particularly)
- garbage collection strategy is suboptimal (for Java apps particularly)
- thread contention
- improperly tuned web/application server
- improperly tuned OS/kernel
- improperly tuned database
- database schema not properly normalized
- database needs more indexes in the right places
- faulty hardware
- unnecessarily large page sizes
- bad caching strategy
- improper HTTP headers
- expensive or wasteful algorithms
There are a great many other possible issues which might also cause a slowdown. Many of these are not difficult to fix, once diagnosed; the difficulty is in finding which area(s) contain performance bottlenecks in the first place.
Benchmarks and Non-Functional Requirements
The most common motivation for running performance tests is to ensure ahead of time that the application can meet its performance-related "non-functional" requirements. In other words, there is probably a requirement that the application support a certain number of simultaneous users. Waiting until production is finding out the hard way.
The steps of a typical performance test are as follows:
- Know your users. The first step is to correctly model "real life" user behavior. If you estimate 30% of your users will be performing one function, and the other 70% will be doing something else, you should first plan out each of these behaviors.
- Once you have planned out your scripts, it is time to record them. With Loadster, you can simply open up a browser window and do the things your real users would do. Loadster runs in the background and records every page you visit, and creates a script that can be replayed as often as you'd like.
- Use Loadster to create a test scenario. A scenario contains one or more groups of "virtual users". It is essentially a recipe dictating how many users will perform each script, how often they will repeat, how long they will each be active, etc. Again, scenarios should resemble your projected "real life" user community as closely as possible.
- When you run a scenario, Loadster shows you detailed real-time graphs so you know how the load test is progressing. At this stage you may already be able to notice bottlenecks: if a particular page is slower than the rest, or if the server shows signs of being overloaded. As the test runs, Loadster continues to gather detailed metrics on the performance of your application under load.
- Once the test has completed, you can use Loadster to generate and export a test report.
Stress Testing
Technically speaking, a stress test is similar to a benchmark test except it addresses a very different purpose. While a benchmarking test is used in part to find out an application's breaking point, a stress test is used to find out what kind of bad things happen when it breaks!
Even the most lean, robust applications have a breaking point, at which performance will degrade dramatically. A stress test is used to identify if there are additional problems beyond the expected slowness. Some of these problems can include data integrity problems, race conditions, or any other bug which leaves the system or its data in an unusable state.
Stress testing is important because some of the most nasty bugs only manifest themselves under heavy load.
Stability Testing
A stability test is also an important aspect of any performance testing strategy. The purpose of stability testing is to look for problems that only manifest themselves after the application has been running for a while.
Strictly speaking, in most high-level languages, true memory leaks are a thing of the past. Automatic garbage collection and similar approaches have relieved developers of the burden of explicitly deallocating memory. All the same, many applications eventually run out of memory because they were unintentionally written to hold onto objects that are no longer used. A common pitfall is in-memory caching strategies and collections that grow without bounds.
By running a load test around 50% of peak load for several hours or even days, you can often identify problems like that ahead of time.